diff options
Diffstat (limited to 'src/gui')
525 files changed, 35366 insertions, 9803 deletions
diff --git a/src/gui/accessible/accessible.pri b/src/gui/accessible/accessible.pri index ad2fb4c..31362ff 100644 --- a/src/gui/accessible/accessible.pri +++ b/src/gui/accessible/accessible.pri @@ -12,7 +12,7 @@ contains(QT_CONFIG, accessibility) { accessible/qaccessiblewidget.cpp \ accessible/qaccessibleplugin.cpp - mac:!embedded { + mac:!embedded:!qpa { HEADERS += accessible/qaccessible_mac_p.h OBJECTIVE_SOURCES += accessible/qaccessible_mac.mm \ accessible/qaccessible_mac_cocoa.mm diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp index aed7b00..10e5785 100644 --- a/src/gui/accessible/qaccessible.cpp +++ b/src/gui/accessible/qaccessible.cpp @@ -212,42 +212,101 @@ QT_BEGIN_NAMESPACE This enum type defines accessible event types. - \value AcceleratorChanged - \value Alert A system alert (e.g., a message from a QMessageBox) - \value ContextHelpEnd Context help (QWhatsThis) for an object is finished. - \value ContextHelpStart Context help (QWhatsThis) for an object is initiated. - \value DefaultActionChanged The default QAccessible::Action for the accessible object changed - \value DescriptionChanged The objects QAccessible::Description changed. - \value DialogEnd A dialog (QDialog) is been hidden - \value DialogStart A dialog (QDialog) has been set visible. - \value DragDropEnd A Drag & Drop operation is about to finished. - \value DragDropStart A Drag & Drop operation is about to be initiated. - \value Focus An object has gained keyboard focus. - \value ForegroundChanged A window has been activated (i.e., a new window has gained focus on the desktop) - \value HelpChanged The QAccessible::Help text property of an object has changed - \value LocationChanged An objects location on the screen changed - \value MenuCommand A menu item is triggered. - \value MenuEnd A menu has been closed (Qt uses PopupMenuEnd for all menus) - \value MenuStart A menu has been opened on the menubar (Qt uses PopupMenuStart for all menus) - \value NameChanged The QAccessible::Name property of an object has changed - \value ObjectCreated A new object is created. - \value ObjectDestroyed An object is deleted. - \value ObjectHide An object is hidden (i.e., with QWidget::hide()). Any children the object that is hidden has do not send this event. - It is not send when an object is hidden as it is being obcured by others. - \value ObjectReorder A layout or item view has added, removed, or moved an object (Qt does not use this event). - \value ObjectShow An object is displayed (i.e., with QWidget::show()). - \value ParentChanged An objects parent object changed. - \value PopupMenuEnd A popup menu has closed. - \value PopupMenuStart A popupmenu has opened. - \value ScrollingEnd A scrollbar scroll operation has ended (the mouse has released the slider handle) - \value ScrollingStart A scrollbar scroll operation is about to start (i.e., the mouse has pressed on the slider handle) - \value Selection The selection has changed in a menu or item view. - \value SelectionAdd An item has been added to the selection in an item view. - \value SelectionRemove An item has been removed from an item view selection. - \value SelectionWithin Several changes to a selection has occurred in an item view. - \value SoundPlayed A sound has been played by an object - \value StateChanged The QAccessible::State of an object has changed. - \value ValueChanged The QAccessible::Value of an object has changed. + \value AcceleratorChanged The keyboard accelerator for an action has been changed. + \value ActionChanged An action has been changed. + \value ActiveDescendantChanged + \value Alert A system alert (e.g., a message from a QMessageBox) + \value AttributeChanged + \value ContextHelpEnd Context help (QWhatsThis) for an object is finished. + \value ContextHelpStart Context help (QWhatsThis) for an object is initiated. + \value DefaultActionChanged The default QAccessible::Action for the accessible + object has changed. + \value DescriptionChanged The object's QAccessible::Description changed. + \value DialogEnd A dialog (QDialog) has been hidden + \value DialogStart A dialog (QDialog) has been set visible. + \value DocumentContentChanged The contents of a text document have changed. + \value DocumentLoadComplete A document has been loaded. + \value DocumentLoadStopped A document load has been stopped. + \value DocumentReload A document reload has been initiated. + \value DragDropEnd A drag and drop operation is about to finished. + \value DragDropStart A drag and drop operation is about to be initiated. + \value Focus An object has gained keyboard focus. + \value ForegroundChanged A window has been activated (i.e., a new window has + gained focus on the desktop). + \value HelpChanged The QAccessible::Help text property of an object has + changed. + \value HyperlinkEndIndexChanged The end position of the display text for a hypertext + link has changed. + \value HyperlinkNumberOfAnchorsChanged The number of anchors in a hypertext link has changed, + perhaps because the display text has been split to + provide more than one link. + \value HyperlinkSelectedLinkChanged The link for the selected hypertext link has changed. + \value HyperlinkStartIndexChanged The start position of the display text for a hypertext + link has changed. + \value HypertextChanged The display text for a hypertext link has changed. + \value HypertextLinkActivated A hypertext link has been activated, perhaps by being + clicked or via a key press. + \value HypertextLinkSelected A hypertext link has been selected. + \value HypertextNLinksChanged + \value LocationChanged An object's location on the screen has changed. + \value MenuCommand A menu item is triggered. + \value MenuEnd A menu has been closed (Qt uses PopupMenuEnd for all + menus). + \value MenuStart A menu has been opened on the menubar (Qt uses + PopupMenuStart for all menus). + \value NameChanged The QAccessible::Name property of an object has changed. + \value ObjectAttributeChanged + \value ObjectCreated A new object is created. + \value ObjectDestroyed An object is deleted. + \value ObjectHide An object is hidden; for example, with QWidget::hide(). + Any children the object that is hidden has do not send + this event. It is not sent when an object is hidden as + it is being obcured by others. + \value ObjectReorder A layout or item view has added, removed, or moved an + object (Qt does not use this event). + \value ObjectShow An object is displayed; for example, with + QWidget::show(). + \value PageChanged + \value ParentChanged An object's parent object changed. + \value PopupMenuEnd A pop-up menu has closed. + \value PopupMenuStart A pop-up menu has opened. + \value ScrollingEnd A scrollbar scroll operation has ended (the mouse has + released the slider handle). + \value ScrollingStart A scrollbar scroll operation is about to start; this may + be caused by a mouse press on the slider handle, for + example. + \value SectionChanged + \value SelectionAdd An item has been added to the selection in an item view. + \value SelectionRemove An item has been removed from an item view selection. + \value Selection The selection has changed in a menu or item view. + \value SelectionWithin Several changes to a selection has occurred in an item + view. + \value SoundPlayed A sound has been played by an object + \value StateChanged The QAccessible::State of an object has changed. + \value TableCaptionChanged A table caption has been changed. + \value TableColumnDescriptionChanged The description of a table column, typically found in + the column's header, has been changed. + \value TableColumnHeaderChanged A table column header has been changed. + \value TableModelChanged The model providing data for a table has been changed. + \value TableRowDescriptionChanged The description of a table row, typically found in the + row's header, has been changed. + \value TableRowHeaderChanged A table row header has been changed. + \value TableSummaryChanged The summary of a table has been changed. + \value TextAttributeChanged + \value TextCaretMoved The caret has moved in an editable widget. + The caret represents the cursor position in an editable + widget with the input focus. + \value TextColumnChanged A text column has been changed. + \value TextInserted Text has been inserted into an editable widget. + \value TextRemoved Text has been removed from an editable widget. + \value TextSelectionChanged The selected text has changed in an editable widget. + \value TextUpdated The text has been update in an editable widget. + \value ValueChanged The QAccessible::Value of an object has changed. + \value VisibleDataChanged + + The values for this enum are defined to be the same as those defined in the + \l{AccessibleEventID.idl File Reference}{IAccessible2} and + \l{Microsoft Active Accessibility Event Constants}{MSAA} specifications. */ /*! @@ -409,13 +468,18 @@ static void qAccessibleCleanup() /*! \typedef QAccessible::InterfaceFactory - A function pointer type. Use a function with this prototype to install - interface factories with installFactory(). + This is a typedef for a pointer to a function with the following + signature: - The function receives a QObject pointer. If the QObject - provides a QAccessibleInterface, it sets the second parameter to - point to the corresponding QAccessibleInterface, and returns true; - otherwise returns false. + \snippet doc/src/snippets/code/src_gui_accessible_qaccessible.cpp 1 + + The function receives a QString and a QObject pointer, where the + QString is the key identifying the interface. The QObject is used + to pass on to the QAccessibleInterface so that it can hold a reference + to it. + + If the key and the QObject does not have a corresponding + QAccessibleInterface, a null-pointer will be returned. Installed factories are called by queryAccessibilityInterface() until one provides an interface. @@ -984,6 +1048,11 @@ const QAccessibleInterface *other, int otherChild) const */ /*! + \fn QAccessibleTable2Interface *QAccessibleInterface::table2Interface() + \internal +*/ + +/*! \fn QAccessibleActionInterface *QAccessibleInterface::actionInterface() \internal */ diff --git a/src/gui/accessible/qaccessible.h b/src/gui/accessible/qaccessible.h index bc8a851..24a6744 100644 --- a/src/gui/accessible/qaccessible.h +++ b/src/gui/accessible/qaccessible.h @@ -83,6 +83,42 @@ public: MenuCommand = 0x0018, + // Values from IAccessible2 + ActionChanged = 0x0101, + ActiveDescendantChanged = 0x0102, + AttributeChanged = 0x0103, + DocumentContentChanged = 0x0104, + DocumentLoadComplete = 0x0105, + DocumentLoadStopped = 0x0106, + DocumentReload = 0x0107, + HyperlinkEndIndexChanged = 0x0108, + HyperlinkNumberOfAnchorsChanged = 0x0109, + HyperlinkSelectedLinkChanged = 0x010A, + HypertextLinkActivated = 0x010B, + HypertextLinkSelected = 0x010C, + HyperlinkStartIndexChanged = 0x010D, + HypertextChanged = 0x010E, + HypertextNLinksChanged = 0x010F, + ObjectAttributeChanged = 0x0110, + PageChanged = 0x0111, + SectionChanged = 0x0112, + TableCaptionChanged = 0x0113, + TableColumnDescriptionChanged = 0x0114, + TableColumnHeaderChanged = 0x0115, + TableModelChanged = 0x0116, + TableRowDescriptionChanged = 0x0117, + TableRowHeaderChanged = 0x0118, + TableSummaryChanged = 0x0119, + TextAttributeChanged = 0x011A, + TextCaretMoved = 0x011B, + // TextChanged = 0x011C, is deprecated in IA2, use TextUpdated + TextColumnChanged = 0x011D, + TextInserted = 0x011E, + TextRemoved = 0x011F, + TextUpdated = 0x0120, + TextSelectionChanged = 0x0121, + VisibleDataChanged = 0x0122, + ObjectCreated = 0x8000, ObjectDestroyed = 0x8001, ObjectShow = 0x8002, @@ -115,6 +151,7 @@ public: ReadOnly = 0x00000040, HotTracked = 0x00000080, DefaultButton = 0x00000100, + // #### Qt5 Expandable Expanded = 0x00000200, Collapsed = 0x00000400, Busy = 0x00000800, @@ -142,6 +179,8 @@ public: HasPopup = 0x40000000, Modal = 0x80000000, + // #### Qt5 ManagesDescendants + // #### Qt5 remove HasInvokeExtension HasInvokeExtension = 0x10000000 // internal }; Q_DECLARE_FLAGS(State, StateFlag) @@ -312,7 +351,8 @@ namespace QAccessible2 ValueInterface, TableInterface, ActionInterface, - ImageInterface + ImageInterface, + Table2Interface }; } @@ -323,6 +363,7 @@ class QAccessibleValueInterface; class QAccessibleTableInterface; class QAccessibleActionInterface; class QAccessibleImageInterface; +class QAccessibleTable2Interface; class Q_GUI_EXPORT QAccessibleInterface : public QAccessible { @@ -386,6 +427,9 @@ public: inline QAccessibleImageInterface *imageInterface() { return reinterpret_cast<QAccessibleImageInterface *>(cast_helper(QAccessible2::ImageInterface)); } + inline QAccessibleTable2Interface *table2Interface() + { return reinterpret_cast<QAccessibleTable2Interface *>(cast_helper(QAccessible2::Table2Interface)); } + private: QAccessible2Interface *cast_helper(QAccessible2::InterfaceType); }; diff --git a/src/gui/accessible/qaccessible2.cpp b/src/gui/accessible/qaccessible2.cpp index 35b24f6..078ff13 100644 --- a/src/gui/accessible/qaccessible2.cpp +++ b/src/gui/accessible/qaccessible2.cpp @@ -42,6 +42,7 @@ #include "qaccessible2.h" #include "qapplication.h" #include "qclipboard.h" +#include "qtextboundaryfinder.h" #ifndef QT_NO_ACCESSIBILITY @@ -132,6 +133,117 @@ QT_BEGIN_NAMESPACE \link http://www.linux-foundation.org/en/Accessibility/IAccessible2 IAccessible2 Specification \endlink */ + +/*! + \internal +*/ +QString Q_GUI_EXPORT qTextBeforeOffsetFromString(int offset, QAccessible2::BoundaryType boundaryType, + int *startOffset, int *endOffset, const QString& text) +{ + QTextBoundaryFinder::BoundaryType type; + switch (boundaryType) { + case QAccessible2::CharBoundary: + type = QTextBoundaryFinder::Grapheme; + break; + case QAccessible2::WordBoundary: + type = QTextBoundaryFinder::Word; + break; + case QAccessible2::SentenceBoundary: + type = QTextBoundaryFinder::Sentence; + break; + default: + // in any other case return the whole line + *startOffset = 0; + *endOffset = text.length(); + return text; + } + + QTextBoundaryFinder boundary(type, text); + boundary.setPosition(offset); + + if (!boundary.isAtBoundary()) { + boundary.toPreviousBoundary(); + } + boundary.toPreviousBoundary(); + *startOffset = boundary.position(); + boundary.toNextBoundary(); + *endOffset = boundary.position(); + + return text.mid(*startOffset, *endOffset - *startOffset); +} + +/*! + \internal +*/ +QString Q_GUI_EXPORT qTextAfterOffsetFromString(int offset, QAccessible2::BoundaryType boundaryType, + int *startOffset, int *endOffset, const QString& text) +{ + QTextBoundaryFinder::BoundaryType type; + switch (boundaryType) { + case QAccessible2::CharBoundary: + type = QTextBoundaryFinder::Grapheme; + break; + case QAccessible2::WordBoundary: + type = QTextBoundaryFinder::Word; + break; + case QAccessible2::SentenceBoundary: + type = QTextBoundaryFinder::Sentence; + break; + default: + // in any other case return the whole line + *startOffset = 0; + *endOffset = text.length(); + return text; + } + + QTextBoundaryFinder boundary(type, text); + boundary.setPosition(offset); + + boundary.toNextBoundary(); + *startOffset = boundary.position(); + boundary.toNextBoundary(); + *endOffset = boundary.position(); + + return text.mid(*startOffset, *endOffset - *startOffset); +} + +/*! + \internal +*/ +QString Q_GUI_EXPORT qTextAtOffsetFromString(int offset, QAccessible2::BoundaryType boundaryType, + int *startOffset, int *endOffset, const QString& text) +{ + QTextBoundaryFinder::BoundaryType type; + switch (boundaryType) { + case QAccessible2::CharBoundary: + type = QTextBoundaryFinder::Grapheme; + break; + case QAccessible2::WordBoundary: + type = QTextBoundaryFinder::Word; + break; + case QAccessible2::SentenceBoundary: + type = QTextBoundaryFinder::Sentence; + break; + default: + // in any other case return the whole line + *startOffset = 0; + *endOffset = text.length(); + return text; + } + + QTextBoundaryFinder boundary(type, text); + boundary.setPosition(offset); + + if (!boundary.isAtBoundary()) { + boundary.toPreviousBoundary(); + } + *startOffset = boundary.position(); + boundary.toNextBoundary(); + *endOffset = boundary.position(); + + return text.mid(*startOffset, *endOffset - *startOffset); +} + QAccessibleSimpleEditableTextInterface::QAccessibleSimpleEditableTextInterface( QAccessibleInterface *accessibleInterface) : iface(accessibleInterface) diff --git a/src/gui/accessible/qaccessible2.h b/src/gui/accessible/qaccessible2.h index 5cb0323..106b69e 100644 --- a/src/gui/accessible/qaccessible2.h +++ b/src/gui/accessible/qaccessible2.h @@ -52,6 +52,8 @@ QT_MODULE(Gui) #ifndef QT_NO_ACCESSIBILITY +class QModelIndex; + namespace QAccessible2 { enum CoordinateType @@ -68,6 +70,24 @@ namespace QAccessible2 LineBoundary, NoBoundary }; + + enum TableModelChangeType { + TableModelChangeInsert, + TableModelChangeDelete, + TableModelChangeUpdate + }; + + struct TableModelChange { + int firstColumn; + int firstRow; + int lastColumn; + int lastRow; + TableModelChangeType type; + + TableModelChange() + : firstColumn(0), firstRow(0), lastColumn(0), lastRow(0), type(TableModelChangeUpdate) + {} + }; } class Q_GUI_EXPORT QAccessible2Interface @@ -83,6 +103,7 @@ inline QAccessible2Interface *qAccessibleEditableTextCastHelper() { return 0; } inline QAccessible2Interface *qAccessibleTableCastHelper() { return 0; } inline QAccessible2Interface *qAccessibleActionCastHelper() { return 0; } inline QAccessible2Interface *qAccessibleImageCastHelper() { return 0; } +inline QAccessible2Interface *qAccessibleTable2CastHelper() { return 0; } #define Q_ACCESSIBLE_OBJECT \ public: \ @@ -101,6 +122,8 @@ inline QAccessible2Interface *qAccessibleImageCastHelper() { return 0; } return qAccessibleActionCastHelper(); \ case QAccessible2::ImageInterface: \ return qAccessibleImageCastHelper(); \ + case QAccessible2::Table2Interface: \ + return qAccessibleTable2CastHelper(); \ } \ return 0; \ } \ @@ -214,6 +237,95 @@ public: int *columnSpan, bool *isSelected) = 0; }; +class Q_GUI_EXPORT QAccessibleTable2CellInterface: public QAccessibleInterface +{ +public: + // Returns the number of columns occupied by this cell accessible. + virtual int columnExtent() const = 0; + + // Returns the column headers as an array of cell accessibles. + virtual QList<QAccessibleInterface*> columnHeaderCells() const = 0; + + // Translates this cell accessible into the corresponding column index. + virtual int columnIndex() const = 0; + // Returns the number of rows occupied by this cell accessible. + virtual int rowExtent() const = 0; + // Returns the row headers as an array of cell accessibles. + virtual QList<QAccessibleInterface*> rowHeaderCells() const = 0; + // Translates this cell accessible into the corresponding row index. + virtual int rowIndex() const = 0; + // Returns a boolean value indicating whether this cell is selected. + virtual bool isSelected() const = 0; + + // Gets the row and column indexes and extents of this cell accessible and whether or not it is selected. + virtual void rowColumnExtents(int *row, int *column, int *rowExtents, int *columnExtents, bool *selected) const = 0; + // Returns a reference to the accessbile of the containing table. + virtual QAccessibleTable2Interface* table() const = 0; + + // #### Qt5 this should not be here but part of the state + virtual bool isExpandable() const = 0; +}; + +class Q_GUI_EXPORT QAccessibleTable2Interface: public QAccessible2Interface +{ +public: + inline QAccessible2Interface *qAccessibleTable2CastHelper() { return this; } + + // Returns the cell at the specified row and column in the table. + virtual QAccessibleTable2CellInterface *cellAt (int row, int column) const = 0; + // Returns the caption for the table. + virtual QAccessibleInterface *caption() const = 0; + // Returns the description text of the specified column in the table. + virtual QString columnDescription(int column) const = 0; + // Returns the total number of columns in table. + virtual int columnCount() const = 0; + // Returns the total number of rows in table. + virtual int rowCount() const = 0; + // Returns the total number of selected cells. + virtual int selectedCellCount() const = 0; + // Returns the total number of selected columns. + virtual int selectedColumnCount() const = 0; + // Returns the total number of selected rows. + virtual int selectedRowCount() const = 0; + // Returns the description text of the specified row in the table. + virtual QString rowDescription(int row) const = 0; + // Returns a list of accessibles currently selected. + virtual QList<QAccessibleTable2CellInterface*> selectedCells() const = 0; + // Returns a list of column indexes currently selected (0 based). + virtual QList<int> selectedColumns() const = 0; + // Returns a list of row indexes currently selected (0 based). + virtual QList<int> selectedRows() const = 0; + // Returns the summary description of the table. + virtual QAccessibleInterface *summary() const = 0; + // Returns a boolean value indicating whether the specified column is completely selected. + virtual bool isColumnSelected(int column) const = 0; + // Returns a boolean value indicating whether the specified row is completely selected. + virtual bool isRowSelected(int row) const = 0; + // Selects a row and unselects all previously selected rows. + virtual bool selectRow(int row) = 0; + // Selects a column and unselects all previously selected columns. + virtual bool selectColumn(int column) = 0; + // Unselects one row, leaving other selected rows selected (if any). + virtual bool unselectRow(int row) = 0; + // Unselects one column, leaving other selected columns selected (if any). + virtual bool unselectColumn(int column) = 0; + // Returns the type and extents describing how a table changed. + virtual QAccessible2::TableModelChange modelChange() const = 0; + +protected: + // These functions are called when the model changes. + virtual void modelReset() = 0; + virtual void rowsInserted(const QModelIndex &parent, int first, int last) = 0; + virtual void rowsRemoved(const QModelIndex &parent, int first, int last) = 0; + virtual void columnsInserted(const QModelIndex &parent, int first, int last) = 0; + virtual void columnsRemoved(const QModelIndex &parent, int first, int last) = 0; + virtual void rowsMoved( const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row) = 0; + virtual void columnsMoved( const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column) = 0; + +friend class QAbstractItemView; +friend class QAbstractItemViewPrivate; +}; + class Q_GUI_EXPORT QAccessibleActionInterface : public QAccessible2Interface { public: diff --git a/src/gui/accessible/qaccessible_mac.mm b/src/gui/accessible/qaccessible_mac.mm index c405eee..a250730 100644 --- a/src/gui/accessible/qaccessible_mac.mm +++ b/src/gui/accessible/qaccessible_mac.mm @@ -774,7 +774,7 @@ QAInterface QAccessibleHierarchyManager::lookup(const AXUIElementRef &element) return factory->interface(id); #else return QAInterface(); -#endif; +#endif } QAInterface QAccessibleHierarchyManager::lookup(const QAElement &element) @@ -2427,7 +2427,7 @@ void QAccessible::updateAccessibility(QObject *object, int child, Event reason) } // There is no equivalent Mac notification for ObjectShow/Hide, so we call HIObjectSetAccessibilityIgnored - // and isItIntersting which will mark the HIObject accociated with the element as ignored if the + // and isItInteresting which will mark the HIObject accociated with the element as ignored if the // QAccessible::Invisible state bit is set. QAInterface interface = accessibleHierarchyManager()->lookup(element); if (interface.isValid()) { diff --git a/src/gui/accessible/qaccessible_unix.cpp b/src/gui/accessible/qaccessible_unix.cpp index a6b7ec3..1c1eb2a 100644 --- a/src/gui/accessible/qaccessible_unix.cpp +++ b/src/gui/accessible/qaccessible_unix.cpp @@ -96,13 +96,24 @@ void QAccessible::updateAccessibility(QObject *o, int who, Event reason) } initialize(); - if (bridges()->isEmpty()) + if (!bridges() || bridges()->isEmpty()) return; QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(o); if (!iface) return; + // updates for List/Table/Tree should send child + if (who) { + QAccessibleInterface *child; + iface->navigate(QAccessible::Child, who, &child); + if (child) { + delete iface; + iface = child; + who = 0; + } + } + for (int i = 0; i < bridges()->count(); ++i) bridges()->at(i)->notifyAccessibilityUpdate(reason, iface, who); delete iface; diff --git a/src/gui/accessible/qaccessible_win.cpp b/src/gui/accessible/qaccessible_win.cpp index 3c42864..caabae5 100644 --- a/src/gui/accessible/qaccessible_win.cpp +++ b/src/gui/accessible/qaccessible_win.cpp @@ -47,6 +47,11 @@ #include "qt_windows.h" #include "qwidget.h" #include "qsettings.h" +#include <QtCore/qmap.h> +#include <QtCore/qpair.h> +#include <QtGui/qgraphicsitem.h> +#include <QtGui/qgraphicsscene.h> +#include <QtGui/qgraphicsview.h> #include <winuser.h> #if !defined(WINABLEAPI) @@ -148,6 +153,106 @@ static const char *roleString(QAccessible::Role role) return roles[int(role)]; } +static const char *eventString(QAccessible::Event ev) +{ + static const char *events[] = { + "null", // 0 + "SoundPlayed" /*= 0x0001*/, + "Alert" /*= 0x0002*/, + "ForegroundChanged" /*= 0x0003*/, + "MenuStart" /*= 0x0004*/, + "MenuEnd" /*= 0x0005*/, + "PopupMenuStart" /*= 0x0006*/, + "PopupMenuEnd" /*= 0x0007*/, + "ContextHelpStart" /*= 0x000C*/, // 8 + "ContextHelpEnd" /*= 0x000D*/, + "DragDropStart" /*= 0x000E*/, + "DragDropEnd" /*= 0x000F*/, + "DialogStart" /*= 0x0010*/, + "DialogEnd" /*= 0x0011*/, + "ScrollingStart" /*= 0x0012*/, + "ScrollingEnd" /*= 0x0013*/, + "MenuCommand" /*= 0x0018*/, // 16 + + // Values from IAccessible2 + "ActionChanged" /*= 0x0101*/, // 17 + "ActiveDescendantChanged", + "AttributeChanged", + "DocumentContentChanged", + "DocumentLoadComplete", + "DocumentLoadStopped", + "DocumentReload", + "HyperlinkEndIndexChanged", + "HyperlinkNumberOfAnchorsChanged", + "HyperlinkSelectedLinkChanged", + "HypertextLinkActivated", + "HypertextLinkSelected", + "HyperlinkStartIndexChanged", + "HypertextChanged", + "HypertextNLinksChanged", + "ObjectAttributeChanged", + "PageChanged", + "SectionChanged", + "TableCaptionChanged", + "TableColumnDescriptionChanged", + "TableColumnHeaderChanged", + "TableModelChanged", + "TableRowDescriptionChanged", + "TableRowHeaderChanged", + "TableSummaryChanged", + "TextAttributeChanged", + "TextCaretMoved", + // TextChanged, deprecated, use TextUpdated + //TextColumnChanged = TextCaretMoved + 2, + "TextInserted", + "TextRemoved", + "TextUpdated", + "TextSelectionChanged", + "VisibleDataChanged", /*= 0x0101+32*/ + "ObjectCreated" /*= 0x8000*/, // 49 + "ObjectDestroyed" /*= 0x8001*/, + "ObjectShow" /*= 0x8002*/, + "ObjectHide" /*= 0x8003*/, + "ObjectReorder" /*= 0x8004*/, + "Focus" /*= 0x8005*/, + "Selection" /*= 0x8006*/, + "SelectionAdd" /*= 0x8007*/, + "SelectionRemove" /*= 0x8008*/, + "SelectionWithin" /*= 0x8009*/, + "StateChanged" /*= 0x800A*/, + "LocationChanged" /*= 0x800B*/, + "NameChanged" /*= 0x800C*/, + "DescriptionChanged" /*= 0x800D*/, + "ValueChanged" /*= 0x800E*/, + "ParentChanged" /*= 0x800F*/, + "HelpChanged" /*= 0x80A0*/, + "DefaultActionChanged" /*= 0x80B0*/, + "AcceleratorChanged" /*= 0x80C0*/ + }; + int e = int(ev); + if (e <= 0x80c0) { + const int last = sizeof(events)/sizeof(char*) - 1; + + if (e <= 0x07) + return events[e]; + else if (e <= 0x13) + return events[e - 0x0c + 8]; + else if (e == 0x18) + return events[16]; + else if (e <= 0x0101 + 32) + return events[e - 0x101 + 17]; + else if (e <= 0x800f) + return events[e - 0x8000 + 49]; + else if (e == 0x80a0) + return events[last - 2]; + else if (e == 0x80b0) + return events[last - 1]; + else if (e == 0x80c0) + return events[last]; + } + return "unknown"; +}; + void showDebug(const char* funcName, const QAccessibleInterface *iface) { qDebug() << "Role:" << roleString(iface->role(0)) @@ -159,6 +264,12 @@ void showDebug(const char* funcName, const QAccessibleInterface *iface) # define showDebug(f, iface) #endif +// This stuff is used for widgets/items with no window handle: +typedef QMap<int, QPair<QObject*,int> > NotifyMap; +Q_GLOBAL_STATIC(NotifyMap, qAccessibleRecentSentEvents) +static int eventNum = 0; + + void QAccessible::initialize() { @@ -251,18 +362,35 @@ void QAccessible::updateAccessibility(QObject *o, int who, Event reason) // An event has to be associated with a window, // so find the first parent that is a widget. QWidget *w = 0; - if (o->isWidgetType()) { - w = (QWidget*)o; - } else { - QObject *p = o; - while ((p = p->parent()) != 0) { - if (p->isWidgetType()) { - w = (QWidget*)p; + QObject *p = o; + do { + if (p->isWidgetType()) { + w = static_cast<QWidget*>(p); + if (w->internalWinId()) break; + } + if (QGraphicsObject *gfxObj = qobject_cast<QGraphicsObject*>(p)) { + QGraphicsItem *parentItem = gfxObj->parentItem(); + if (parentItem) { + p = parentItem->toGraphicsObject(); + } else { + QGraphicsView *view = 0; + if (QGraphicsScene *scene = gfxObj->scene()) { + QWidget *fw = QApplication::focusWidget(); + const QList<QGraphicsView*> views = scene->views(); + for (int i = 0 ; i < views.count() && view != fw; ++i) { + view = views.at(i); + } + } + p = view; } + } else { + p = p->parent(); } - } + } while (p); + + //qDebug() << "updateAccessibility(), hwnd:" << w << ", object:" << o << "," << eventString(reason); if (!w) { if (reason != QAccessible::ContextHelpStart && reason != QAccessible::ContextHelpEnd) @@ -282,12 +410,81 @@ void QAccessible::updateAccessibility(QObject *o, int who, Event reason) } } + WId wid = w->internalWinId(); if (reason != MenuCommand) { // MenuCommand is faked - ptrNotifyWinEvent(reason, w->winId(), OBJID_CLIENT, who); + if (w != o) { + // See comment "SENDING EVENTS TO OBJECTS WITH NO WINDOW HANDLE" + eventNum %= 50; //[0..49] + int eventId = - eventNum - 1; + + qAccessibleRecentSentEvents()->insert(eventId, qMakePair(o,who)); + ptrNotifyWinEvent(reason, wid, OBJID_CLIENT, eventId ); + + ++eventNum; + } else { + ptrNotifyWinEvent(reason, wid, OBJID_CLIENT, who); + } } #endif // Q_WS_WINCE } +/* == SENDING EVENTS TO OBJECTS WITH NO WINDOW HANDLE == + + If the user requested to send the event to a widget with no window, + we need to send an event to an object with no hwnd. + The way we do that is to send it to the *first* ancestor widget + with a window. + Then we'll need a way of identifying the child: + We'll just keep a list of the most recent events that we have sent, + where each entry in the list is identified by a negative value + between [-50,-1]. This negative value we will pass on to + NotifyWinEvent() as the child id. When the negative value have + reached -50, it will wrap around to -1. This seems to be enough + + Now, when the client receives that event, he will first call + AccessibleObjectFromEvent() where dwChildID is the special + negative value. AccessibleObjectFromEvent does two steps: + 1. It will first sent a WM_GETOBJECT to the server, asking + for the IAccessible interface for the HWND. + 2. With the IAccessible interface it got hold of it will call + acc_getChild where the child id argument is the special + negative identifier. In our reimplementation of get_accChild + we check for this if the child id is negative. If it is, then + we'll look up in our table for the entry that is associated + with that value. + The entry will then contain a pointer to the QObject /QWidget + that we can use to call queryAccessibleInterface() on. + + + The following figure shows how the interaction between server and + client is in the case when the server is sending an event. + +SERVER (Qt) | CLIENT | +--------------------------------------------+---------------------------------------+ + | +acc->updateAccessibility(obj, childIndex) | + | +recentEvents()->insert(- 1 - eventNum, | + qMakePair(obj, childIndex) | +NotifyWinEvent(hwnd, childId) => | + | AccessibleObjectFromEvent(event, hwnd, OBJID_CLIENT, childId ) + | will do: + <=== 1. send WM_GETOBJECT(hwnd, OBJID_CLIENT) +widget ~= hwnd +iface = queryAccessibleInteface(widget) +(create IAccessible interface wrapper for + iface) + return iface ===> IAccessible* iface; (for hwnd) + | + <=== call iface->get_accChild(childId) +get_accChild() { | + if (varChildID.lVal < 0) { + QPair ref = recentEvents().value(varChildID.lVal); + [...] + } +*/ + + void QAccessible::setRootObject(QObject *o) { if (rootObjectHandler) { @@ -403,6 +600,35 @@ HRESULT STDMETHODCALLTYPE QWindowsEnumerate::Skip(unsigned long celt) return S_OK; } +struct AccessibleElement { + AccessibleElement(int entryId, QAccessibleInterface *accessible) { + if (entryId < 0) { + QPair<QObject*, int> ref = qAccessibleRecentSentEvents()->value(entryId); + iface = QAccessible::queryAccessibleInterface(ref.first); + entry = ref.second; + cleanupInterface = true; + } else { + iface = accessible; + entry = entryId; + cleanupInterface = false; + } + } + + QString text(QAccessible::Text t) const { + return iface ? iface->text(t, entry) : QString(); + } + + ~AccessibleElement() { + if (cleanupInterface) + delete iface; + } + + QAccessibleInterface *iface; + int entry; +private: + bool cleanupInterface; +}; + /* */ class QWindowsAccessible : public IAccessible, IOleWindow, QAccessible @@ -418,15 +644,18 @@ public: delete accessible; } + /* IUnknown */ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); + /* IDispatch */ HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int *); HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int, unsigned long, ITypeInfo **); HRESULT STDMETHODCALLTYPE GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *); HRESULT STDMETHODCALLTYPE Invoke(long, const _GUID &, unsigned long, unsigned short, tagDISPPARAMS *, tagVARIANT *, tagEXCEPINFO *, unsigned int *); + /* IAccessible */ HRESULT STDMETHODCALLTYPE accHitTest(long xLeft, long yTop, VARIANT *pvarID); HRESULT STDMETHODCALLTYPE accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID); HRESULT STDMETHODCALLTYPE accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd); @@ -451,6 +680,7 @@ public: HRESULT STDMETHODCALLTYPE get_accFocus(VARIANT *pvarID); HRESULT STDMETHODCALLTYPE get_accSelection(VARIANT *pvarChildren); + /* IOleWindow */ HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd); HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode); @@ -797,7 +1027,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::accLocation(long *pxLeft, long *py if (!accessible->isValid()) return E_FAIL; - QRect rect = accessible->rect(varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QRect rect = elem.iface ? elem.iface->rect(elem.entry) : QRect(); if (rect.isValid()) { *pxLeft = rect.x(); *pyTop = rect.y(); @@ -896,9 +1127,17 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, I if (varChildID.vt == VT_EMPTY) return E_INVALIDARG; + + int childIndex = varChildID.lVal; QAccessibleInterface *acc = 0; - RelationFlag rel = varChildID.lVal ? Child : Self; - accessible->navigate(rel, varChildID.lVal, &acc); + + AccessibleElement elem(childIndex, accessible); + if (elem.iface) { + RelationFlag rel = elem.entry ? Child : Self; + int index = elem.iface->navigate(rel, elem.entry, &acc); + if (index == -1) + return E_INVALIDARG; + } if (acc) { QWindowsAccessible* wacc = new QWindowsAccessible(acc); @@ -949,7 +1188,9 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::accDoDefaultAction(VARIANT varID) if (!accessible->isValid()) return E_FAIL; - return accessible->doAction(DefaultAction, varID.lVal, QVariantList()) ? S_OK : S_FALSE; + AccessibleElement elem(varID.lVal, accessible); + const bool res = elem.iface ? elem.iface->doAction(DefaultAction, elem.entry, QVariantList()) : false; + return res ? S_OK : S_FALSE; } HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction) @@ -958,7 +1199,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDefaultAction(VARIANT varID if (!accessible->isValid()) return E_FAIL; - QString def = accessible->actionText(DefaultAction, Name, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString def = elem.iface ? elem.iface->actionText(DefaultAction, Name, elem.entry) : QString(); if (def.isEmpty()) { *pszDefaultAction = 0; return S_FALSE; @@ -974,7 +1216,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accDescription(VARIANT varID, if (!accessible->isValid()) return E_FAIL; - QString descr = accessible->text(Description, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString descr = elem.text(Description); if (descr.size()) { *pszDescription = QStringToBSTR(descr); return S_OK; @@ -990,7 +1233,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accHelp(VARIANT varID, BSTR *p if (!accessible->isValid()) return E_FAIL; - QString help = accessible->text(Help, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString help = elem.text(Help); if (help.size()) { *pszHelp = QStringToBSTR(help); return S_OK; @@ -1011,7 +1255,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accKeyboardShortcut(VARIANT va if (!accessible->isValid()) return E_FAIL; - QString sc = accessible->text(Accelerator, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString sc = elem.text(Accelerator); if (sc.size()) { *pszKeyboardShortcut = QStringToBSTR(sc); return S_OK; @@ -1027,7 +1272,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accName(VARIANT varID, BSTR* p if (!accessible->isValid()) return E_FAIL; - QString n = accessible->text(Name, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString n = elem.text(Name); if (n.size()) { *pszName = QStringToBSTR(n); return S_OK; @@ -1049,7 +1295,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accRole(VARIANT varID, VARIANT if (!accessible->isValid()) return E_FAIL; - Role role = accessible->role(varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + Role role = elem.iface ? elem.iface->role(elem.entry) : NoRole; if (role != NoRole) { if (role == LayeredPane) role = QAccessible::Pane; @@ -1068,7 +1315,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accState(VARIANT varID, VARIAN return E_FAIL; (*pvarState).vt = VT_I4; - (*pvarState).lVal = accessible->state(varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + (*pvarState).lVal = elem.iface ? elem.iface->state(elem.entry) : 0; return S_OK; } @@ -1078,7 +1326,8 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accValue(VARIANT varID, BSTR* if (!accessible->isValid()) return E_FAIL; - QString value = accessible->text(Value, varID.lVal); + AccessibleElement elem(varID.lVal, accessible); + QString value = elem.text(Value); if (!value.isNull()) { *pszValue = QStringToBSTR(value); return S_OK; @@ -1102,19 +1351,23 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::accSelect(long flagsSelect, VARIAN bool res = false; - if (flagsSelect & SELFLAG_TAKEFOCUS) - res = accessible->doAction(SetFocus, varID.lVal, QVariantList()); - if (flagsSelect & SELFLAG_TAKESELECTION) { - accessible->doAction(ClearSelection, 0, QVariantList()); - res = accessible->doAction(AddToSelection, varID.lVal, QVariantList()); + AccessibleElement elem(varID.lVal, accessible); + QAccessibleInterface *acc = elem.iface; + if (acc) { + const int entry = elem.entry; + if (flagsSelect & SELFLAG_TAKEFOCUS) + res = acc->doAction(SetFocus, entry, QVariantList()); + if (flagsSelect & SELFLAG_TAKESELECTION) { + acc->doAction(ClearSelection, 0, QVariantList()); //### bug, 0 should be entry?? + res = acc->doAction(AddToSelection, entry, QVariantList()); + } + if (flagsSelect & SELFLAG_EXTENDSELECTION) + res = acc->doAction(ExtendSelection, entry, QVariantList()); + if (flagsSelect & SELFLAG_ADDSELECTION) + res = acc->doAction(AddToSelection, entry, QVariantList()); + if (flagsSelect & SELFLAG_REMOVESELECTION) + res = acc->doAction(RemoveSelection, entry, QVariantList()); } - if (flagsSelect & SELFLAG_EXTENDSELECTION) - res = accessible->doAction(ExtendSelection, varID.lVal, QVariantList()); - if (flagsSelect & SELFLAG_ADDSELECTION) - res = accessible->doAction(AddToSelection, varID.lVal, QVariantList()); - if (flagsSelect & SELFLAG_REMOVESELECTION) - res = accessible->doAction(RemoveSelection, varID.lVal, QVariantList()); - return res ? S_OK : S_FALSE; } @@ -1203,7 +1456,7 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::GetWindow(HWND *phwnd) if (!o || !o->isWidgetType()) return E_FAIL; - *phwnd = static_cast<QWidget*>(o)->winId(); + *phwnd = static_cast<QWidget*>(o)->effectiveWinId(); return S_OK; } diff --git a/src/gui/accessible/qaccessibleobject.cpp b/src/gui/accessible/qaccessibleobject.cpp index baaf129..20f3a15 100644 --- a/src/gui/accessible/qaccessibleobject.cpp +++ b/src/gui/accessible/qaccessibleobject.cpp @@ -276,7 +276,7 @@ QAccessible::Relation QAccessibleApplication::relationTo(int child, const for (int i = 0; i < tlw.count(); ++i) { QWidget *w = tlw.at(i); - QObjectList cl = qFindChildren<QObject *>(w, QString()); + QObjectList cl = w->findChildren<QObject *>(QString()); if (cl.contains(o)) return Ancestor; } @@ -322,9 +322,7 @@ QString QAccessibleApplication::text(Text t, int) const { switch (t) { case Name: - if (QApplication::activeWindow()) - return QApplication::activeWindow()->windowTitle(); - break; + return QApplication::applicationName(); case Description: return QApplication::applicationFilePath(); default: diff --git a/src/gui/accessible/qaccessiblewidget.cpp b/src/gui/accessible/qaccessiblewidget.cpp index d42826e..6a7d7e9 100644 --- a/src/gui/accessible/qaccessiblewidget.cpp +++ b/src/gui/accessible/qaccessiblewidget.cpp @@ -704,13 +704,16 @@ int QAccessibleWidget::navigate(RelationFlag relation, int entry, int sibCount = pIface->childCount(); QAccessibleInterface *candidate = 0; for (int i = 0; i < sibCount && entry; ++i) { - pIface->navigate(Child, i+1, &candidate); - Q_ASSERT(candidate); - if (candidate->relationTo(0, this, 0) & Label) + const int childId = pIface->navigate(Child, i+1, &candidate); + Q_ASSERT(childId >= 0); + if (childId > 0) + candidate = pIface; + if (candidate->relationTo(childId, this, 0) & Label) --entry; if (!entry) break; - delete candidate; + if (candidate != pIface) + delete candidate; candidate = 0; } if (!candidate) { @@ -1015,7 +1018,7 @@ QVariant QAccessibleWidgetEx::invokeMethodEx(Method method, int child, const QVa case ListSupportedMethods: { QSet<QAccessible::Method> set; set << ListSupportedMethods << ForegroundColor << BackgroundColor; - return qVariantFromValue(set); + return QVariant::fromValue(set); } case ForegroundColor: return widget()->palette().color(widget()->foregroundRole()); diff --git a/src/gui/dialogs/dialogs.pri b/src/gui/dialogs/dialogs.pri index 365f589..1dddb44 100644 --- a/src/gui/dialogs/dialogs.pri +++ b/src/gui/dialogs/dialogs.pri @@ -27,7 +27,7 @@ HEADERS += \ dialogs/qwizard.h \ dialogs/qprintpreviewdialog.h -!embedded:mac { +!embedded:!qpa:mac { OBJECTIVE_SOURCES += dialogs/qfiledialog_mac.mm \ dialogs/qfontdialog_mac.mm \ dialogs/qnspanelproxy_mac.mm \ @@ -61,7 +61,7 @@ win32 { !win32-borland:!wince*: LIBS += -lshell32 # the filedialog needs this library } -!mac:!embedded:!symbian:unix { +!mac:!embedded:!symbian:unix|qpa { HEADERS += dialogs/qpagesetupdialog_unix_p.h SOURCES += dialogs/qprintdialog_unix.cpp \ dialogs/qpagesetupdialog_unix.cpp diff --git a/src/gui/dialogs/qcolordialog_mac.mm b/src/gui/dialogs/qcolordialog_mac.mm index ee6e9bd..1d77751 100644 --- a/src/gui/dialogs/qcolordialog_mac.mm +++ b/src/gui/dialogs/qcolordialog_mac.mm @@ -185,7 +185,7 @@ QT_USE_NAMESPACE [self relayout]; } -- (void)colorChanged:(NSNotification *)notification; +- (void)colorChanged:(NSNotification *)notification { Q_UNUSED(notification); [self updateQtColor]; @@ -343,6 +343,7 @@ QT_USE_NAMESPACE mDialogIsExecuting = true; bool modalEnded = false; while (!modalEnded) { +#ifndef QT_NO_EXCEPTIONS @try { [NSApp runModalForWindow:mColorPanel]; modalEnded = true; @@ -351,6 +352,10 @@ QT_USE_NAMESPACE // clicking on 'SelectedMenuItemColor' from the 'Developer' // palette (tab three). } +#else + [NSApp runModalForWindow:mColorPanel]; + modalEnded = true; +#endif } QAbstractEventDispatcher::instance()->interrupt(); @@ -439,7 +444,7 @@ void QColorDialogPrivate::openCocoaColorPanel(const QColor &initial, priv:this]; [colorPanel setDelegate:static_cast<QT_MANGLE_NAMESPACE(QCocoaColorPanelDelegate) *>(delegate)]; } - [delegate setResultSet:false]; + [static_cast<QT_MANGLE_NAMESPACE(QCocoaColorPanelDelegate) *>(delegate) setResultSet:NO]; setCocoaPanelColor(initial); [static_cast<QT_MANGLE_NAMESPACE(QCocoaColorPanelDelegate) *>(delegate) showColorPanel]; } diff --git a/src/gui/dialogs/qdialog.cpp b/src/gui/dialogs/qdialog.cpp index 4c6c3bc..2fb6c67 100644 --- a/src/gui/dialogs/qdialog.cpp +++ b/src/gui/dialogs/qdialog.cpp @@ -337,7 +337,7 @@ void QDialogPrivate::setDefault(QPushButton *pushButton) { Q_Q(QDialog); bool hasMain = false; - QList<QPushButton*> list = qFindChildren<QPushButton*>(q); + QList<QPushButton*> list = q->findChildren<QPushButton*>(); for (int i=0; i<list.size(); ++i) { QPushButton *pb = list.at(i); if (pb->window() == q) { @@ -372,7 +372,7 @@ void QDialogPrivate::setMainDefault(QPushButton *pushButton) void QDialogPrivate::hideDefault() { Q_Q(QDialog); - QList<QPushButton*> list = qFindChildren<QPushButton*>(q); + QList<QPushButton*> list = q->findChildren<QPushButton*>(); for (int i=0; i<list.size(); ++i) { list.at(i)->setDefault(false); } @@ -675,7 +675,7 @@ void QDialog::keyPressEvent(QKeyEvent *e) switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: { - QList<QPushButton*> list = qFindChildren<QPushButton*>(this); + QList<QPushButton*> list = findChildren<QPushButton*>(); for (int i=0; i<list.size(); ++i) { QPushButton *pb = list.at(i); if (pb->isDefault() && pb->isVisible()) { diff --git a/src/gui/dialogs/qfiledialog.cpp b/src/gui/dialogs/qfiledialog.cpp index 710b593..5e8533e 100644 --- a/src/gui/dialogs/qfiledialog.cpp +++ b/src/gui/dialogs/qfiledialog.cpp @@ -66,6 +66,9 @@ #if defined(Q_OS_WINCE) extern bool qt_priv_ptr_valid; #endif +#if defined(Q_OS_UNIX) +#include <pwd.h> +#endif #endif QT_BEGIN_NAMESPACE @@ -858,23 +861,78 @@ void QFileDialog::selectFile(const QString &filename) d->lineEdit()->setText(file); } +#ifdef Q_OS_UNIX +Q_AUTOTEST_EXPORT QString qt_tildeExpansion(const QString &path, bool *expanded = 0) +{ + if (expanded != 0) + *expanded = false; + if (!path.startsWith(QLatin1Char('~'))) + return path; + QString ret = path; + QStringList tokens = ret.split(QDir::separator()); + if (tokens.first() == QLatin1String("~")) { + ret.replace(0, 1, QDir::homePath()); + } else { + QString userName = tokens.first(); + userName.remove(0, 1); +#if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD) + passwd pw; + passwd *tmpPw; + char buf[200]; + const int bufSize = sizeof(buf); + int err = getpwnam_r(userName.toLocal8Bit().constData(), &pw, buf, bufSize, &tmpPw); + if (err || !tmpPw) + return ret; + const QString homePath = QString::fromLocal8Bit(pw.pw_dir); +#else + passwd *pw = getpwnam(userName.toLocal8Bit().constData()); + if (!pw) + return ret; + const QString homePath = QString::fromLocal8Bit(pw->pw_dir); +#endif + ret.replace(0, tokens.first().length(), homePath); + } + if (expanded != 0) + *expanded = true; + return ret; +} +#endif + /** Returns the text in the line edit which can be one or more file names */ QStringList QFileDialogPrivate::typedFiles() const { + Q_Q(const QFileDialog); QStringList files; QString editText = lineEdit()->text(); - if (!editText.contains(QLatin1Char('"'))) + if (!editText.contains(QLatin1Char('"'))) { +#ifdef Q_OS_UNIX + const QString prefix = q->directory().absolutePath() + QDir::separator(); + if (QFile::exists(prefix + editText)) + files << editText; + else + files << qt_tildeExpansion(editText); +#else files << editText; - else { +#endif + } else { // " is used to separate files like so: "file1" "file2" "file3" ... // ### need escape character for filenames with quotes (") QStringList tokens = editText.split(QLatin1Char('\"')); for (int i=0; i<tokens.size(); ++i) { if ((i % 2) == 0) continue; // Every even token is a separator +#ifdef Q_OS_UNIX + const QString token = tokens.at(i); + const QString prefix = q->directory().absolutePath() + QDir::separator(); + if (QFile::exists(prefix + token)) + files << token; + else + files << qt_tildeExpansion(token); +#else files << toInternal(tokens.at(i)); +#endif } } return addDefaultSuffixToFiles(files); @@ -2258,9 +2316,9 @@ void QFileDialogPrivate::createWidgets() #ifndef QT_NO_FSCOMPLETER completer = new QFSCompleter(model, q); qFileDialogUi->fileNameEdit->setCompleter(completer); +#endif // QT_NO_FSCOMPLETER QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)), q, SLOT(_q_autoCompleteFileName(QString))); -#endif // QT_NO_FSCOMPLETER QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)), q, SLOT(_q_updateOkButton())); @@ -2996,6 +3054,7 @@ void QFileDialogPrivate::_q_useNameFilter(int index) const int fileNameExtensionLength = fileNameExtension.count(); fileName.replace(fileName.count() - fileNameExtensionLength, fileNameExtensionLength, newNameFilterExtension); + qFileDialogUi->listView->clearSelection(); lineEdit()->setText(fileName); } } @@ -3311,7 +3370,10 @@ QString QFSCompleter::pathFromIndex(const QModelIndex &index) const if (currentLocation == QDir::separator()) return path.mid(currentLocation.length()); #endif - return path.mid(currentLocation.length() + 1); + if (currentLocation.endsWith(QLatin1Char('/'))) + return path.mid(currentLocation.length()); + else + return path.mid(currentLocation.length()+1); } return index.data(QFileSystemModel::FilePathRole).toString(); } @@ -3334,6 +3396,17 @@ QStringList QFSCompleter::splitPath(const QString &path) const pathCopy = pathCopy.mid(2); else doubleSlash.clear(); +#elif defined(Q_OS_UNIX) + bool expanded; + pathCopy = qt_tildeExpansion(pathCopy, &expanded); + if (expanded) { + QFileSystemModel *dirModel; + if (proxyModel) + dirModel = qobject_cast<QFileSystemModel *>(proxyModel->sourceModel()); + else + dirModel = sourceModel; + dirModel->fetchMore(dirModel->index(pathCopy)); + } #endif QRegExp re(QLatin1Char('[') + QRegExp::escape(sep) + QLatin1Char(']')); @@ -3350,14 +3423,14 @@ QStringList QFSCompleter::splitPath(const QString &path) const parts.append(QString()); #else QStringList parts = pathCopy.split(re); - if (path[0] == sep[0]) // read the "/" at the beginning as the split removed it + if (pathCopy[0] == sep[0]) // read the "/" at the beginning as the split removed it parts[0] = sep[0]; #endif #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) bool startsFromRoot = !parts.isEmpty() && parts[0].endsWith(QLatin1Char(':')); #else - bool startsFromRoot = path[0] == sep[0]; + bool startsFromRoot = pathCopy[0] == sep[0]; #endif if (parts.count() == 1 || (parts.count() > 1 && !startsFromRoot)) { const QFileSystemModel *dirModel; diff --git a/src/gui/dialogs/qfiledialog_mac.mm b/src/gui/dialogs/qfiledialog_mac.mm index 8177395..f1d3a4a 100644 --- a/src/gui/dialogs/qfiledialog_mac.mm +++ b/src/gui/dialogs/qfiledialog_mac.mm @@ -63,6 +63,7 @@ #include <qdesktopwidget.h> #include <stdlib.h> #include <qabstracteventdispatcher.h> +#import <AppKit/NSSavePanel.h> #include "ui_qfiledialog.h" QT_BEGIN_NAMESPACE @@ -84,7 +85,13 @@ QT_USE_NAMESPACE @class QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate); -@interface QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) : NSObject { +@interface QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + : NSObject<NSOpenSavePanelDelegate> +#else + : NSObject +#endif +{ @public NSOpenPanel *mOpenPanel; NSSavePanel *mSavePanel; @@ -298,12 +305,13 @@ QT_USE_NAMESPACE QString qtFileName = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)(filename); QFileInfo info(qtFileName.normalized(QT_PREPEND_NAMESPACE(QString::NormalizationForm_C))); QString path = info.absolutePath(); + QString name = info.fileName(); if (path != *mLastFilterCheckPath){ *mLastFilterCheckPath = path; *mQDirFilterEntryList = info.dir().entryList(*mQDirFilter); } // Check if the QDir filter accepts the file: - if (!mQDirFilterEntryList->contains(info.fileName())) + if (!mQDirFilterEntryList->contains(name)) return NO; // No filter means accept everything @@ -311,7 +319,7 @@ QT_USE_NAMESPACE return YES; // Check if the current file name filter accepts the file: for (int i=0; i<mSelectedNameFilter->size(); ++i) { - if (QDir::match(mSelectedNameFilter->at(i), qtFileName)) + if (QDir::match(mSelectedNameFilter->at(i), name)) return YES; } return NO; @@ -546,9 +554,6 @@ void QFileDialogPrivate::QNSOpenSavePanelDelegate_filterSelected(int menuIndex) emit q_func()->filterSelected(nameFilters.at(menuIndex)); } -extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp -extern void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding=0, int len=-1); // qglobal.cpp - void QFileDialogPrivate::setDirectory_sys(const QString &directory) { #ifndef QT_MAC_USE_COCOA diff --git a/src/gui/dialogs/qfiledialog_symbian.cpp b/src/gui/dialogs/qfiledialog_symbian.cpp index b7186e5..1ffbf1d 100644 --- a/src/gui/dialogs/qfiledialog_symbian.cpp +++ b/src/gui/dialogs/qfiledialog_symbian.cpp @@ -59,7 +59,7 @@ extern QStringList qt_clean_filter_list(const QString &filter); // defined in qf enum DialogMode { DialogOpen, DialogSave, DialogFolder }; #if defined(Q_WS_S60) && !defined(SYMBIAN_VERSION_9_4) && !defined(SYMBIAN_VERSION_9_3) && !defined(SYMBIAN_VERSION_9_2) -class CExtensionFilter : public MAknFileFilter +class CExtensionFilter : public CBase, public MAknFileFilter { public: void setFilter(const QString filter) @@ -127,7 +127,7 @@ static QString launchSymbianDialog(const QString dialogCaption, const QString st extensionFilter->setFilter(filter); select = AknCommonDialogsDynMem::RunSelectDlgLD(types, target, startFolder, 0, 0, titlePtr, extensionFilter); - CleanupStack::Pop(extensionFilter); + CleanupStack::PopAndDestroy(extensionFilter); } else if (dialogMode == DialogSave) { QString defaultFileName = QFileDialogPrivate::initialSelection(startDirectory); target = qt_QString2TPtrC(defaultFileName); diff --git a/src/gui/dialogs/qfiledialog_win.cpp b/src/gui/dialogs/qfiledialog_win.cpp index 78803ba..de8e33d 100644 --- a/src/gui/dialogs/qfiledialog_win.cpp +++ b/src/gui/dialogs/qfiledialog_win.cpp @@ -60,7 +60,6 @@ #endif #ifdef Q_WS_WINCE -#include <shlobj.h> #include <commdlg.h> bool qt_priv_ptr_valid = false; #else @@ -603,7 +602,7 @@ QString qt_win_CID_get_existing_directory(const QFileDialogArgs &args) // Set the FOS_PICKFOLDERS flag DWORD newOptions; hr = pfd->GetOptions(&newOptions); - newOptions |= FOS_PICKFOLDERS; + newOptions |= (FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM); if (SUCCEEDED(hr) && SUCCEEDED((hr = pfd->SetOptions(newOptions)))) { QWidget *parentWindow = args.parent; if (parentWindow) diff --git a/src/gui/dialogs/qfiledialog_win_p.h b/src/gui/dialogs/qfiledialog_win_p.h index 1ff29d2..1408057 100644 --- a/src/gui/dialogs/qfiledialog_win_p.h +++ b/src/gui/dialogs/qfiledialog_win_p.h @@ -240,4 +240,4 @@ DECLARE_INTERFACE_(IFileOpenDialog, IFileDialog) STDMETHOD(GetResults)(THIS_ IShellItemArray **ppenum) PURE; STDMETHOD(GetSelectedItems)(THIS_ IShellItemArray **ppsai) PURE; }; -#endif
\ No newline at end of file +#endif diff --git a/src/gui/dialogs/qfileinfogatherer.cpp b/src/gui/dialogs/qfileinfogatherer.cpp index 2d551d8..315b931 100644 --- a/src/gui/dialogs/qfileinfogatherer.cpp +++ b/src/gui/dialogs/qfileinfogatherer.cpp @@ -78,11 +78,11 @@ QFileInfoGatherer::QFileInfoGatherer(QObject *parent) #endif m_resolveSymlinks(false), m_iconProvider(&defaultProvider) { -#ifndef Q_OS_WIN +#ifdef Q_OS_WIN + m_resolveSymlinks = true; +#elif !defined(Q_OS_INTEGRITY) userId = getuid(); groupId = getgid(); -#else - m_resolveSymlinks = true; #endif #ifndef QT_NO_FILESYSTEMWATCHER watcher = new QFileSystemWatcher(this); diff --git a/src/gui/dialogs/qfileinfogatherer_p.h b/src/gui/dialogs/qfileinfogatherer_p.h index ff3ef85..98217c1 100644 --- a/src/gui/dialogs/qfileinfogatherer_p.h +++ b/src/gui/dialogs/qfileinfogatherer_p.h @@ -84,10 +84,13 @@ public: && permissions() == fileInfo.permissions(); } +#ifndef QT_NO_FSFILEENGINE bool isCaseSensitive() const { QFSFileEngine fe(mFileInfo.absoluteFilePath()); return fe.caseSensitive(); } +#endif + QFile::Permissions permissions() const { return mFileInfo.permissions(); } diff --git a/src/gui/dialogs/qfilesystemmodel.cpp b/src/gui/dialogs/qfilesystemmodel.cpp index f9f9df8..10d627c 100644 --- a/src/gui/dialogs/qfilesystemmodel.cpp +++ b/src/gui/dialogs/qfilesystemmodel.cpp @@ -1313,6 +1313,10 @@ QString QFileSystemModelPrivate::filePath(const QModelIndex &index) const if ((fullPath.length() > 2) && fullPath[0] == QLatin1Char('/') && fullPath[1] == QLatin1Char('/')) fullPath = fullPath.mid(1); #endif +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + if (fullPath.length() == 2 && fullPath.endsWith(QLatin1Char(':'))) + fullPath.append(QLatin1Char('/')); +#endif return fullPath; } @@ -1454,7 +1458,6 @@ void QFileSystemModel::setIconProvider(QFileIconProvider *provider) { Q_D(QFileSystemModel); d->fileInfoGatherer.setIconProvider(provider); - QApplication::processEvents(); d->root.updateIcon(provider, QString()); } @@ -1631,6 +1634,14 @@ bool QFileSystemModel::event(QEvent *event) return QAbstractItemModel::event(event); } +bool QFileSystemModel::rmdir(const QModelIndex &aindex) const +{ + QString path = filePath(aindex); + QFileSystemModelPrivate * d = const_cast<QFileSystemModelPrivate*>(d_func()); + d->fileInfoGatherer.removePath(path); + return QDir().rmdir(path); +} + /*! \internal @@ -1966,13 +1977,14 @@ bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) co const bool hideHidden = !(filters & QDir::Hidden); const bool hideSystem = !(filters & QDir::System); const bool hideSymlinks = (filters & QDir::NoSymLinks); - const bool hideDotAndDotDot = (filters & QDir::NoDotAndDotDot); + const bool hideDot = (filters & QDir::NoDot) || (filters & QDir::NoDotAndDotDot); // ### Qt5: simplify (because NoDotAndDotDot=NoDot|NoDotDot) + const bool hideDotDot = (filters & QDir::NoDotDot) || (filters & QDir::NoDotAndDotDot); // ### Qt5: simplify (because NoDotAndDotDot=NoDot|NoDotDot) // Note that we match the behavior of entryList and not QFileInfo on this and this // incompatibility won't be fixed until Qt 5 at least - bool isDotOrDot = ( (node->fileName == QLatin1String(".") - || node->fileName == QLatin1String(".."))); - if ( (hideHidden && (!isDotOrDot && node->isHidden())) + bool isDot = (node->fileName == QLatin1String(".")); + bool isDotDot = (node->fileName == QLatin1String("..")); + if ( (hideHidden && !(isDot || isDotDot) && node->isHidden()) || (hideSystem && node->isSystem()) || (hideDirs && node->isDir()) || (hideFiles && node->isFile()) @@ -1980,7 +1992,8 @@ bool QFileSystemModelPrivate::filtersAcceptsNode(const QFileSystemNode *node) co || (hideReadable && node->isReadable()) || (hideWritable && node->isWritable()) || (hideExecutable && node->isExecutable()) - || (hideDotAndDotDot && isDotOrDot)) + || (hideDot && isDot) + || (hideDotDot && isDotDot)) return false; return nameFilterDisables || passNameFilters(node); diff --git a/src/gui/dialogs/qfilesystemmodel.h b/src/gui/dialogs/qfilesystemmodel.h index 1e4d673..0b1fa47 100644 --- a/src/gui/dialogs/qfilesystemmodel.h +++ b/src/gui/dialogs/qfilesystemmodel.h @@ -139,7 +139,7 @@ public: QDateTime lastModified(const QModelIndex &index) const; QModelIndex mkdir(const QModelIndex &parent, const QString &name); - inline bool rmdir(const QModelIndex &index) const; + bool rmdir(const QModelIndex &index) const; // ### Qt5: should not be const inline QString fileName(const QModelIndex &index) const; inline QIcon fileIcon(const QModelIndex &index) const; QFile::Permissions permissions(const QModelIndex &index) const; @@ -163,8 +163,6 @@ private: friend class QFileDialogPrivate; }; -inline bool QFileSystemModel::rmdir(const QModelIndex &aindex) const -{ QDir dir; return dir.rmdir(filePath(aindex)); } inline QString QFileSystemModel::fileName(const QModelIndex &aindex) const { return aindex.data(Qt::DisplayRole).toString(); } inline QIcon QFileSystemModel::fileIcon(const QModelIndex &aindex) const diff --git a/src/gui/dialogs/qfontdialog_mac.mm b/src/gui/dialogs/qfontdialog_mac.mm index ffe1290..088aa52 100644 --- a/src/gui/dialogs/qfontdialog_mac.mm +++ b/src/gui/dialogs/qfontdialog_mac.mm @@ -51,6 +51,7 @@ #include <private/qt_mac_p.h> #include <qabstracteventdispatcher.h> #include <qdebug.h> +#include <private/qfontengine_coretext_p.h> #import <AppKit/AppKit.h> #import <Foundation/Foundation.h> @@ -346,7 +347,7 @@ static QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont) [self relayoutToContentSize:[[mStolenContentView superview] frame].size]; } -- (void)relayoutToContentSize:(NSSize)frameSize; +- (void)relayoutToContentSize:(NSSize)frameSize { Q_ASSERT(mPanelHackedWithButtons); @@ -505,7 +506,7 @@ void QFontDialogPrivate::setFont(void *delegate, const QFont &font) QMacCocoaAutoReleasePool pool; QFontEngine *fe = font.d->engineForScript(QUnicodeTables::Common); NSFontManager *mgr = [NSFontManager sharedFontManager]; - NSFont *nsFont = 0; + const NSFont *nsFont = 0; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 if (qstrcmp(fe->name(), "CoreText") == 0) { @@ -531,7 +532,7 @@ void QFontDialogPrivate::setFont(void *delegate, const QFont &font) size:fontInfo.pointSize()]; } - [mgr setSelectedFont:nsFont isMultiple:NO]; + [mgr setSelectedFont:const_cast<NSFont *>(nsFont) isMultiple:NO]; [static_cast<QT_MANGLE_NAMESPACE(QCocoaFontPanelDelegate) *>(delegate) setQtFont:font]; } diff --git a/src/gui/dialogs/qinputdialog.cpp b/src/gui/dialogs/qinputdialog.cpp index e23ca59..5ca947c 100644 --- a/src/gui/dialogs/qinputdialog.cpp +++ b/src/gui/dialogs/qinputdialog.cpp @@ -1136,6 +1136,8 @@ void QInputDialog::done(int result) be entered). \a text is the default text which is placed in the line edit. \a mode is the echo mode the line edit will use. + \a inputMethodHints is the input method hints that will be used in the + edit widget if an input method is active. If \a ok is nonnull \e *\a ok will be set to true if the user pressed \gui OK and to false if the user pressed \gui Cancel. The dialog's parent @@ -1158,13 +1160,14 @@ void QInputDialog::done(int result) QString QInputDialog::getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode, const QString &text, bool *ok, - Qt::WindowFlags flags) + Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints) { QInputDialog dialog(parent, flags); dialog.setWindowTitle(title); dialog.setLabelText(label); dialog.setTextValue(text); dialog.setTextEchoMode(mode); + dialog.setInputMethodHints(inputMethodHints); int ret = dialog.exec(); if (ok) @@ -1177,6 +1180,17 @@ QString QInputDialog::getText(QWidget *parent, const QString &title, const QStri } /*! + \internal +*/ +// ### Qt 5: Use only the version above. +QString QInputDialog::getText(QWidget *parent, const QString &title, const QString &label, + QLineEdit::EchoMode mode, const QString &text, bool *ok, + Qt::WindowFlags flags) +{ + return getText(parent, title, label, mode, text, ok, flags, Qt::ImhNone); +} + +/*! \since 4.5 Static convenience function to get an integer input from the user. @@ -1286,6 +1300,8 @@ double QInputDialog::getDouble(QWidget *parent, const QString &title, const QStr be entered). \a items is the string list which is inserted into the combobox. \a current is the number of the item which should be the current item. + \a inputMethodHints is the input method hints that will be used if the + combobox is editable and an input method is active. If \a editable is true the user can enter their own text; otherwise the user may only select one of the existing items. @@ -1310,7 +1326,7 @@ double QInputDialog::getDouble(QWidget *parent, const QString &title, const QStr QString QInputDialog::getItem(QWidget *parent, const QString &title, const QString &label, const QStringList &items, int current, bool editable, bool *ok, - Qt::WindowFlags flags) + Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints) { QString text(items.value(current)); @@ -1320,6 +1336,7 @@ QString QInputDialog::getItem(QWidget *parent, const QString &title, const QStri dialog.setComboBoxItems(items); dialog.setTextValue(text); dialog.setComboBoxEditable(editable); + dialog.setInputMethodHints(inputMethodHints); int ret = dialog.exec(); if (ok) @@ -1332,6 +1349,17 @@ QString QInputDialog::getItem(QWidget *parent, const QString &title, const QStri } /*! + \internal +*/ +// ### Qt 5: Use only the version above. +QString QInputDialog::getItem(QWidget *parent, const QString &title, const QString &label, + const QStringList &items, int current, bool editable, bool *ok, + Qt::WindowFlags flags) +{ + return getItem(parent, title, label, items, current, editable, ok, flags, Qt::ImhNone); +} + +/*! \obsolete Use getInt() instead. diff --git a/src/gui/dialogs/qinputdialog.h b/src/gui/dialogs/qinputdialog.h index c737929..7853945 100644 --- a/src/gui/dialogs/qinputdialog.h +++ b/src/gui/dialogs/qinputdialog.h @@ -167,18 +167,37 @@ public: void setVisible(bool visible); +#ifdef Q_QDOC + static QString getText(QWidget *parent, const QString &title, const QString &label, + QLineEdit::EchoMode echo = QLineEdit::Normal, + const QString &text = QString(), bool *ok = 0, Qt::WindowFlags flags = 0, + Qt::InputMethodHints inputMethodHints = Qt::ImhNone); + static QString getItem(QWidget *parent, const QString &title, const QString &label, + const QStringList &items, int current = 0, bool editable = true, + bool *ok = 0, Qt::WindowFlags flags = 0, + Qt::InputMethodHints inputMethodHints = Qt::ImhNone); +#else static QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode echo = QLineEdit::Normal, const QString &text = QString(), bool *ok = 0, Qt::WindowFlags flags = 0); + static QString getItem(QWidget *parent, const QString &title, const QString &label, + const QStringList &items, int current = 0, bool editable = true, + bool *ok = 0, Qt::WindowFlags flags = 0); + static QString getText(QWidget *parent, const QString &title, const QString &label, + QLineEdit::EchoMode echo, + const QString &text, bool *ok, Qt::WindowFlags flags, + Qt::InputMethodHints inputMethodHints); + static QString getItem(QWidget *parent, const QString &title, const QString &label, + const QStringList &items, int current, bool editable, + bool *ok, Qt::WindowFlags flags, + Qt::InputMethodHints inputMethodHints); +#endif static int getInt(QWidget *parent, const QString &title, const QString &label, int value = 0, int minValue = -2147483647, int maxValue = 2147483647, int step = 1, bool *ok = 0, Qt::WindowFlags flags = 0); static double getDouble(QWidget *parent, const QString &title, const QString &label, double value = 0, double minValue = -2147483647, double maxValue = 2147483647, int decimals = 1, bool *ok = 0, Qt::WindowFlags flags = 0); - static QString getItem(QWidget *parent, const QString &title, const QString &label, - const QStringList &items, int current = 0, bool editable = true, - bool *ok = 0, Qt::WindowFlags flags = 0); // obsolete static int getInteger(QWidget *parent, const QString &title, const QString &label, int value = 0, diff --git a/src/gui/dialogs/qmessagebox.cpp b/src/gui/dialogs/qmessagebox.cpp index f18fe60..66e7216 100644 --- a/src/gui/dialogs/qmessagebox.cpp +++ b/src/gui/dialogs/qmessagebox.cpp @@ -419,7 +419,7 @@ void QMessageBoxPrivate::updateSize() void QMessageBoxPrivate::hideSpecial() { Q_Q(QMessageBox); - QList<QPushButton*> list = qFindChildren<QPushButton*>(q); + QList<QPushButton*> list = q->findChildren<QPushButton*>(); for (int i=0; i<list.size(); ++i) { QPushButton *pb = list.at(i); QString text = pb->text(); @@ -1271,7 +1271,7 @@ bool QMessageBox::event(QEvent *e) (e->type() == QEvent::OkRequest) ? QApplication::translate("QMessageBox", "OK") : QApplication::translate("QMessageBox", "Help"); - QList<QPushButton*> list = qFindChildren<QPushButton*>(this); + QList<QPushButton*> list = findChildren<QPushButton*>(); for (int i=0; i<list.size(); ++i) { QPushButton *pb = list.at(i); if (pb->text() == bName) { @@ -1518,7 +1518,7 @@ static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, int(defaultButton), 0); QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent); - QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox*>(&msgBox); + QDialogButtonBox *buttonBox = msgBox.findChild<QDialogButtonBox*>(); Q_ASSERT(buttonBox != 0); uint mask = QMessageBox::FirstButton; @@ -1952,7 +1952,7 @@ void QMessageBoxPrivate::retranslateStrings() { #ifndef QT_NO_TEXTEDIT if (detailsButton) - detailsButton->setLabel(detailsText->isHidden() ? HideLabel : ShowLabel); + detailsButton->setLabel(detailsText->isHidden() ? ShowLabel : HideLabel); #endif } @@ -2486,7 +2486,7 @@ void QMessageBox::setInformativeText(const QString &text) } if (!d->informativeLabel) { - QLabel *label = new QLabel; + QLabel *label = new QLabel(this); label->setObjectName(QLatin1String("qt_msgbox_informativelabel")); label->setTextInteractionFlags(Qt::TextInteractionFlags(style()->styleHint(QStyle::SH_MessageBox_TextInteractionFlags, 0, this))); label->setAlignment(Qt::AlignTop | Qt::AlignLeft); diff --git a/src/gui/dialogs/qnspanelproxy_mac.mm b/src/gui/dialogs/qnspanelproxy_mac.mm index 70fd416..1de5484 100644 --- a/src/gui/dialogs/qnspanelproxy_mac.mm +++ b/src/gui/dialogs/qnspanelproxy_mac.mm @@ -42,6 +42,7 @@ #include <qdialogbuttonbox.h> #if defined(Q_WS_MAC) #include <private/qt_mac_p.h> +#include <private/qcocoaintrospection_p.h> #import <AppKit/AppKit.h> #import <Foundation/Foundation.h> #import <objc/objc-class.h> @@ -137,46 +138,6 @@ QT_USE_NAMESPACE QT_BEGIN_NAMESPACE -void macStartIntercept(SEL originalSel, SEL fakeSel, Class baseClass, Class proxyClass) -{ -#ifndef QT_MAC_USE_COCOA - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) -#endif - { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - // The following code replaces the _implementation_ for the selector we want to hack - // (originalSel) with the implementation found in proxyClass. Then it creates - // a new 'backup' method inside baseClass containing the old, original, - // implementation (fakeSel). You can let the proxy implementation of originalSel - // call fakeSel if needed (similar approach to calling a super class implementation). - // fakeSel must also be implemented in proxyClass, as the signature is used - // as template for the method one we add into baseClass. - // NB: You will typically never create any instances of proxyClass; we use it - // only for stealing its contents and put it into baseClass. - Method originalMethod = class_getInstanceMethod(baseClass, originalSel); - Method newMethod = class_getInstanceMethod(proxyClass, originalSel); - Method fakeMethod = class_getInstanceMethod(proxyClass, fakeSel); - - IMP originalImp = method_setImplementation(originalMethod, method_getImplementation(newMethod)); - class_addMethod(baseClass, fakeSel, originalImp, method_getTypeEncoding(fakeMethod)); -#endif - } -} - -void macStopIntercept(SEL originalSel, SEL fakeSel, Class baseClass, Class /* proxyClass */) -{ -#ifndef QT_MAC_USE_COCOA - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) -#endif - { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - Method originalMethod = class_getInstanceMethod(baseClass, originalSel); - Method fakeMethodInBaseClass = class_getInstanceMethod(baseClass, fakeSel); - method_setImplementation(originalMethod, method_getImplementation(fakeMethodInBaseClass)); -#endif - } -} - /* Intercept the NSColorPanel constructor if the shared color panel doesn't exist yet. What's going on here is @@ -188,12 +149,18 @@ void macStopIntercept(SEL originalSel, SEL fakeSel, Class baseClass, Class /* pr */ void macStartInterceptNSPanelCtor() { - macStartIntercept(@selector(initWithContentRect:styleMask:backing:defer:), - @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:), - [NSPanel class], [QT_MANGLE_NAMESPACE(QNSPanelProxy) class]); - macStartIntercept(@selector(initWithContentRect:styleMask:backing:defer:screen:), - @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:screen:), - [NSPanel class], [QT_MANGLE_NAMESPACE(QNSPanelProxy) class]); + qt_cocoa_change_implementation( + [NSPanel class], + @selector(initWithContentRect:styleMask:backing:defer:), + [QT_MANGLE_NAMESPACE(QNSPanelProxy) class], + @selector(initWithContentRect:styleMask:backing:defer:), + @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:)); + qt_cocoa_change_implementation( + [NSPanel class], + @selector(initWithContentRect:styleMask:backing:defer:screen:), + [QT_MANGLE_NAMESPACE(QNSPanelProxy) class], + @selector(initWithContentRect:styleMask:backing:defer:screen:), + @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:screen:)); } /* @@ -201,12 +168,14 @@ void macStartInterceptNSPanelCtor() */ void macStopInterceptNSPanelCtor() { - macStopIntercept(@selector(initWithContentRect:styleMask:backing:defer:screen:), - @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:screen:), - [NSPanel class], [QT_MANGLE_NAMESPACE(QNSPanelProxy) class]); - macStopIntercept(@selector(initWithContentRect:styleMask:backing:defer:), - @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:), - [NSPanel class], [QT_MANGLE_NAMESPACE(QNSPanelProxy) class]); + qt_cocoa_change_back_implementation( + [NSPanel class], + @selector(initWithContentRect:styleMask:backing:defer:screen:), + @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:screen:)); + qt_cocoa_change_back_implementation( + [NSPanel class], + @selector(initWithContentRect:styleMask:backing:defer:), + @selector(qt_fakeInitWithContentRect:styleMask:backing:defer:)); } /* @@ -216,8 +185,12 @@ void macStopInterceptNSPanelCtor() void macStartInterceptWindowTitle(QWidget *window) { currentWindow = window; - macStartIntercept(@selector(setTitle:), @selector(qt_fakeSetTitle:), - [NSWindow class], [QT_MANGLE_NAMESPACE(QNSWindowProxy) class]); + qt_cocoa_change_implementation( + [NSWindow class], + @selector(setTitle:), + [QT_MANGLE_NAMESPACE(QNSWindowProxy) class], + @selector(setTitle:), + @selector(qt_fakeSetTitle:)); } /* @@ -226,8 +199,10 @@ void macStartInterceptWindowTitle(QWidget *window) void macStopInterceptWindowTitle() { currentWindow = 0; - macStopIntercept(@selector(setTitle:), @selector(qt_fakeSetTitle:), - [NSWindow class], [QT_MANGLE_NAMESPACE(QNSWindowProxy) class]); + qt_cocoa_change_back_implementation( + [NSWindow class], + @selector(setTitle:), + @selector(qt_fakeSetTitle:)); } /* diff --git a/src/gui/dialogs/qpagesetupdialog_mac.mm b/src/gui/dialogs/qpagesetupdialog_mac.mm index 9a4b711..7255201 100644 --- a/src/gui/dialogs/qpagesetupdialog_mac.mm +++ b/src/gui/dialogs/qpagesetupdialog_mac.mm @@ -61,7 +61,7 @@ QT_USE_NAMESPACE @end @implementation QT_MANGLE_NAMESPACE(QCocoaPageLayoutDelegate) -- (id)initWithMacPrintEngine:(QMacPrintEnginePrivate *)printEngine; +- (id)initWithMacPrintEngine:(QMacPrintEnginePrivate *)printEngine { self = [super init]; if (self) { @@ -312,4 +312,4 @@ int QPageSetupDialog::exec() QT_END_NAMESPACE -#endif QT_NO_PRINTDIALOG +#endif /* QT_NO_PRINTDIALOG */ diff --git a/src/gui/egl/egl.pri b/src/gui/egl/egl.pri index c6c020d..8e8664c 100644 --- a/src/gui/egl/egl.pri +++ b/src/gui/egl/egl.pri @@ -6,9 +6,21 @@ contains(QT_CONFIG, egl): { egl/qeglcontext_p.h \ egl/qeglproperties_p.h - SOURCES += \ - egl/qegl.cpp \ - egl/qeglproperties.cpp + SOURCES += \ + egl/qegl.cpp \ + egl/qeglproperties.cpp + unix { + !isEmpty(QMAKE_INCDIR_EGL){ + INCLUDEPATH += $$QMAKE_INCDIR_EGL + } + !isEmpty(QMAKE_LIBDIR_EGL){ + for(p, QMAKE_LIBDIR_EGL) { + exists($$p):LIBS += -L$$p + } + } + + !isEmpty(QMAKE_LIBS_EGL): LIBS += $$QMAKE_LIBS_EGL + } wince*: SOURCES += egl/qegl_wince.cpp @@ -16,10 +28,14 @@ contains(QT_CONFIG, egl): { embedded { SOURCES += egl/qegl_qws.cpp } else { - symbian { - SOURCES += egl/qegl_symbian.cpp + qpa { + SOURCES += egl/qegl_qpa.cpp } else { - SOURCES += egl/qegl_x11.cpp + symbian { + SOURCES += egl/qegl_symbian.cpp + } else { + SOURCES += egl/qegl_x11.cpp + } } } } diff --git a/src/gui/egl/qegl.cpp b/src/gui/egl/qegl.cpp index 4db4a6a..2a37d45 100644 --- a/src/gui/egl/qegl.cpp +++ b/src/gui/egl/qegl.cpp @@ -395,7 +395,7 @@ bool QEglContext::createContext(QEglContext *shareContext, const QEglProperties } } if (ctx == EGL_NO_CONTEXT) { - ctx = eglCreateContext(display(), cfg, 0, contextProps.properties()); + ctx = eglCreateContext(display(), cfg, EGL_NO_CONTEXT, contextProps.properties()); if (ctx == EGL_NO_CONTEXT) { qWarning() << "QEglContext::createContext(): Unable to create EGL context:" << QEgl::errorString(); return false; diff --git a/src/gui/egl/qegl_p.h b/src/gui/egl/qegl_p.h index 9b0d82e..15e7cab 100644 --- a/src/gui/egl/qegl_p.h +++ b/src/gui/egl/qegl_p.h @@ -165,13 +165,21 @@ typedef int EGLImageKHR; typedef void *EGLImageKHR; #endif +#if !defined(EGL_NO_IMAGE_KHR) #define EGL_NO_IMAGE_KHR ((EGLImageKHR)0) +#endif +#if !defined(EGL_IMAGE_PRESERVED_KHR) #define EGL_IMAGE_PRESERVED_KHR 0x30D2 +#endif +#if !defined(EGL_KHR_image_base) #define EGL_KHR_image_base #endif +#endif -#if !defined(EGL_KHR_image) && !defined(EGL_KHR_image_pixmap) +#if !defined(EGL_KHR_image) #define EGL_NATIVE_PIXMAP_KHR 0x30B0 +#endif +#if !defined(EGL_KHR_image_pixmap) #define EGL_KHR_image_pixmap #endif diff --git a/src/gui/egl/qegl_qpa.cpp b/src/gui/egl/qegl_qpa.cpp new file mode 100644 index 0000000..9b99e8b --- /dev/null +++ b/src/gui/egl/qegl_qpa.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/qpaintdevice.h> +#include <QtGui/qpixmap.h> +#include <QtGui/qwidget.h> +#include "qeglcontext_p.h" + +#if !defined(QT_NO_EGL) + +#include <QtGui/private/qgraphicssystem_p.h> +#include <QtGui/private/qapplication_p.h> +#include <QtGui/qdesktopwidget.h> + +QT_BEGIN_NAMESPACE + +EGLNativeDisplayType QEgl::nativeDisplay() +{ + return EGLNativeDisplayType(EGL_DEFAULT_DISPLAY); +} + +EGLNativeWindowType QEgl::nativeWindow(QWidget* widget) +{ + return (EGLNativeWindowType)(widget->winId()); +} + +EGLNativePixmapType QEgl::nativePixmap(QPixmap* pixmap) +{ + Q_UNUSED(pixmap); + return 0; +} + +//EGLDisplay QEglContext::display() +//{ +// return eglGetDisplay(EGLNativeDisplayType(EGL_DEFAULT_DISPLAY)); +//} + +static QPlatformScreen *screenForDevice(QPaintDevice *device) +{ + QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); + + QList<QPlatformScreen *> screens = pi->screens(); + + int screenNumber; + if (device && device->devType() == QInternal::Widget) + screenNumber = qApp->desktop()->screenNumber(static_cast<QWidget *>(device)); + else + screenNumber = 0; + if (screenNumber < 0 || screenNumber >= screens.size()) + return 0; + return screens[screenNumber]; +} + +// Set pixel format and other properties based on a paint device. +void QEglProperties::setPaintDeviceFormat(QPaintDevice *dev) +{ + if (!dev) + return; + + // Find the QGLScreen for this paint device. + QPlatformScreen *screen = screenForDevice(dev); + if (!screen) + return; + int devType = dev->devType(); + if (devType == QInternal::Image) + setPixelFormat(static_cast<QImage *>(dev)->format()); + else + setPixelFormat(screen->format()); +} + +QT_END_NAMESPACE + +#endif // !QT_NO_EGL diff --git a/src/gui/egl/qegl_x11.cpp b/src/gui/egl/qegl_x11.cpp index 119414a..c0f8a24 100644 --- a/src/gui/egl/qegl_x11.cpp +++ b/src/gui/egl/qegl_x11.cpp @@ -330,10 +330,8 @@ void qt_set_winid_on_widget(QWidget* w, Qt::HANDLE id) // NOTE: The X11 version of createSurface will re-create the native drawable if it's visual doesn't // match the one for the passed in EGLConfig -EGLSurface QEgl::createSurface(QPaintDevice *device, EGLConfig config, const QEglProperties *unusedProperties) +EGLSurface QEgl::createSurface(QPaintDevice *device, EGLConfig config, const QEglProperties *properties) { - Q_UNUSED(unusedProperties); - int devType = device->devType(); if (devType == QInternal::Pbuffer) { @@ -417,7 +415,12 @@ EGLSurface QEgl::createSurface(QPaintDevice *device, EGLConfig config, const QEg // At this point, the widget's window should be created and have the correct visual. Now we // just need to create the EGL surface for it: - EGLSurface surf = eglCreateWindowSurface(QEgl::display(), config, (EGLNativeWindowType)widget->winId(), 0); + const int *props; + if (properties) + props = properties->properties(); + else + props = 0; + EGLSurface surf = eglCreateWindowSurface(QEgl::display(), config, (EGLNativeWindowType)widget->winId(), props); if (surf == EGL_NO_SURFACE) qWarning("QEglContext::createSurface(): Unable to create EGL surface, error = 0x%x", eglGetError()); return surf; diff --git a/src/gui/embedded/embedded.pri b/src/gui/embedded/embedded.pri index eb13d8d..836c116 100644 --- a/src/gui/embedded/embedded.pri +++ b/src/gui/embedded/embedded.pri @@ -117,7 +117,13 @@ embedded { contains( gfx-drivers, qnx ) { HEADERS += embedded/qscreenqnx_qws.h SOURCES += embedded/qscreenqnx_qws.cpp - LIBS += -lgf + LIBS_PRIVATE += -lgf + } + + contains( gfx-drivers, integrityfb ) { + HEADERS += embedded/qscreenintegrityfb_qws.h + SOURCES += embedded/qscreenintegrityfb_qws.cpp + LIBS += -lfbdev } contains( gfx-drivers, qvfb ) { @@ -174,6 +180,11 @@ embedded { SOURCES += embedded/qkbdqnx_qws.cpp } + contains( kbd-drivers, integrity ) { + HEADERS += embedded/qkbdintegrity_qws.h + SOURCES += embedded/qkbdintegrity_qws.cpp + } + # # Mouse drivers # @@ -207,4 +218,9 @@ embedded { HEADERS += embedded/qmouseqnx_qws.h SOURCES += embedded/qmouseqnx_qws.cpp } + + contains( mouse-drivers, integrity ) { + HEADERS += embedded/qmouseintegrity_qws.h + SOURCES += embedded/qmouseintegrity_qws.cpp + } } diff --git a/src/gui/embedded/qkbd_defaultmap_qws_p.h b/src/gui/embedded/qkbd_defaultmap_qws_p.h index 0c1aaf9..5aa8f83 100644 --- a/src/gui/embedded/qkbd_defaultmap_qws_p.h +++ b/src/gui/embedded/qkbd_defaultmap_qws_p.h @@ -642,7 +642,15 @@ const QWSKeyboard::Mapping QWSKbPrivate::s_keymap_default[] = { { 111, 0xffff, 0x01000007, 0x00, 0x00, 0x0000 }, { 111, 0xffff, 0x01000000, 0x06, 0x08, 0x0200 }, { 111, 0xffff, 0x01000000, 0x0c, 0x08, 0x0200 }, + { 113, 0xffff, 0x01000071, 0x00, 0x00, 0x0000 }, + { 114, 0xffff, 0x01000070, 0x00, 0x00, 0x0000 }, + { 115, 0xffff, 0x01000072, 0x00, 0x00, 0x0000 }, + { 116, 0xffff, 0x0100010b, 0x00, 0x00, 0x0000 }, { 119, 0xffff, 0x01000008, 0x00, 0x00, 0x0000 }, + { 138, 0xffff, 0x01000058, 0x00, 0x00, 0x0000 }, + { 139, 0xffff, 0x01000055, 0x00, 0x00, 0x0000 }, + { 152, 0xffff, 0x010000ba, 0x00, 0x00, 0x0000 }, + }; const QWSKeyboard::Composing QWSKbPrivate::s_keycompose_default[] = { diff --git a/src/gui/embedded/qkbd_qws.cpp b/src/gui/embedded/qkbd_qws.cpp index 91771e1..5edc4d9 100644 --- a/src/gui/embedded/qkbd_qws.cpp +++ b/src/gui/embedded/qkbd_qws.cpp @@ -48,8 +48,16 @@ #include <QDataStream> #include <QStringList> +#ifdef Q_WS_QWS #include "qwindowsystem_qws.h" #include "qscreen_qws.h" +#endif + +#ifdef Q_WS_QPA +#include <QWindowSystemInterface> +#include <QKeyEvent> +#endif + #include "qtimer.h" #include <stdlib.h> @@ -217,7 +225,7 @@ bool QWSKbPrivate::loadKeymap(const QString &file) ds >> qmap_magic >> qmap_version >> qmap_keymap_size >> qmap_keycompose_size; if (ds.status() != QDataStream::Ok || qmap_magic != QWSKeyboard::FileMagic || qmap_version != 1 || qmap_keymap_size == 0) { - qWarning("'%s' is ot a valid.qmap keymap file.", qPrintable(file)); + qWarning("'%s' is not a valid .qmap keymap file.", qPrintable(file)); return false; } @@ -352,7 +360,15 @@ QWSKeyboardHandler::~QWSKeyboardHandler() void QWSKeyboardHandler::processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers modifiers, bool isPress, bool autoRepeat) { +#if defined(Q_WS_QWS) qwsServer->processKeyEvent(unicode, keycode, modifiers, isPress, autoRepeat); +#elif defined(Q_WS_QPA) + QEvent::Type type = isPress ? QEvent::KeyPress : QEvent::KeyRelease; + QString str; + if (unicode != 0xffff) + str = QString(unicode); + QWindowSystemInterface::handleKeyEvent(0, type, keycode, modifiers, str); +#endif } /*! @@ -375,6 +391,7 @@ void QWSKeyboardHandler::processKeyEvent(int unicode, int keycode, Qt::KeyboardM */ int QWSKeyboardHandler::transformDirKey(int key) { +#ifdef Q_WS_QWS static int dir_keyrot = -1; if (dir_keyrot < 0) { // get the rotation @@ -387,6 +404,9 @@ int QWSKeyboardHandler::transformDirKey(int key) } int xf = qt_screen->transformOrientation() + dir_keyrot; return (key-Qt::Key_Left+xf)%4+Qt::Key_Left; +#else + return 0; +#endif } /*! @@ -454,7 +474,7 @@ void QWSKeyboardHandler::endAutoRepeat() Maps \a keycode according to a keymap and sends that key event to the \l{Qt for Embedded Linux} server application. - + Please see the \l{Qt for Embedded Linux Character Input} and the \l {kmap2qmap} documentations for a description on how to create and use keymap files. diff --git a/src/gui/embedded/qkbddriverfactory_qws.cpp b/src/gui/embedded/qkbddriverfactory_qws.cpp index 51ae16b..d474b5e 100644 --- a/src/gui/embedded/qkbddriverfactory_qws.cpp +++ b/src/gui/embedded/qkbddriverfactory_qws.cpp @@ -49,6 +49,7 @@ #include "qkbdum_qws.h" #include "qkbdvfb_qws.h" #include "qkbdqnx_qws.h" +#include "qkbdintegrity_qws.h" #include <stdlib.h> #include "private/qfactoryloader_p.h" #include "qkbddriverplugin_qws.h" @@ -106,6 +107,10 @@ QWSKeyboardHandler *QKbdDriverFactory::create(const QString& key, const QString& if (driver == QLatin1String("qnx") || driver.isEmpty()) return new QWSQnxKeyboardHandler(device); #endif +#if defined(Q_OS_INTEGRITY) + if (driver == QLatin1String("integrity") || driver.isEmpty()) + return new QWSIntKeyboardHandler(device); +#endif #ifndef QT_NO_QWS_KEYBOARD # ifndef QT_NO_QWS_KBD_TTY if (driver == QLatin1String("tty") || driver.isEmpty()) @@ -151,6 +156,9 @@ QStringList QKbdDriverFactory::keys() #if defined(Q_OS_QNX) && !defined(QT_NO_QWS_KBD_QNX) list << QLatin1String("QNX"); #endif +#if defined(Q_OS_INTEGRITY) && !defined(QT_NO_QWS_KBD_INTEGRITY) + list << QLatin1String("INTEGRITY"); +#endif #ifndef QT_NO_QWS_KBD_TTY list << QLatin1String("TTY"); #endif diff --git a/src/gui/embedded/qkbdintegrity_qws.cpp b/src/gui/embedded/qkbdintegrity_qws.cpp new file mode 100644 index 0000000..2dcbb6b --- /dev/null +++ b/src/gui/embedded/qkbdintegrity_qws.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if !defined(QT_NO_QWS_KEYBOARD) && !defined(QT_NO_QWS_KBD_INTEGRITY) + +#include "qkbdintegrity_qws.h" +#include <qwindowsystem_qws.h> +#include <qapplication.h> +#include <qtimer.h> +#include <qthread.h> + +#include <INTEGRITY.h> + + +//=========================================================================== + +QT_BEGIN_NAMESPACE + +// +// INTEGRITY keyboard +// + +class QIntKeyboardListenThread; + +class QWSIntKbPrivate : public QObject +{ + Q_OBJECT + friend class QIntKeyboardListenThread; +public: + QWSIntKbPrivate(QWSKeyboardHandler *, const QString &device); + ~QWSIntKbPrivate(); + void dataReady(int amount) { emit kbdDataAvailable(amount); } + uint8_t scancodebuf[32 /* USB_SCANCODE_BUF_LEN */ ]; + uint8_t rxpost; + uint8_t rxack; + +Q_SIGNALS: + void kbdDataAvailable(int amount); + +private Q_SLOTS: + void readKeyboardData(int amount); + +private: + QWSKeyboardHandler *handler; + QIntKeyboardListenThread *kbdthread; +}; +class QIntKeyboardListenThread : public QThread +{ +protected: + QWSIntKbPrivate *imp; + bool loop; +public: + QIntKeyboardListenThread(QWSIntKbPrivate *im) : QThread(), imp(im) {}; + ~QIntKeyboardListenThread() {}; + void run(); + void stoploop() { loop = false; }; +}; + + +QWSIntKeyboardHandler::QWSIntKeyboardHandler(const QString &device) + : QWSKeyboardHandler(device) +{ + d = new QWSIntKbPrivate(this, device); +} + +QWSIntKeyboardHandler::~QWSIntKeyboardHandler() +{ + delete d; +} + +//void QWSIntKeyboardHandler::processKeyEvent(int keycode, bool isPress, +// bool autoRepeat) +//{ +// QWSKeyboardHandler::processKeyEvent(keycode, isPress, autoRepeat); +//} + +void QIntKeyboardListenThread::run(void) +{ + Error E; + Buffer b; + Connection kbdc; + bool waitforresource = true; + do { + E = RequestResource((Object*)&kbdc, + "USBKeyboardClient", "!systempassword"); + if (E == Success) { + loop = false; + } else { + E = RequestResource((Object*)&kbdc, + "KeyboardClient", "!systempassword"); + if (E == Success) { + waitforresource = false; + } + } + if (waitforresource) + ::sleep(1); + } while (loop && waitforresource); + if (!loop) + return; + b.BufferType = DataBuffer | LastBuffer; + b.Length = sizeof(imp->scancodebuf); + b.TheAddress = (Address)imp->scancodebuf; + do { + b.Transferred = 0; + b.TheAddress = (Address)imp->scancodebuf + imp->rxpost; + CheckSuccess(SynchronousReceive(kbdc, &b)); + imp->rxpost += b.Transferred; + if (imp->rxpost >= 32 /* USB_SCANCODE_BUF_LEN */) + imp->rxpost = 0; + if (imp->rxpost == (imp->rxack + b.Transferred) % 32 /* USB_SCANCODE_BUF_LEN */) { + imp->kbdDataAvailable(b.Transferred); + } + } while (loop); +} + +void QWSIntKbPrivate::readKeyboardData(int amount) +{ + uint16_t keycode; + do { + if (scancodebuf[rxack] == 0xe0) { + keycode = scancodebuf[rxack] << 8; + rxack++; + if (rxack >= 32 /* USB_SCANCODE_BUF_LEN */) + rxack = 0; + } else { + keycode = 0; + } + + handler->processKeycode(keycode + (scancodebuf[rxack] & 0x7f), + (scancodebuf[rxack] & 0x80) == 0, + scancodebuf[rxack] == 2); + rxack++; + if (rxack >= 32 /* USB_SCANCODE_BUF_LEN */) + rxack = 0; + } while (rxack != rxpost); +} + +QWSIntKbPrivate::QWSIntKbPrivate(QWSKeyboardHandler *h, const QString &device) : handler(h) +{ + connect(this, SIGNAL(kbdDataAvailable(int)), this, SLOT(readKeyboardData(int))); + this->handler = handler; + rxack = rxpost = 0; + kbdthread = new QIntKeyboardListenThread(this); + kbdthread->start(); +} + +QWSIntKbPrivate::~QWSIntKbPrivate() +{ + kbdthread->stoploop(); + kbdthread->wait(); + delete kbdthread; +} + + +QT_END_NAMESPACE + +#include "qkbdintegrity_qws.moc" + +#endif // QT_NO_QWS_KEYBOARD || QT_NO_QWS_KBD_TTY diff --git a/src/gui/embedded/qkbdintegrity_qws.h b/src/gui/embedded/qkbdintegrity_qws.h new file mode 100644 index 0000000..811b152 --- /dev/null +++ b/src/gui/embedded/qkbdintegrity_qws.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QKBDINTEGRITY_QWS_H +#define QKBDINTEGRITY_QWS_H + +#include <QtGui/qkbd_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_KEYBOARD + +#ifndef QT_NO_QWS_KBD_INTEGRITY + +class QSocketNotifier; +class QWSIntKbPrivate; + +class QWSIntKeyboardHandler : public QWSKeyboardHandler +{ +public: + explicit QWSIntKeyboardHandler(const QString&); + virtual ~QWSIntKeyboardHandler(); + +//protected: +// virtual void processKeyEvent(int keycode, bool isPress, bool autoRepeat); + +private: + QWSIntKbPrivate *d; +}; + +#endif // QT_NO_QWS_KBD_INTEGRITY + +#endif // QT_NO_QWS_KEYBOARD + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QKBDINTEGRITY_QWS_H diff --git a/src/gui/embedded/qkbdlinuxinput_qws.cpp b/src/gui/embedded/qkbdlinuxinput_qws.cpp index 376b0d0..b2e7cb3 100644 --- a/src/gui/embedded/qkbdlinuxinput_qws.cpp +++ b/src/gui/embedded/qkbdlinuxinput_qws.cpp @@ -103,6 +103,7 @@ QWSLinuxInputKbPrivate::QWSLinuxInputKbPrivate(QWSLinuxInputKeyboardHandler *h, QString dev = QLatin1String("/dev/input/event1"); int repeat_delay = -1; int repeat_rate = -1; + int grab = 0; QStringList args = device.split(QLatin1Char(':')); foreach (const QString &arg, args) { @@ -110,12 +111,15 @@ QWSLinuxInputKbPrivate::QWSLinuxInputKbPrivate(QWSLinuxInputKeyboardHandler *h, repeat_delay = arg.mid(13).toInt(); else if (arg.startsWith(QLatin1String("repeat-rate="))) repeat_rate = arg.mid(12).toInt(); + else if (arg.startsWith(QLatin1String("grab="))) + grab = arg.mid(5).toInt(); else if (arg.startsWith(QLatin1String("/dev/"))) dev = arg; } m_fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDWR, 0); if (m_fd >= 0) { + ::ioctl(m_fd, EVIOCGRAB, grab); if (repeat_delay > 0 && repeat_rate > 0) { int kbdrep[2] = { repeat_delay, repeat_rate }; ::ioctl(m_fd, EVIOCSREP, kbdrep); diff --git a/src/gui/embedded/qkbdqnx_qws.cpp b/src/gui/embedded/qkbdqnx_qws.cpp index 5a8118d..ad76446 100644 --- a/src/gui/embedded/qkbdqnx_qws.cpp +++ b/src/gui/embedded/qkbdqnx_qws.cpp @@ -40,16 +40,16 @@ ****************************************************************************/ #include "qkbdqnx_qws.h" -#include "QtCore/qsocketnotifier.h" + +#include "qplatformdefs.h" +#include "qsocketnotifier.h" +#include "private/qcore_unix_p.h" #include "QtCore/qdebug.h" #include <sys/dcmd_input.h> -#include <photon/keycodes.h> - -#include "qplatformdefs.h" +#include <sys/keycodes.h> #include <errno.h> - QT_BEGIN_NAMESPACE /*! @@ -72,7 +72,7 @@ QT_BEGIN_NAMESPACE Example invocation from command line: \c{/usr/photon/bin/devi-hid -Pr kbd mouse} Note that after running \c{devi-hid}, you will not be able to use the local - shell anymore. It is suggested to run the command in a shell scrip, that launches + shell anymore. It is suggested to run the command in a shell script, that launches a Qt application after invocation of \c{devi-hid}. To make \l{Qt for Embedded Linux} explicitly choose the qnx keyboard @@ -100,15 +100,13 @@ QWSQnxKeyboardHandler::QWSQnxKeyboardHandler(const QString &device) QT_OPEN_RDONLY); if (keyboardFD == -1) { qErrnoWarning(errno, "QWSQnxKeyboardHandler: Unable to open device"); - return; - } - - // create a socket notifier so we'll wake up whenever keyboard input is detected. - QSocketNotifier *notifier = new QSocketNotifier(keyboardFD, QSocketNotifier::Read, this); - connect(notifier, SIGNAL(activated(int)), SLOT(socketActivated())); - - qDebug() << "QWSQnxKeyboardHandler: connected."; + } else { + // create a socket notifier so we'll wake up whenever keyboard input is detected. + QSocketNotifier *notifier = new QSocketNotifier(keyboardFD, QSocketNotifier::Read, this); + connect(notifier, SIGNAL(activated(int)), SLOT(socketActivated())); + qDebug("QWSQnxKeyboardHandler: connected."); + } } /*! @@ -116,7 +114,16 @@ QWSQnxKeyboardHandler::QWSQnxKeyboardHandler(const QString &device) */ QWSQnxKeyboardHandler::~QWSQnxKeyboardHandler() { - QT_CLOSE(keyboardFD); + if (keyboardFD != -1) + QT_CLOSE(keyboardFD); +} + +// similar to PhKeyToMb +static inline bool key_sym_displayable(unsigned long sym) +{ + if (sym >= 0xF000) + return sym >= 0xF100 && (sizeof(wchar_t) > 2 || sym < 0x10000); + return (sym & ~0x9F) != 0; // exclude 0...0x1F and 0x80...0x9F } /*! \internal @@ -136,6 +143,11 @@ void QWSQnxKeyboardHandler::socketActivated() // the bytes read must be the size of a keyboard packet Q_ASSERT(bytesRead == sizeof(_keyboard_packet)); + if (packet.data.flags & KEY_SYM_VALID_EX) + packet.data.flags |= KEY_SYM_VALID; + else if (!(packet.data.flags & (KEY_SYM_VALID | KEY_CAP_VALID))) + return; + #if 0 qDebug() << "keyboard got scancode" << hex << packet.data.modifiers @@ -145,86 +157,157 @@ void QWSQnxKeyboardHandler::socketActivated() << packet.data.key_scan; #endif - // QNX is nice enough to translate the raw keyboard data into a QNX data structure + // QNX is nice enough to translate the raw keyboard data into generic format for us. // Now we just have to translate it into a format Qt understands. - // figure out whether it's a press - bool isPress = packet.data.key_cap & KEY_DOWN; - // figure out whether the key is still pressed and the key event is repeated - bool isRepeat = packet.data.key_cap & KEY_REPEAT; - - Qt::Key key = Qt::Key_unknown; - int unicode = 0xffff; - - // TODO - this switch is not complete! - switch (packet.data.key_scan) { - case KEYCODE_SPACE: key = Qt::Key_Space; unicode = 0x20; break; - case KEYCODE_F1: key = Qt::Key_F1; break; - case KEYCODE_F2: key = Qt::Key_F2; break; - case KEYCODE_F3: key = Qt::Key_F3; break; - case KEYCODE_F4: key = Qt::Key_F4; break; - case KEYCODE_F5: key = Qt::Key_F5; break; - case KEYCODE_F6: key = Qt::Key_F6; break; - case KEYCODE_F7: key = Qt::Key_F7; break; - case KEYCODE_F8: key = Qt::Key_F8; break; - case KEYCODE_F9: key = Qt::Key_F9; break; - case KEYCODE_F10: key = Qt::Key_F10; break; - case KEYCODE_F11: key = Qt::Key_F11; break; - case KEYCODE_F12: key = Qt::Key_F12; break; - case KEYCODE_BACKSPACE: key = Qt::Key_Backspace; break; - case KEYCODE_TAB: key = Qt::Key_Tab; break; - case KEYCODE_RETURN: key = Qt::Key_Return; break; - case KEYCODE_KP_ENTER: key = Qt::Key_Enter; break; - case KEYCODE_UP: - case KEYCODE_KP_UP: - key = Qt::Key_Up; break; - case KEYCODE_DOWN: - case KEYCODE_KP_DOWN: - key = Qt::Key_Down; break; - case KEYCODE_LEFT: - case KEYCODE_KP_LEFT: - key = Qt::Key_Left; break; - case KEYCODE_RIGHT: - case KEYCODE_KP_RIGHT: - key = Qt::Key_Right; break; - case KEYCODE_HOME: - case KEYCODE_KP_HOME: - key = Qt::Key_Home; break; - case KEYCODE_END: - case KEYCODE_KP_END: - key = Qt::Key_End; break; - case KEYCODE_PG_UP: - case KEYCODE_KP_PG_UP: - key = Qt::Key_PageUp; break; - case KEYCODE_PG_DOWN: - case KEYCODE_KP_PG_DOWN: - key = Qt::Key_PageDown; break; - case KEYCODE_INSERT: - case KEYCODE_KP_INSERT: - key = Qt::Key_Insert; break; - case KEYCODE_DELETE: - case KEYCODE_KP_DELETE: - key = Qt::Key_Delete; break; - case KEYCODE_ESCAPE: - key = Qt::Key_Escape; break; - default: // none of the above, try the key_scan directly - unicode = packet.data.key_scan; - break; - } - // figure out the modifiers that are currently pressed Qt::KeyboardModifiers modifiers = Qt::NoModifier; - if (packet.data.flags & KEYMOD_SHIFT) + if (packet.data.modifiers & KEYMOD_SHIFT) modifiers |= Qt::ShiftModifier; - if (packet.data.flags & KEYMOD_CTRL) + if (packet.data.modifiers & KEYMOD_CTRL) modifiers |= Qt::ControlModifier; - if (packet.data.flags & KEYMOD_ALT) + if (packet.data.modifiers & KEYMOD_ALT) modifiers |= Qt::AltModifier; + if (packet.data.modifiers & KEYMOD_NUM_LOCK) + modifiers |= Qt::KeypadModifier; +#if 0 + // special case for AltGr + if (packet.data.modifiers & KEYMOD_ALTGR) + key = Qt::Key_AltGr; +#endif + + // figure out whether it's a press + bool isPress = packet.data.flags & KEY_DOWN; + // figure out whether the key is still pressed and the key event is repeated + bool isRepeat = packet.data.flags & KEY_REPEAT; + + int key = Qt::Key_unknown; + int unicode = 0; + + if (((packet.data.flags & KEY_SYM_VALID) && key_sym_displayable(unicode = packet.data.key_sym)) + || ((packet.data.flags & KEY_CAP_VALID) && key_sym_displayable(unicode = packet.data.key_cap))) { + if (unicode <= 0x0ff) { + if (unicode >= 'a' && unicode <= 'z') + key = Qt::Key_A + unicode - 'a'; + else + key = unicode; + } + // Ctrl<something> or Alt<something> is not a displayable character + if (modifiers & (Qt::ControlModifier | Qt::AltModifier)) + unicode = 0; + } else { + unicode = 0; + + unsigned long sym = 0; + if (packet.data.flags & KEY_SYM_VALID) + sym = packet.data.key_sym; + else if (packet.data.flags & KEY_CAP_VALID) + sym = packet.data.key_cap; - // if the unicode value is not ascii, we ignore it. - // TODO - do a complete mapping between all QNX scan codes and Qt codes - if (unicode != 0xffff && !isascii(unicode)) - return; // unprintable character + switch (sym) { + case KEYCODE_ESCAPE: key = Qt::Key_Escape; unicode = 27; break; + case KEYCODE_TAB: key = Qt::Key_Tab; unicode = 9; break; + case KEYCODE_BACK_TAB: key = Qt::Key_Backtab; break; + case KEYCODE_BACKSPACE: key = Qt::Key_Backspace; unicode = 127; break; + case KEYCODE_RETURN: key = Qt::Key_Return; break; + case KEYCODE_KP_ENTER: key = Qt::Key_Enter; break; + case KEYCODE_INSERT: + case KEYCODE_KP_INSERT: + key = Qt::Key_Insert; break; + case KEYCODE_KP_DELETE: + if (modifiers & Qt::KeypadModifier) { + key = Qt::Key_Comma; + break; + } + // fall through + case KEYCODE_DELETE: + key = Qt::Key_Delete; break; + case KEYCODE_PAUSE: + case KEYCODE_BREAK: + if (modifiers & (Qt::ControlModifier | Qt::AltModifier)) + return; // sometimes occurs at the middle of a key sequence + key = Qt::Key_Pause; break; + case KEYCODE_PRINT: + if (modifiers & (Qt::ControlModifier | Qt::AltModifier)) + return; // sometimes occurs at the middle of a key sequence + key = Qt::Key_Print; break; + case KEYCODE_SYSREQ: + key = Qt::Key_SysReq; break; + case KEYCODE_HOME: + case KEYCODE_KP_HOME: + key = Qt::Key_Home; break; + case KEYCODE_END: + case KEYCODE_KP_END: + key = Qt::Key_End; break; + case KEYCODE_LEFT: + case KEYCODE_KP_LEFT: + key = Qt::Key_Left; break; + case KEYCODE_UP: + case KEYCODE_KP_UP: + key = Qt::Key_Up; break; + case KEYCODE_RIGHT: + case KEYCODE_KP_RIGHT: + key = Qt::Key_Right; break; + case KEYCODE_DOWN: + case KEYCODE_KP_DOWN: + key = Qt::Key_Down; break; + case KEYCODE_PG_UP: + case KEYCODE_KP_PG_UP: + key = Qt::Key_PageUp; break; + case KEYCODE_PG_DOWN: + case KEYCODE_KP_PG_DOWN: + key = Qt::Key_PageDown; break; + + case KEYCODE_LEFT_SHIFT: + case KEYCODE_RIGHT_SHIFT: + key = Qt::Key_Shift; break; + case KEYCODE_LEFT_CTRL: + case KEYCODE_RIGHT_CTRL: + key = Qt::Key_Control; break; + case KEYCODE_LEFT_ALT: + case KEYCODE_RIGHT_ALT: + key = Qt::Key_Alt; break; + case KEYCODE_CAPS_LOCK: + key = Qt::Key_CapsLock; break; + case KEYCODE_NUM_LOCK: + key = Qt::Key_NumLock; break; + case KEYCODE_SCROLL_LOCK: + key = Qt::Key_ScrollLock; break; + + case KEYCODE_F1: + case KEYCODE_F2: + case KEYCODE_F3: + case KEYCODE_F4: + case KEYCODE_F5: + case KEYCODE_F6: + case KEYCODE_F7: + case KEYCODE_F8: + case KEYCODE_F9: + case KEYCODE_F10: + case KEYCODE_F11: + case KEYCODE_F12: + key = Qt::Key_F1 + sym - KEYCODE_F1; break; + + case KEYCODE_MENU: key = Qt::Key_Menu; break; + case KEYCODE_LEFT_HYPER: key = Qt::Key_Hyper_L; break; + case KEYCODE_RIGHT_HYPER: key = Qt::Key_Hyper_R; break; + + case KEYCODE_KP_PLUS: key = Qt::Key_Plus; break; + case KEYCODE_KP_MINUS: key = Qt::Key_Minus; break; + case KEYCODE_KP_MULTIPLY: key = Qt::Key_multiply; break; + case KEYCODE_KP_DIVIDE: key = Qt::Key_Slash; break; + case KEYCODE_KP_FIVE: + if (!(modifiers & Qt::KeypadModifier)) + key = Qt::Key_5; + break; + + default: // none of the above + break; + } + } + + if (key == Qt::Key_unknown && unicode == 0) + return; // call processKeyEvent. This is where all the magic happens to insert a // key event into Qt's event loop. diff --git a/src/gui/embedded/qkbdum_qws.cpp b/src/gui/embedded/qkbdum_qws.cpp index 4fbe03e..97561b5 100644 --- a/src/gui/embedded/qkbdum_qws.cpp +++ b/src/gui/embedded/qkbdum_qws.cpp @@ -40,7 +40,6 @@ ****************************************************************************/ #include "qkbdum_qws.h" -#include "qvfbhdr.h" #if !defined(QT_NO_QWS_KEYBOARD) && !defined(QT_NO_QWS_KBD_UM) @@ -55,6 +54,7 @@ #include <qwindowsystem_qws.h> #include <qsocketnotifier.h> #include "qplatformdefs.h" +#include "qvfbhdr.h" QT_BEGIN_NAMESPACE diff --git a/src/gui/embedded/qlock.cpp b/src/gui/embedded/qlock.cpp index 09d4ea1..eaad15c 100644 --- a/src/gui/embedded/qlock.cpp +++ b/src/gui/embedded/qlock.cpp @@ -41,7 +41,6 @@ #include "qlock_p.h" - #ifdef QT_NO_QWS_MULTIPROCESS QT_BEGIN_NAMESPACE @@ -82,38 +81,28 @@ QT_END_NAMESPACE #else // QT_NO_QWS_MULTIPROCESS +#if defined(Q_OS_DARWIN) +# define QT_NO_SEMAPHORE +#endif + #include "qwssignalhandler_p.h" #include <unistd.h> #include <sys/types.h> -#if defined(Q_OS_DARWIN) -# define Q_NO_SEMAPHORE -# include <sys/stat.h> -# include <sys/file.h> -#else // Q_OS_DARWIN -# include <sys/sem.h> -# if (defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) && !defined(QT_LINUXBASE)) \ - || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD) \ - || defined(Q_OS_BSDI) - /* union semun is defined by including <sys/sem.h> */ -# else -/* according to X/OPEN we have to define it ourselves */ -union semun { - int val; /* value for SETVAL */ - struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ - unsigned short *array; /* array for GETALL, SETALL */ -}; -# endif -#endif // Q_OS_DARWIN #include <sys/ipc.h> +#if defined(QT_NO_SEMAPHORE) +# include <sys/stat.h> +# include <sys/file.h> +#elif !defined(QT_POSIX_IPC) +# include <sys/sem.h> +#else +# include <semaphore.h> +#endif #include <string.h> #include <errno.h> -#include <qdebug.h> -#include <signal.h> #include <private/qcore_unix_p.h> // overrides QT_OPEN - QT_BEGIN_NAMESPACE #define MAX_LOCKS 200 // maximum simultaneous read locks @@ -121,17 +110,24 @@ QT_BEGIN_NAMESPACE class QLockData { public: -#ifdef Q_NO_SEMAPHORE +#if defined(QT_NO_SEMAPHORE) || defined(QT_POSIX_IPC) QByteArray file; -#endif // Q_NO_SEMAPHORE +#endif +#if !defined(QT_POSIX_IPC) int id; +#else + sem_t *id; // Read mode resource counter + sem_t *rsem; // Read mode lock + sem_t *wsem; // Write mode lock +#endif int count; bool owned; }; + /*! \class QLock - \brief The QLock class is a wrapper for a System V shared semaphore. + \brief The QLock class is a wrapper for a system shared semaphore. \ingroup qws @@ -149,8 +145,6 @@ public: */ /*! - \fn QLock::QLock(const QString &filename, char id, bool create) - Creates a lock. \a filename is the file path of the Unix-domain socket the \l{Qt for Embedded Linux} client is using. \a id is the name of the particular lock to be created on that socket. If \a create is true @@ -158,76 +152,120 @@ public: create is false the lock should exist already (as the Qt for Embedded Linux client expects). */ - QLock::QLock(const QString &filename, char id, bool create) { data = new QLockData; data->count = 0; -#ifdef Q_NO_SEMAPHORE - data->file = QString(filename+id).toLocal8Bit().constData(); - for(int x = 0; x < 2; x++) { - data->id = QT_OPEN(data->file, O_RDWR | (x ? O_CREAT : 0), S_IRWXU); - if(data->id != -1 || !create) { +#if defined(QT_NO_SEMAPHORE) + data->file = filename.toLocal8Bit() + id; + for (int x = 0; x < 2; ++x) { + data->id = QT_OPEN(data->file.constData(), O_RDWR | (x ? O_CREAT : 0), S_IRWXU); + if (data->id != -1 || !create) { data->owned = x; break; } } -#else +#elif !defined(QT_POSIX_IPC) key_t semkey = ftok(filename.toLocal8Bit().constData(), id); - data->id = semget(semkey,0,0); + data->id = semget(semkey, 0, 0); data->owned = create; if (create) { - semun arg; arg.val = 0; + qt_semun arg; + arg.val = 0; if (data->id != -1) - semctl(data->id,0,IPC_RMID,arg); - data->id = semget(semkey,1,IPC_CREAT|0600); + semctl(data->id, 0, IPC_RMID, arg); + data->id = semget(semkey, 1, IPC_CREAT | 0600); arg.val = MAX_LOCKS; - semctl(data->id,0,SETVAL,arg); + semctl(data->id, 0, SETVAL, arg); + } +#else + data->file = filename.toLocal8Bit() + id; + data->owned = create; - QWSSignalHandler::instance()->addSemaphore(data->id); + char ids[3] = { 'c', 'r', 'w' }; + sem_t **sems[3] = { &data->id, &data->rsem, &data->wsem }; + unsigned short initialValues[3] = { MAX_LOCKS, 1, 1 }; + for (int i = 0; i < 3; ++i) { + QByteArray file = data->file + ids[i]; + do { + *sems[i] = sem_open(file.constData(), 0, 0666, 0); + } while (*sems[i] == SEM_FAILED && errno == EINTR); + if (create) { + if (*sems[i] != SEM_FAILED) { + sem_close(*sems[i]); + sem_unlink(file.constData()); + } + do { + *sems[i] = sem_open(file.constData(), O_CREAT, 0666, initialValues[i]); + } while (*sems[i] == SEM_FAILED && errno == EINTR); + } } #endif - if (data->id == -1) { - int eno = errno; - qWarning("Cannot %s semaphore %s '%c'", (create ? "create" : "get"), - qPrintable(filename), id); - qDebug() << "Error" << eno << strerror(eno); + if (!isValid()) { + qWarning("QLock::QLock: Cannot %s semaphore %s '%c' (%d, %s)", + (create ? "create" : "get"), qPrintable(filename), id, + errno, strerror(errno)); } + +#ifndef QT_NO_QWS_SIGNALHANDLER + QWSSignalHandler::instance()->addLock(this); +#endif } /*! - \fn QLock::~QLock() - Destroys a lock */ - QLock::~QLock() { - if (locked()) +#ifndef QT_NO_QWS_SIGNALHANDLER + QWSSignalHandler::instance()->removeLock(this); +#endif + + while (locked()) unlock(); -#ifdef Q_NO_SEMAPHORE - if(isValid()) { + +#if defined(QT_NO_SEMAPHORE) + if (isValid()) QT_CLOSE(data->id); - if(data->owned) - unlink(data->file); - } +#elif defined(QT_POSIX_IPC) + if (data->id != SEM_FAILED) + sem_close(data->id); + if (data->rsem != SEM_FAILED) + sem_close(data->rsem); + if (data->wsem != SEM_FAILED) + sem_close(data->wsem); +#endif + + if (data->owned) { +#if defined(QT_NO_SEMAPHORE) + unlink(data->file.constData()); +#elif !defined(QT_POSIX_IPC) + qt_semun semval; + semval.val = 0; + semctl(data->id, 0, IPC_RMID, semval); #else - if(data->owned) - QWSSignalHandler::instance()->removeSemaphore(data->id); + char ids[3] = { 'c', 'r', 'w' }; + for (int i = 0; i < 3; ++i) { + QByteArray file = data->file + ids[i]; + sem_unlink(file.constData()); + } #endif + } delete data; + data = 0; } /*! - \fn bool QLock::isValid() const - Returns true if the lock constructor was successful; returns false if the lock could not be created or was not available to connect to. */ - bool QLock::isValid() const { - return (data->id != -1); +#if !defined(QT_POSIX_IPC) + return data && data->id != -1; +#else + return data && data->id != SEM_FAILED && data->rsem != SEM_FAILED && data->wsem != SEM_FAILED; +#endif } /*! @@ -241,96 +279,117 @@ bool QLock::isValid() const will only be unlocked after a corresponding number of unlock() calls. */ - void QLock::lock(Type t) { + if (!isValid()) + return; + if (!data->count) { -#ifdef Q_NO_SEMAPHORE - int op = LOCK_SH; - if(t == Write) - op = LOCK_EX; - for(int rv=1; rv;) { - rv = flock(data->id, op); - if (rv == -1 && errno != EINTR) - qDebug("Semop lock failure %s",strerror(errno)); - } -#else + type = t; + + int rv; +#if defined(QT_NO_SEMAPHORE) + int op = type == Write ? LOCK_EX : LOCK_SH; + + EINTR_LOOP(rv, flock(data->id, op)); +#elif !defined(QT_POSIX_IPC) sembuf sops; sops.sem_num = 0; + sops.sem_op = type == Write ? -MAX_LOCKS : -1; sops.sem_flg = SEM_UNDO; - if (t == Write) { - sops.sem_op = -MAX_LOCKS; - type = Write; + EINTR_LOOP(rv, semop(data->id, &sops, 1)); +#else + if (type == Write) { + EINTR_LOOP(rv, sem_wait(data->rsem)); + if (rv != -1) { + EINTR_LOOP(rv, sem_wait(data->wsem)); + if (rv == -1) + sem_post(data->rsem); + } } else { - sops.sem_op = -1; - type = Read; + EINTR_LOOP(rv, sem_wait(data->wsem)); + if (rv != -1) { + EINTR_LOOP(rv, sem_trywait(data->rsem)); + if (rv != -1 || errno == EAGAIN) { + EINTR_LOOP(rv, sem_wait(data->id)); + if (rv == -1) { + int semval; + sem_getvalue(data->id, &semval); + if (semval == MAX_LOCKS) + sem_post(data->rsem); + } + } + rv = sem_post(data->wsem); + } } - - int rv; - do { - rv = semop(data->id,&sops,1); - if (rv == -1 && errno != EINTR) - qDebug("Semop lock failure %s",strerror(errno)); - } while (rv == -1 && errno == EINTR); #endif + if (rv == -1) { + qDebug("QLock::lock(): %s", strerror(errno)); + return; + } + } else if (type == Read && t == Write) { + qDebug("QLock::lock(): Attempt to lock for write while locked for read"); } data->count++; } /*! - \fn void QLock::unlock() - Unlocks the semaphore. If other processes were blocking waiting to lock() the semaphore, one of them will wake up and succeed in - lock()ing. + locking. */ - void QLock::unlock() { - if(data->count) { + if (!isValid()) + return; + + if (data->count > 0) { data->count--; - if(!data->count) { -#ifdef Q_NO_SEMAPHORE - for(int rv=1; rv;) { - rv = flock(data->id, LOCK_UN); - if (rv == -1 && errno != EINTR) - qDebug("Semop lock failure %s",strerror(errno)); - } -#else + if (!data->count) { + int rv; +#if defined(QT_NO_SEMAPHORE) + EINTR_LOOP(rv, flock(data->id, LOCK_UN)); +#elif !defined(QT_POSIX_IPC) sembuf sops; sops.sem_num = 0; - sops.sem_op = 1; + sops.sem_op = type == Write ? MAX_LOCKS : 1; sops.sem_flg = SEM_UNDO; - if (type == Write) - sops.sem_op = MAX_LOCKS; - int rv; - do { - rv = semop(data->id,&sops,1); - if (rv == -1 && errno != EINTR) - qDebug("Semop unlock failure %s",strerror(errno)); - } while (rv == -1 && errno == EINTR); + EINTR_LOOP(rv, semop(data->id, &sops, 1)); +#else + if (type == Write) { + sem_post(data->wsem); + rv = sem_post(data->rsem); + } else { + EINTR_LOOP(rv, sem_wait(data->wsem)); + if (rv != -1) { + sem_post(data->id); + int semval; + sem_getvalue(data->id, &semval); + if (semval == MAX_LOCKS) + sem_post(data->rsem); + rv = sem_post(data->wsem); + } + } #endif + if (rv == -1) + qDebug("QLock::unlock(): %s", strerror(errno)); } } else { - qDebug("Unlock without corresponding lock"); + qDebug("QLock::unlock(): Unlock without corresponding lock"); } } /*! - \fn bool QLock::locked() const - Returns true if the lock is currently held by the current process; otherwise returns false. */ - bool QLock::locked() const { - return (data->count > 0); + return isValid() && data->count > 0; } QT_END_NAMESPACE #endif // QT_NO_QWS_MULTIPROCESS - diff --git a/src/gui/embedded/qmousedriverfactory_qws.cpp b/src/gui/embedded/qmousedriverfactory_qws.cpp index 84fa951..d995bdf 100644 --- a/src/gui/embedded/qmousedriverfactory_qws.cpp +++ b/src/gui/embedded/qmousedriverfactory_qws.cpp @@ -48,6 +48,7 @@ #include "qmousevfb_qws.h" #include "qmousetslib_qws.h" #include "qmouseqnx_qws.h" +#include "qmouseintegrity_qws.h" #include <stdlib.h> #include "private/qfactoryloader_p.h" #include "qmousedriverplugin_qws.h" @@ -107,6 +108,10 @@ QWSMouseHandler *QMouseDriverFactory::create(const QString& key, const QString & if (driver == QLatin1String("qnx") || driver.isEmpty()) return new QQnxMouseHandler(key, device); #endif +#if defined(Q_OS_INTEGRITY) && !defined(QT_NO_MOUSE_INTEGRITY) + if (driver == QLatin1String("integrity") || driver.isEmpty()) + return new QIntMouseHandler(key, device); +#endif #ifndef QT_NO_QWS_MOUSE_LINUXTP if (driver == QLatin1String("linuxtp") || driver.isEmpty()) return new QWSLinuxTPMouseHandler(key, device); @@ -157,6 +162,9 @@ QStringList QMouseDriverFactory::keys() #if defined(Q_OS_QNX) && !defined(QT_NO_QWS_MOUSE_QNX) list << QLatin1String("QNX"); #endif +#if defined(Q_OS_INTEGRITY) && !defined(QT_NO_QWS_MOUSE_INTEGRITY) + list << QLatin1String("INTEGRITY"); +#endif #ifndef QT_NO_QWS_MOUSE_LINUXTP list << QLatin1String("LinuxTP"); #endif diff --git a/src/gui/embedded/qmouseintegrity_qws.cpp b/src/gui/embedded/qmouseintegrity_qws.cpp new file mode 100644 index 0000000..c4ce603 --- /dev/null +++ b/src/gui/embedded/qmouseintegrity_qws.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_QWS_MOUSE_INTEGRITY + +#include "qmouseintegrity_qws.h" +#include <qwindowsystem_qws.h> +#include <qapplication.h> +#include <qtimer.h> +#include <qthread.h> + +#include <INTEGRITY.h> + + +typedef Address MOUSEHandler; +typedef struct MOUSEMessageStruct +{ + Value x; + Value y; + Value z; + Value buttons; +} MOUSEMessage; + +static Error MOUSE_Init(MOUSEHandler *handler, Boolean *isabsolute); +static Error MOUSE_SynchronousGetPosition(MOUSEHandler handler, MOUSEMessage *msg, + Boolean absolute); +static Error MOUSE_ShouldFilter(MOUSEHandler handler, Boolean *filter); + +QT_BEGIN_NAMESPACE + +class QIntMouseListenThread; + +class QIntMousePrivate : public QObject +{ + Q_OBJECT + friend class QIntMouseListenTaskThread; +Q_SIGNALS: + void mouseDataAvailable(int x, int y, int buttons); +public: + QIntMousePrivate(QIntMouseHandler *handler); + ~QIntMousePrivate(); + void dataReady(int x, int y, int buttons) { emit mouseDataAvailable(x, y, buttons); } + bool calibrated; + bool waitforread; + bool suspended; + QIntMouseListenThread *mousethread; + +private: + QIntMouseHandler *handler; +}; + +class QIntMouseListenThread : public QThread +{ +protected: + QIntMousePrivate *imp; + bool loop; +public: + QIntMouseListenThread(QIntMousePrivate *im) : QThread(), imp(im) {}; + ~QIntMouseListenThread() {}; + void run(); + void stoploop() { loop = false; }; +}; + + +QIntMouseHandler::QIntMouseHandler(const QString &driver, const QString &device) + : QObject(), QWSCalibratedMouseHandler(driver, device) +{ + QPoint test(1,1); + d = new QIntMousePrivate(this); + connect(d, SIGNAL(mouseDataAvailable(int, int, int)), this, SLOT(readMouseData(int, int, int))); + + d->calibrated = (test != transform(test)); + + d->mousethread->start(); +} + +QIntMouseHandler::~QIntMouseHandler() +{ + disconnect(d, SIGNAL(mouseDataAvailable(int, int, int)), this, SLOT(readMouseData(int, int, int))); + delete d; +} + +void QIntMouseHandler::resume() +{ + d->suspended = true; +} + +void QIntMouseHandler::suspend() +{ + d->suspended = false; +} + +void QIntMouseHandler::readMouseData(int x, int y, int buttons) +{ + d->waitforread = false; + if (d->suspended) + return; + if (d->calibrated) { + sendFiltered(QPoint(x, y), buttons); + } else { + QPoint pos; + pos = transform(QPoint(x, y)); + limitToScreen(pos); + mouseChanged(pos, buttons, 0); + } +} + +void QIntMouseHandler::clearCalibration() +{ + QWSCalibratedMouseHandler::clearCalibration(); +} + +void QIntMouseHandler::calibrate(const QWSPointerCalibrationData *data) +{ + QWSCalibratedMouseHandler::calibrate(data); +} + +void QIntMouseListenThread::run(void) +{ + MOUSEHandler handler; + MOUSEMessage msg; + Boolean filter; + Boolean isabsolute; + loop = true; + CheckSuccess(MOUSE_Init(&handler, &isabsolute)); + CheckSuccess(MOUSE_ShouldFilter(handler, &filter)); + if (!filter) + imp->calibrated = false; + imp->waitforread = false; + do { + MOUSE_SynchronousGetPosition(handler, &msg, isabsolute); + imp->dataReady(msg.x, msg.y, msg.buttons); + } while (loop); + QThread::exit(0); +} + +QIntMousePrivate::QIntMousePrivate(QIntMouseHandler *handler) + : QObject() +{ + this->handler = handler; + suspended = false; + mousethread = new QIntMouseListenThread(this); +} + +QIntMousePrivate::~QIntMousePrivate() +{ + mousethread->stoploop(); + mousethread->wait(); + delete mousethread; +} + +QT_END_NAMESPACE + +#include "qmouseintegrity_qws.moc" + +typedef struct USBMouseStruct +{ + Connection mouseconn; + Buffer mousemsg[2]; + Value x; + Value y; +} USBMouse; + +USBMouse mousedev; + +Error MOUSE_Init(MOUSEHandler *handler, Boolean *isabsolute) +{ + Error E; + bool loop = true; + memset((void*)&mousedev, 0, sizeof(USBMouse)); + mousedev.mousemsg[0].BufferType = DataImmediate; + mousedev.mousemsg[1].BufferType = DataImmediate | LastBuffer; + do { + E = RequestResource((Object*)&mousedev.mouseconn, + "MouseClient", "!systempassword"); + if (E == Success) { + *isabsolute = true; + loop = false; + } else { + E = RequestResource((Object*)&mousedev.mouseconn, + "USBMouseClient", "!systempassword"); + if (E == Success) { + *isabsolute = false; + loop = false; + } + } + if (loop) + sleep(1); + } while (loop); + *handler = (MOUSEHandler)&mousedev; + return Success; +} + +Error MOUSE_SynchronousGetPosition(MOUSEHandler handler, MOUSEMessage *msg, + Boolean isabsolute) +{ + signed long x; + signed long y; + USBMouse *mdev = (USBMouse *)handler; + mdev->mousemsg[0].Transferred = 0; + mdev->mousemsg[1].Transferred = 0; + SynchronousReceive(mdev->mouseconn, mdev->mousemsg); + if (isabsolute) { + x = (signed long)mdev->mousemsg[0].Length; + y = (signed long)mdev->mousemsg[1].TheAddress; + } else { + x = mdev->x + (signed long)mdev->mousemsg[0].Length; + y = mdev->y + (signed long)mdev->mousemsg[1].TheAddress; + } + if (x < 0) + mdev->x = 0; + else + mdev->x = x; + if (y < 0) + mdev->y = 0; + else + mdev->y = y; + msg->x = mdev->x; + msg->y = mdev->y; + msg->buttons = mdev->mousemsg[0].TheAddress; + return Success; +} + +Error MOUSE_ShouldFilter(MOUSEHandler handler, Boolean *filter) +{ + if (filter == NULL) + return Failure; + *filter = false; + return Success; +} + +#endif // QT_NO_QWS_MOUSE_INTEGRITY + diff --git a/src/gui/embedded/qmouseintegrity_qws.h b/src/gui/embedded/qmouseintegrity_qws.h new file mode 100644 index 0000000..5f0c2aa --- /dev/null +++ b/src/gui/embedded/qmouseintegrity_qws.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMOUSEINTEGRITY_QWS_H +#define QMOUSEINTEGRITY_QWS_H + +#include <QtGui/qmouse_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_MOUSE_INTEGRITY + +class QSocketNotifier; +class QIntMousePrivate; + +class QIntMouseHandler : public QObject, public QWSCalibratedMouseHandler { + Q_OBJECT +public: + QIntMouseHandler(const QString &driver = QString(), + const QString &device = QString()); + ~QIntMouseHandler(); + + void resume(); + void suspend(); + + void calibrate(const QWSPointerCalibrationData *data); + void clearCalibration(); + +private: + QIntMousePrivate *d; +private Q_SLOTS: + void readMouseData(int x, int y, int buttons); +}; +#endif // QT_NO_QWS_MOUSE_INTEGRITY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMOUSEINTEGRITY_QWS_H diff --git a/src/gui/embedded/qmouselinuxinput_qws.cpp b/src/gui/embedded/qmouselinuxinput_qws.cpp index efcf6d4..5b4f664 100644 --- a/src/gui/embedded/qmouselinuxinput_qws.cpp +++ b/src/gui/embedded/qmouselinuxinput_qws.cpp @@ -43,6 +43,7 @@ #include <QScreen> #include <QSocketNotifier> +#include <QStringList> #include <qplatformdefs.h> #include <private/qcore_unix_p.h> // overrides QT_OPEN @@ -101,11 +102,19 @@ QWSLinuxInputMousePrivate::QWSLinuxInputMousePrivate(QWSLinuxInputMouseHandler * setObjectName(QLatin1String("LinuxInputSubsystem Mouse Handler")); QString dev = QLatin1String("/dev/input/event0"); - if (device.startsWith(QLatin1String("/dev/"))) - dev = device; + int grab = 0; + + QStringList args = device.split(QLatin1Char(':')); + foreach (const QString &arg, args) { + if (arg.startsWith(QLatin1String("grab="))) + grab = arg.mid(5).toInt(); + else if (arg.startsWith(QLatin1String("/dev/"))) + dev = arg; + } m_fd = QT_OPEN(dev.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0); if (m_fd >= 0) { + ::ioctl(m_fd, EVIOCGRAB, grab); m_notify = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); connect(m_notify, SIGNAL(activated(int)), this, SLOT(readMouseData())); } else { @@ -135,19 +144,21 @@ void QWSLinuxInputMousePrivate::readMouseData() int n = 0; forever { - n = QT_READ(m_fd, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n); - - if (n == 0) { + int bytesRead = QT_READ(m_fd, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n); + if (bytesRead == 0) { qWarning("Got EOF from the input device."); return; - } else if (n < 0 && (errno != EINTR && errno != EAGAIN)) { - qWarning("Could not read from input device: %s", strerror(errno)); - return; - } else if (n % sizeof(buffer[0]) == 0) { + } + if (bytesRead == -1) { + if (errno != EAGAIN) + qWarning("Could not read from input device: %s", strerror(errno)); break; } - } + n += bytesRead; + if (n % sizeof(buffer[0]) == 0) + break; + } n /= sizeof(buffer[0]); for (int i = 0; i < n; ++i) { diff --git a/src/gui/embedded/qmousepc_qws.cpp b/src/gui/embedded/qmousepc_qws.cpp index 5d3b182..c22cab9 100644 --- a/src/gui/embedded/qmousepc_qws.cpp +++ b/src/gui/embedded/qmousepc_qws.cpp @@ -261,7 +261,7 @@ public: usleep(50000); QT_WRITE(fd,"@EeI!",5); usleep(10000); - static const char ibuf[] = { 246, 244 }; + static const unsigned char ibuf[] = { 246, 244 }; QT_WRITE(fd,ibuf,1); QT_WRITE(fd,ibuf+1,1); if (tcflush(fd,TCIOFLUSH) == -1) { diff --git a/src/gui/embedded/qmouseqnx_qws.cpp b/src/gui/embedded/qmouseqnx_qws.cpp index a9647c0..d0b892e 100644 --- a/src/gui/embedded/qmouseqnx_qws.cpp +++ b/src/gui/embedded/qmouseqnx_qws.cpp @@ -39,14 +39,13 @@ ** ****************************************************************************/ -#include "qplatformdefs.h" #include "qmouseqnx_qws.h" +#include "qplatformdefs.h" #include "qsocketnotifier.h" -#include "qdebug.h" +#include "private/qcore_unix_p.h" #include <sys/dcmd_input.h> - #include <errno.h> QT_BEGIN_NAMESPACE @@ -92,22 +91,28 @@ QT_BEGIN_NAMESPACE \sa QMouseDriverFactory */ -QQnxMouseHandler::QQnxMouseHandler(const QString & /*driver*/, const QString &device) +QQnxMouseHandler::QQnxMouseHandler(const QString & driver, const QString &device) + : QObject(), QWSMouseHandler(driver, device), mouseButtons(Qt::NoButton) { // open the mouse device with O_NONBLOCK so reading won't block when there's no data mouseFD = QT_OPEN(device.isEmpty() ? "/dev/devi/mouse0" : device.toLatin1().constData(), - QT_OPEN_RDONLY | O_NONBLOCK); + QT_OPEN_RDONLY | O_NONBLOCK); if (mouseFD == -1) { qErrnoWarning(errno, "QQnxMouseHandler: Unable to open mouse device"); - return; + } else { + struct _pointer_info data; + if (devctl(mouseFD, _POINTERGETINFO, &data, sizeof(data), NULL) == EOK) + absolutePositioning = (data.flags & _POINTER_FLAG_ABSOLUTE); + else + absolutePositioning = !device.isEmpty() && device.contains(QLatin1String("touch")); + + // register a socket notifier on the file descriptor so we'll wake up whenever + // there's a mouse move waiting for us. + mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read, this); + connect(mouseNotifier, SIGNAL(activated(int)), SLOT(socketActivated())); + + qDebug("QQnxMouseHandler: connected."); } - - // register a socket notifier on the file descriptor so we'll wake up whenever - // there's a mouse move waiting for us. - mouseNotifier = new QSocketNotifier(mouseFD, QSocketNotifier::Read, this); - connect(mouseNotifier, SIGNAL(activated(int)), SLOT(socketActivated())); - - qDebug() << "QQnxMouseHandler: connected."; } /*! @@ -115,7 +120,8 @@ QQnxMouseHandler::QQnxMouseHandler(const QString & /*driver*/, const QString &de */ QQnxMouseHandler::~QQnxMouseHandler() { - QT_CLOSE(mouseFD); + if (mouseFD != -1) + QT_CLOSE(mouseFD); } /*! \reimp */ @@ -140,39 +146,45 @@ void QQnxMouseHandler::suspend() */ void QQnxMouseHandler::socketActivated() { + QPoint queuedPos = mousePos; + // _mouse_packet is a QNX structure. devi-hid is nice enough to translate // the raw byte data from mouse devices into generic format for us. - _mouse_packet packet; + struct _mouse_packet buffer[32]; + int n = 0; - int iteration = 0; - - // read mouse events in batches of 10. Since we're getting quite a lot - // of mouse events, it's better to do them in batches than to return to the - // event loop every time. - do { - int bytesRead = QT_READ(mouseFD, &packet, sizeof(packet)); + forever { + int bytesRead = QT_READ(mouseFD, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n); if (bytesRead == -1) { // EAGAIN means that there are no more mouse events to read if (errno != EAGAIN) - qErrnoWarning(errno, "QQnxMouseHandler: Unable to read from socket"); - return; + qErrnoWarning(errno, "QQnxMouseHandler: Could not read from input device"); + break; } - // bytes read should always be equal to the size of a packet. - Q_ASSERT(bytesRead == sizeof(packet)); + n += bytesRead; + if (n % sizeof(buffer[0]) == 0) + break; + } + n /= sizeof(buffer[0]); - // translate the coordinates from the QNX data structure to Qt coordinates - // note the swapped y axis - QPoint pos = mousePos; - pos += QPoint(packet.dx, -packet.dy); + for (int i = 0; i < n; ++i) { + const struct _mouse_packet &packet = buffer[i]; - // QNX only tells us relative mouse movements, not absolute ones, so limit the - // cursor position manually to the screen - limitToScreen(pos); + // translate the coordinates from the QNX data structure to the Qt coordinates + if (absolutePositioning) { + queuedPos = QPoint(packet.dx, packet.dy); + } else { + // note the swapped y axis + queuedPos += QPoint(packet.dx, -packet.dy); + + // QNX only tells us relative mouse movements, not absolute ones, so + // limit the cursor position manually to the screen + limitToScreen(queuedPos); + } // translate the QNX mouse button bitmask to Qt buttons int buttons = Qt::NoButton; - if (packet.hdr.buttons & _POINTER_BUTTON_LEFT) buttons |= Qt::LeftButton; if (packet.hdr.buttons & _POINTER_BUTTON_MIDDLE) @@ -180,11 +192,17 @@ void QQnxMouseHandler::socketActivated() if (packet.hdr.buttons & _POINTER_BUTTON_RIGHT) buttons |= Qt::RightButton; - // call mouseChanged() - this does all the magic to actually move the on-screen - // mouse cursor. - mouseChanged(pos, buttons, 0); - } while (++iteration < 11); + if (buttons != mouseButtons) { + // send the MouseEvent to avoid missing any clicks + mouseChanged(queuedPos, buttons, 0); + // mousePos updated by the mouseChanged() + queuedPos = mousePos; + mouseButtons = buttons; + } + } + + if (queuedPos != mousePos) + mouseChanged(queuedPos, mouseButtons, 0); } QT_END_NAMESPACE - diff --git a/src/gui/embedded/qmouseqnx_qws.h b/src/gui/embedded/qmouseqnx_qws.h index 2a5eef2..54deaf3 100644 --- a/src/gui/embedded/qmouseqnx_qws.h +++ b/src/gui/embedded/qmouseqnx_qws.h @@ -70,6 +70,8 @@ private Q_SLOTS: private: QSocketNotifier *mouseNotifier; int mouseFD; + int mouseButtons; + bool absolutePositioning; }; QT_END_NAMESPACE diff --git a/src/gui/embedded/qscreen_qws.cpp b/src/gui/embedded/qscreen_qws.cpp index d55fbed..b17ade0 100644 --- a/src/gui/embedded/qscreen_qws.cpp +++ b/src/gui/embedded/qscreen_qws.cpp @@ -64,7 +64,6 @@ QT_BEGIN_NAMESPACE // #define QT_USE_MEMCPY_DUFF #ifndef QT_NO_QWS_CURSOR -bool qt_sw_cursor=false; Q_GUI_EXPORT QScreenCursor * qt_screencursor = 0; #endif Q_GUI_EXPORT QScreen * qt_screen = 0; @@ -119,8 +118,6 @@ ClearCacheFunc QScreen::clearCacheFunc = 0; Returns a pointer to the application's unique screen cursor. */ -extern bool qws_sw_cursor; - /*! Constructs a screen cursor */ @@ -1548,6 +1545,7 @@ QImage::Format QScreenPrivate::preferredImageFormat() const \value SvgalibClass QSvgalibScreen \value ProxyClass QProxyScreen \value GLClass QGLScreen + \value IntfbClass QIntfbScreen \value CustomClass Unknown QScreen subclass \sa classId() diff --git a/src/gui/embedded/qscreen_qws.h b/src/gui/embedded/qscreen_qws.h index c2f8010..5ff90f9 100644 --- a/src/gui/embedded/qscreen_qws.h +++ b/src/gui/embedded/qscreen_qws.h @@ -44,7 +44,7 @@ #include <QtCore/qnamespace.h> #include <QtCore/qpoint.h> -#include <QtCore/qlist.h> +#include <QtCore/qstringlist.h> #include <QtGui/qrgb.h> #include <QtCore/qrect.h> #include <QtGui/qimage.h> @@ -130,7 +130,7 @@ const int SourcePixmap=1; class QScreenCursor; extern QScreenCursor *qt_screencursor; -extern bool qt_sw_cursor; +extern bool qws_sw_cursor; class Q_GUI_EXPORT QScreenCursor { @@ -145,7 +145,7 @@ public: bool supportsAlphaCursor() const { return supportsAlpha; } - static bool enabled() { return qt_sw_cursor; } + static bool enabled() { return qws_sw_cursor; } QRect boundingRect() const { return QRect(pos - hotspot, size); } QImage image() const { return cursor; } @@ -193,7 +193,7 @@ class Q_GUI_EXPORT QScreen { public: enum ClassId { LinuxFBClass, TransformedClass, VNCClass, MultiClass, VFbClass, DirectFBClass, SvgalibClass, ProxyClass, - GLClass, CustomClass = 1024 }; + GLClass, IntfbClass, CustomClass = 1024 }; QScreen(int display_id, ClassId classId); explicit QScreen(int display_id); @@ -357,7 +357,9 @@ private: friend class QVNCScreen; friend class QLinuxFbScreen; friend class QVFbScreen; + friend class QQnxScreen; friend class QProxyScreen; + friend class QIntfbScreen; #endif friend void qt_solidFill_setup(QScreen*, const QColor&, const QRegion&); friend void qt_blit_setup(QScreen *screen, const QImage &image, diff --git a/src/gui/embedded/qscreendriverfactory_qws.cpp b/src/gui/embedded/qscreendriverfactory_qws.cpp index 62330b1..710505a 100644 --- a/src/gui/embedded/qscreendriverfactory_qws.cpp +++ b/src/gui/embedded/qscreendriverfactory_qws.cpp @@ -48,6 +48,7 @@ #include "qscreenvfb_qws.h" #include "qscreenmulti_qws_p.h" #include "qscreenqnx_qws.h" +#include "qscreenintegrityfb_qws.h" #include <stdlib.h> #include "private/qfactoryloader_p.h" #include "qscreendriverplugin_qws.h" @@ -112,6 +113,10 @@ QScreen *QScreenDriverFactory::create(const QString& key, int displayId) if (driver == QLatin1String("qnx") || driver.isEmpty()) return new QQnxScreen(displayId); #endif +#if defined(Q_OS_INTEGRITY) && !defined(QT_NO_QWS_INTEGRITY) + if (driver == QLatin1String("integrityfb") || driver.isEmpty()) + return new QIntfbScreen(displayId); +#endif #ifndef QT_NO_QWS_QVFB if (driver == QLatin1String("qvfb") || driver.isEmpty()) return new QVFbScreen(displayId); @@ -159,6 +164,9 @@ QStringList QScreenDriverFactory::keys() #if defined(Q_OS_QNX) && !defined(QT_NO_QWS_QNX) list << QLatin1String("QNX"); #endif +#if defined(Q_OS_INTEGRITY) && !defined(QT_NO_QWS_INTEGRITY) + list << QLatin1String("INTEGRITYFB"); +#endif #ifndef QT_NO_QWS_QVFB list << QLatin1String("QVFb"); #endif diff --git a/src/gui/embedded/qscreenintegrityfb_qws.cpp b/src/gui/embedded/qscreenintegrityfb_qws.cpp new file mode 100644 index 0000000..6f30812 --- /dev/null +++ b/src/gui/embedded/qscreenintegrityfb_qws.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_QWS_INTEGRITYFB + +#include <qscreenintegrityfb_qws.h> +#include <qwindowsystem_qws.h> +#include <qsocketnotifier.h> +#include <qapplication.h> +#include <qscreen_qws.h> +#include "qmouseintegrity_qws.h" +#include "qkbdintegrity_qws.h" +#include <qmousedriverfactory_qws.h> +#include <qkbddriverfactory_qws.h> +#include <qdebug.h> + +#include <INTEGRITY.h> +#include <device/fbdriver.h> + +QT_BEGIN_NAMESPACE + +class QIntfbScreenPrivate +{ +public: + QIntfbScreenPrivate(); + ~QIntfbScreenPrivate(); + + FBHandle handle; + struct FBInfoStruct fbinfo; + + QWSMouseHandler *mouse; +#ifndef QT_NO_QWS_KEYBOARD + QWSKeyboardHandler *keyboard; +#endif +}; + +QIntfbScreenPrivate::QIntfbScreenPrivate() + : mouse(0) + +{ +#ifndef QT_NO_QWS_KEYBOARD + keyboard = 0; +#endif +} + +QIntfbScreenPrivate::~QIntfbScreenPrivate() +{ + delete mouse; +#ifndef QT_NO_QWS_KEYBOARD + delete keyboard; +#endif +} + +/*! + \internal + + \class QIntfbScreen + \ingroup qws + + \brief The QIntfbScreen class implements a screen driver for the + INTEGRITY framebuffer drivers. + + Note that this class is only available in \l{Qt for INTEGRITY}. + Custom screen drivers can be added by subclassing the + QScreenDriverPlugin class, using the QScreenDriverFactory class to + dynamically load the driver into the application, but there should + only be one screen object per application. + + \sa QScreen, QScreenDriverPlugin, {Running Applications} +*/ + +/*! + \fn bool QIntfbScreen::connect(const QString & displaySpec) + \reimp +*/ + +/*! + \fn void QIntfbScreen::disconnect() + \reimp +*/ + +/*! + \fn bool QIntfbScreen::initDevice() + \reimp +*/ + +/*! + \fn void QIntfbScreen::restore() + \reimp +*/ + +/*! + \fn void QIntfbScreen::save() + \reimp +*/ + +/*! + \fn void QIntfbScreen::setDirty(const QRect & r) + \reimp +*/ + +/*! + \fn void QIntfbScreen::setMode(int nw, int nh, int nd) + \reimp +*/ + +/*! + \fn void QIntfbScreen::shutdownDevice() + \reimp +*/ + +/*! + \fn QIntfbScreen::QIntfbScreen(int displayId) + + Constructs a QVNCScreen object. The \a displayId argument + identifies the Qt for Embedded Linux server to connect to. +*/ +QIntfbScreen::QIntfbScreen(int display_id) + : QScreen(display_id, IntfbClass), d_ptr(new QIntfbScreenPrivate) +{ + d_ptr->handle = 0; + data = 0; +} + +/*! + Destroys this QIntfbScreen object. +*/ +QIntfbScreen::~QIntfbScreen() +{ + delete d_ptr; +} + +static QIntfbScreen *connected = 0; + +bool QIntfbScreen::connect(const QString &displaySpec) +{ + FBDriver *fbdev; + + CheckSuccess(gh_FB_get_driver(0, &fbdev)); + CheckSuccess(gh_FB_init_device(fbdev, 0, &d_ptr->handle)); + CheckSuccess(gh_FB_get_info(d_ptr->handle, &d_ptr->fbinfo)); + + data = (uchar *)d_ptr->fbinfo.start; + + d = d_ptr->fbinfo.bitsperpixel; + switch (d) { + case 1: + setPixelFormat(QImage::Format_Mono); + break; + case 8: + setPixelFormat(QImage::Format_Indexed8); + break; + case 12: + setPixelFormat(QImage::Format_RGB444); + break; + case 15: + setPixelFormat(QImage::Format_RGB555); + break; + case 16: + setPixelFormat(QImage::Format_RGB16); + break; + case 18: + setPixelFormat(QImage::Format_RGB666); + break; + case 24: + setPixelFormat(QImage::Format_RGB888); +#ifdef QT_QWS_DEPTH_GENERIC +#if Q_BYTE_ORDER != Q_BIG_ENDIAN + qt_set_generic_blit(this, 24, + d_ptr->fbinfo.redbits, + d_ptr->fbinfo.greenbits, + d_ptr->fbinfo.bluebits, + d_ptr->fbinfo.alphabits, + d_ptr->fbinfo.redoffset, + d_ptr->fbinfo.greenoffset, + d_ptr->fbinfo.blueoffset, + d_ptr->fbinfo.alphaoffset); +#else + qt_set_generic_blit(this, 24, + d_ptr->fbinfo.redbits, + d_ptr->fbinfo.greenbits, + d_ptr->fbinfo.bluebits, + d_ptr->fbinfo.alphabits, + 16 - d_ptr->fbinfo.redoffset, + 16 - d_ptr->fbinfo.greenoffset, + 16 - d_ptr->fbinfo.blueoffset, + d_ptr->fbinfo.alphaoffset); +#endif +#endif + break; + case 32: + setPixelFormat(QImage::Format_ARGB32_Premultiplied); +#ifdef QT_QWS_DEPTH_GENERIC +#if Q_BYTE_ORDER != Q_BIG_ENDIAN + qt_set_generic_blit(this, 32, + d_ptr->fbinfo.redbits, + d_ptr->fbinfo.greenbits, + d_ptr->fbinfo.bluebits, + d_ptr->fbinfo.alphabits, + d_ptr->fbinfo.redoffset, + d_ptr->fbinfo.greenoffset, + d_ptr->fbinfo.blueoffset, + d_ptr->fbinfo.alphaoffset); +#else + qt_set_generic_blit(this, 32, + d_ptr->fbinfo.redbits, + d_ptr->fbinfo.greenbits, + d_ptr->fbinfo.bluebits, + d_ptr->fbinfo.alphabits, + 24 - d_ptr->fbinfo.redoffset, + 24 - d_ptr->fbinfo.greenoffset, + 24 - d_ptr->fbinfo.blueoffset, + d_ptr->fbinfo.alphaoffset ? 24 - d_ptr->fbinfo.alphaoffset : 0); +#endif +#endif + break; + } + + dw = w = d_ptr->fbinfo.width; + dh = h = d_ptr->fbinfo.height; + + /* assumes no padding */ + lstep = w * ((d + 7) >> 3); + + mapsize = size = h * lstep; + + /* default values */ + int dpi = 72; + physWidth = qRound(dw * 25.4 / dpi); + physHeight = qRound(dh * 25.4 / dpi); + + qDebug("Connected to INTEGRITYfb server: %d x %d x %d %dx%dmm (%dx%ddpi)", + w, h, d, physWidth, physHeight, qRound(dw*25.4/physWidth), qRound(dh*25.4/physHeight) ); + + + QWSServer::setDefaultMouse("integrity"); + QWSServer::setDefaultKeyboard("integrity"); + + connected = this; + + return true; +} + +void QIntfbScreen::disconnect() +{ + connected = 0; +} + +bool QIntfbScreen::initDevice() +{ + + CheckSuccess(gh_FB_set_info(d_ptr->handle, &d_ptr->fbinfo, false)); + CheckSuccess(gh_FB_get_info(d_ptr->handle, &d_ptr->fbinfo)); + data = (uchar *)d_ptr->fbinfo.start; + d = d_ptr->fbinfo.bitsperpixel; + dw = w = d_ptr->fbinfo.width; + dh = h = d_ptr->fbinfo.height; + mapsize = d_ptr->fbinfo.length; + /* assumes no padding */ + lstep = w * ((d + 7) >> 3); + + mapsize = size = h * lstep; + + data = (uchar *)d_ptr->fbinfo.start; + + d = d_ptr->fbinfo.bitsperpixel; + switch (d) { + case 1: + setPixelFormat(QImage::Format_Mono); + break; + case 8: + setPixelFormat(QImage::Format_Indexed8); + break; + case 12: + setPixelFormat(QImage::Format_RGB444); + break; + case 15: + setPixelFormat(QImage::Format_RGB555); + break; + case 16: + setPixelFormat(QImage::Format_RGB16); + break; + case 18: + setPixelFormat(QImage::Format_RGB666); + break; + case 24: + setPixelFormat(QImage::Format_RGB888); + break; + case 32: + setPixelFormat(QImage::Format_ARGB32_Premultiplied); + break; + } +#ifdef QT_QWS_DEPTH_GENERIC +#if defined(__BIG_ENDIAN__) + qt_set_generic_blit(this, d, + d_ptr->fbinfo.redbits, + d_ptr->fbinfo.greenbits, + d_ptr->fbinfo.bluebits, + d_ptr->fbinfo.alphabits, + 24 - d_ptr->fbinfo.redoffset, + 24 - d_ptr->fbinfo.greenoffset, + 24 - d_ptr->fbinfo.blueoffset, + d_ptr->fbinfo.alphaoffset ? 24 - d_ptr->fbinfo.alphaoffset : 0); +#else + qt_set_generic_blit(this, d, + d_ptr->fbinfo.redbits, + d_ptr->fbinfo.greenbits, + d_ptr->fbinfo.bluebits, + d_ptr->fbinfo.alphabits, + d_ptr->fbinfo.redoffset, + d_ptr->fbinfo.greenoffset, + d_ptr->fbinfo.blueoffset, + d_ptr->fbinfo.alphaoffset); +#endif +#endif + +#ifndef QT_NO_QWS_CURSOR + QScreenCursor::initSoftwareCursor(); +#endif + return true; +} + +void QIntfbScreen::shutdownDevice() +{ + gh_FB_close(d_ptr->handle); +} + +void QIntfbScreen::setMode(int ,int ,int) +{ +} + +// save the state of the graphics card +// This is needed so that e.g. we can restore the palette when switching +// between linux virtual consoles. +void QIntfbScreen::save() +{ + // nothing to do. +} + +// restore the state of the graphics card. +void QIntfbScreen::restore() +{ +} +void QIntfbScreen::setDirty(const QRect& rect) +{ + FBRect fbrect; + fbrect.dx = rect.x(); + fbrect.dy = rect.y(); + fbrect.width = rect.width(); + fbrect.height = rect.height(); + gh_FB_expose(d_ptr->handle, &fbrect); +} + +void QIntfbScreen::setBrightness(int b) +{ + if (connected) { + } +} + +void QIntfbScreen::blank(bool on) +{ +} + +#endif // QT_NO_QWS_INTEGRITYFB + +QT_END_NAMESPACE + diff --git a/src/gui/embedded/qscreenintegrityfb_qws.h b/src/gui/embedded/qscreenintegrityfb_qws.h new file mode 100644 index 0000000..bc09e8e --- /dev/null +++ b/src/gui/embedded/qscreenintegrityfb_qws.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCREENINTEGRITYFB_QWS_H +#define QSCREENINTEGRITYFB_QWS_H + +#include <QtGui/qscreen_qws.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QWS_INTEGRITYFB + +class QIntfbScreenPrivate; + +class Q_GUI_EXPORT QIntfbScreen : public QScreen +{ +public: + explicit QIntfbScreen(int display_id); + virtual ~QIntfbScreen(); + virtual bool initDevice(); + virtual bool connect(const QString &displaySpec); + virtual void disconnect(); + virtual void shutdownDevice(); + virtual void save(); + virtual void restore(); + virtual void setMode(int nw,int nh,int nd); + virtual void setDirty(const QRect& r); + virtual void blank(bool); + static void setBrightness(int b); + +private: + QIntfbScreenPrivate *d_ptr; +}; + +#endif // QT_NO_QWS_INTEGRITYFB + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCREENINTEGRITYFB_QWS_H diff --git a/src/gui/embedded/qscreenlinuxfb_qws.cpp b/src/gui/embedded/qscreenlinuxfb_qws.cpp index 67c8a31..4b41b5b 100644 --- a/src/gui/embedded/qscreenlinuxfb_qws.cpp +++ b/src/gui/embedded/qscreenlinuxfb_qws.cpp @@ -110,7 +110,9 @@ QLinuxFbScreenPrivate::QLinuxFbScreenPrivate() #endif ttyfd(-1), oldKdMode(KD_TEXT) { +#ifndef QT_NO_QWS_SIGNALHANDLER QWSSignalHandler::instance()->addObject(this); +#endif } QLinuxFbScreenPrivate::~QLinuxFbScreenPrivate() @@ -263,6 +265,9 @@ QLinuxFbScreen::QLinuxFbScreen(int display_id) QLinuxFbScreen::~QLinuxFbScreen() { +#ifdef QT_NO_QWS_SIGNALHANDLER + delete d_ptr; +#endif } /*! diff --git a/src/gui/embedded/qscreenqnx_qws.cpp b/src/gui/embedded/qscreenqnx_qws.cpp index 4afe087..d34e732 100644 --- a/src/gui/embedded/qscreenqnx_qws.cpp +++ b/src/gui/embedded/qscreenqnx_qws.cpp @@ -40,7 +40,9 @@ ****************************************************************************/ #include "qscreenqnx_qws.h" -#include "qdebug.h" + +#include <qapplication.h> +#include <qregexp.h> #include <gf/gf.h> @@ -52,6 +54,10 @@ struct QQnxScreenContext inline QQnxScreenContext() : device(0), display(0), layer(0), hwSurface(0), memSurface(0), context(0) {} + inline ~QQnxScreenContext() + { cleanup(); } + + void cleanup(); gf_dev_t device; gf_dev_info_t deviceInfo; @@ -64,6 +70,35 @@ struct QQnxScreenContext gf_context_t context; }; +void QQnxScreenContext::cleanup() +{ + if (context) { + gf_context_free(context); + context = 0; + } + if (memSurface) { + gf_surface_free(memSurface); + memSurface = 0; + } + if (hwSurface) { + gf_surface_free(hwSurface); + hwSurface = 0; + } + if (layer) { + gf_layer_detach(layer); + layer = 0; + } + if (display) { + gf_display_detach(display); + display = 0; + } + if (device) { + gf_dev_detach(device); + device = 0; + } +} + + /*! \class QQnxScreen \preliminary @@ -117,19 +152,23 @@ QQnxScreen::~QQnxScreen() delete d; } -/*! \reimp +/*! + \reimp */ bool QQnxScreen::initDevice() { - // implement this if you have multiple processes that want to access the display - // (not required if QT_NO_QWS_MULTIPROCESS is set) +#ifndef QT_NO_QWS_CURSOR + QScreenCursor::initSoftwareCursor(); +#endif + return true; } -/*! \internal - Attaches to the named device \a name. +/*! + \internal + Attaches to the named device \a name. */ -static bool attachDevice(QQnxScreenContext * const d, const char *name) +static inline bool attachDevice(QQnxScreenContext * const d, const char *name) { int ret = gf_dev_attach(&d->device, name, &d->deviceInfo); if (ret != GF_ERR_OK) { @@ -139,193 +178,231 @@ static bool attachDevice(QQnxScreenContext * const d, const char *name) return true; } -/*! \internal - Attaches to the display at index \a displayIndex. - */ -static bool attachDisplay(QQnxScreenContext * const d, int displayIndex) +/*! + \internal + Attaches to the display at index \a displayIndex. +*/ +static inline bool attachDisplay(QQnxScreenContext * const d, int displayIndex) { int ret = gf_display_attach(&d->display, d->device, displayIndex, &d->displayInfo); if (ret != GF_ERR_OK) { - qWarning("QQnxScreen: gf_display_attach(%d) failed with error code %d", - displayIndex, ret); + qWarning("QQnxScreen: gf_display_attach(%d) failed with error code %d", displayIndex, ret); return false; } return true; } -/*! \internal - Attaches to the layer \a layerIndex. - */ -static bool attachLayer(QQnxScreenContext * const d, int layerIndex) +/*! + \internal + Attaches to the layer \a layerIndex. +*/ +static inline bool attachLayer(QQnxScreenContext * const d, int layerIndex) { - int ret = gf_layer_attach(&d->layer, d->display, layerIndex, 0); + unsigned flags = QApplication::type() != QApplication::GuiServer ? GF_LAYER_ATTACH_PASSIVE : 0; + int ret = gf_layer_attach(&d->layer, d->display, layerIndex, flags); if (ret != GF_ERR_OK) { - qWarning("QQnxScreen: gf_layer_attach(%d) failed with error code %d", layerIndex, - ret); + qWarning("QQnxScreen: gf_layer_attach(%d) failed with error code %d", layerIndex, ret); return false; } - gf_layer_enable(d->layer); return true; } -/*! \internal - Creates a new hardware surface (usually on the Gfx card memory) with the dimensions \a w * \a h. - */ -static bool createHwSurface(QQnxScreenContext * const d, int w, int h) +/*! + \internal + Creates a new hardware surface (usually on the Gfx card memory) with the dimensions \a w * \a h. +*/ +static inline bool createHwSurface(QQnxScreenContext * const d, int w, int h) { int ret = gf_surface_create_layer(&d->hwSurface, &d->layer, 1, 0, - w, h, GF_FORMAT_ARGB8888, 0, 0); + w, h, d->displayInfo.format, 0, 0); if (ret != GF_ERR_OK) { - qWarning("QQnxScreen: gf_surface_create_layer(%dx%d) failed with error code %d", - w, h, ret); + qWarning("QQnxScreen: gf_surface_create_layer(%dx%d) failed with error code %d", w, h, ret); return false; } gf_layer_set_surfaces(d->layer, &d->hwSurface, 1); + gf_layer_enable(d->layer); + ret = gf_layer_update(d->layer, 0); if (ret != GF_ERR_OK) { qWarning("QQnxScreen: gf_layer_update() failed with error code %d\n", ret); return false; } - return true; -} - -/*! \internal - Creates an in-memory, linear accessible surface of dimensions \a w * \a h. - This is the main surface that QWS blits to. - */ -static bool createMemSurface(QQnxScreenContext * const d, int w, int h) -{ - // Note: gf_surface_attach() could also be used, so we'll create the buffer - // and let the surface point to it. Here, we use surface_create instead. - - int ret = gf_surface_create(&d->memSurface, d->device, w, h, - GF_FORMAT_ARGB8888, 0, - GF_SURFACE_CREATE_CPU_FAST_ACCESS | GF_SURFACE_CREATE_CPU_LINEAR_ACCESSIBLE - | GF_SURFACE_PHYS_CONTIG | GF_SURFACE_CREATE_SHAREABLE); + ret = gf_context_create(&d->context); if (ret != GF_ERR_OK) { - qWarning("QQnxScreen: gf_surface_create(%dx%d) failed with error code %d", - w, h, ret); + qWarning("QQnxScreen: gf_context_create() failed with error code %d", ret); return false; } - gf_surface_get_info(d->memSurface, &d->memSurfaceInfo); - - if (d->memSurfaceInfo.sid == unsigned(GF_SID_INVALID)) { - qWarning("QQnxScreen: gf_surface_get_info() failed."); + ret = gf_context_set_surface(d->context, d->hwSurface); + if (ret != GF_ERR_OK) { + qWarning("QQnxScreen: gf_context_set_surface() failed with error code %d", ret); return false; } return true; } -/* \internal - Creates a QNX gf context and sets our memory surface on it. - */ -static bool createContext(QQnxScreenContext * const d) +/*! + \internal + Creates an in-memory, linear accessible surface of dimensions \a w * \a h. + This is the main surface that QWS blits to. +*/ +static inline bool createMemSurface(QQnxScreenContext * const d, int w, int h) { - int ret = gf_context_create(&d->context); +#ifndef QT_NO_QWS_MULTIPROCESS + if (QApplication::type() != QApplication::GuiServer) { + unsigned sidlist[64]; + int n = gf_surface_sidlist(d->device, sidlist); // undocumented API + for (int i = 0; i < n; ++i) { + int ret = gf_surface_attach_by_sid(&d->memSurface, d->device, sidlist[i]); + if (ret == GF_ERR_OK) { + gf_surface_get_info(d->memSurface, &d->memSurfaceInfo); + if (d->memSurfaceInfo.sid != unsigned(GF_SID_INVALID)) { + // can we use the surface's vaddr? + unsigned flags = GF_SURFACE_CPU_LINEAR_READABLE | GF_SURFACE_CPU_LINEAR_WRITEABLE; + if ((d->memSurfaceInfo.flags & flags) == flags) + return true; + } + + gf_surface_free(d->memSurface); + d->memSurface = 0; + } + } + qWarning("QQnxScreen: cannot attach to an usable surface; create a new one."); + } +#endif + int ret = gf_surface_create(&d->memSurface, d->device, w, h, d->displayInfo.format, 0, + GF_SURFACE_CREATE_CPU_FAST_ACCESS | GF_SURFACE_CREATE_CPU_LINEAR_ACCESSIBLE + | GF_SURFACE_CREATE_PHYS_CONTIG | GF_SURFACE_CREATE_SHAREABLE); if (ret != GF_ERR_OK) { - qWarning("QQnxScreen: gf_context_create() failed with error code %d", ret); + qWarning("QQnxScreen: gf_surface_create(%dx%d) failed with error code %d", + w, h, ret); return false; } - ret = gf_context_set_surface(d->context, d->memSurface); - if (ret != GF_ERR_OK) { - qWarning("QQnxScreen: gf_context_set_surface() failed with error code %d", ret); + gf_surface_get_info(d->memSurface, &d->memSurfaceInfo); + + if (d->memSurfaceInfo.sid == unsigned(GF_SID_INVALID)) { + qWarning("QQnxScreen: gf_surface_get_info() failed."); return false; } return true; } -/*! \reimp - Connects to QNX's io-display based device based on the \a displaySpec parameters - from the \c{QWS_DISPLAY} environment variable. See the QQnxScreen class documentation - for possible parameters. +/*! + \reimp + Connects to QNX's io-display based device based on the \a displaySpec parameters + from the \c{QWS_DISPLAY} environment variable. See the QQnxScreen class documentation + for possible parameters. - \sa QQnxScreen - */ + \sa QQnxScreen +*/ bool QQnxScreen::connect(const QString &displaySpec) { const QStringList params = displaySpec.split(QLatin1Char(':'), QString::SkipEmptyParts); - bool isOk = false; - QRegExp deviceRegExp(QLatin1String("^device=(.+)$")); - if (params.indexOf(deviceRegExp) != -1) { - isOk = attachDevice(d, deviceRegExp.cap(1).toLocal8Bit().constData()); - } else { - // no device specified - attach to device 0 (the default) - isOk = attachDevice(d, GF_DEVICE_INDEX(0)); + // default to device 0 + int deviceIndex = 0; + if (!params.isEmpty()) { + QRegExp deviceRegExp(QLatin1String("^device=(.+)$")); + if (params.indexOf(deviceRegExp) != -1) + deviceIndex = deviceRegExp.cap(1).toInt(); } - if (!isOk) + if (!attachDevice(d, GF_DEVICE_INDEX(deviceIndex))) return false; qDebug("QQnxScreen: Attached to Device, number of displays: %d", d->deviceInfo.ndisplays); - // default to display 0 - int displayIndex = 0; - QRegExp displayRegexp(QLatin1String("^display=(\\d+)$")); - if (params.indexOf(displayRegexp) != -1) { - displayIndex = displayRegexp.cap(1).toInt(); + // default to display id passed to constructor + int displayIndex = displayId; + if (!params.isEmpty()) { + QRegExp displayRegexp(QLatin1String("^display=(\\d+)$")); + if (params.indexOf(displayRegexp) != -1) + displayIndex = displayRegexp.cap(1).toInt(); } if (!attachDisplay(d, displayIndex)) return false; qDebug("QQnxScreen: Attached to Display %d, resolution %dx%d, refresh %d Hz", - displayIndex, d->displayInfo.xres, d->displayInfo.yres, - d->displayInfo.refresh); - + displayIndex, d->displayInfo.xres, d->displayInfo.yres, d->displayInfo.refresh); // default to main_layer_index from the displayInfo struct - int layerIndex = 0; - QRegExp layerRegexp(QLatin1String("^layer=(\\d+)$")); - if (params.indexOf(layerRegexp) != -1) { - layerIndex = layerRegexp.cap(1).toInt(); - } else { - layerIndex = d->displayInfo.main_layer_index; + int layerIndex = d->displayInfo.main_layer_index; + if (!params.isEmpty()) { + QRegExp layerRegexp(QLatin1String("^layer=(\\d+)$")); + if (params.indexOf(layerRegexp) != -1) + layerIndex = layerRegexp.cap(1).toInt(); } if (!attachLayer(d, layerIndex)) return false; + // determine the pixel format and the pixel type + switch (d->displayInfo.format) { +#if defined(QT_QWS_DEPTH_32) || defined(QT_QWS_DEPTH_GENERIC) + case GF_FORMAT_ARGB8888: + pixeltype = QScreen::BGRPixel; + // fall through + case GF_FORMAT_BGRA8888: + setPixelFormat(QImage::Format_ARGB32); + break; +#endif +#if defined(QT_QWS_DEPTH_24) + case GF_FORMAT_BGR888: + pixeltype = QScreen::BGRPixel; + setPixelFormat(QImage::Format_RGB888); + break; +#endif +#if defined(QT_QWS_DEPTH_16) || defined(QT_QWS_DEPTH_GENERIC) + case GF_FORMAT_PACK_RGB565: + case GF_FORMAT_PKLE_RGB565: + case GF_FORMAT_PKBE_RGB565: +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + setFrameBufferLittleEndian((d->displayInfo.format & GF_FORMAT_PKLE) == GF_FORMAT_PKLE); +#endif + setPixelFormat(QImage::Format_RGB16); + break; +#endif + default: + return false; + } + // tell QWSDisplay the width and height of the display w = dw = d->displayInfo.xres; h = dh = d->displayInfo.yres; - - // we only support 32 bit displays for now. - QScreen::d = 32; + QScreen::d = (d->displayInfo.format & GF_FORMAT_BPP); // colour depth // assume 72 dpi as default, to calculate the physical dimensions if not specified const int defaultDpi = 72; - - // Handle display physical size spec. - QRegExp mmWidthRegexp(QLatin1String("^mmWidth=(\\d+)$")); - if (params.indexOf(mmWidthRegexp) == -1) { - physWidth = qRound(dw * 25.4 / defaultDpi); - } else { - physWidth = mmWidthRegexp.cap(1).toInt(); + // Handle display physical size + physWidth = qRound(dw * 25.4 / defaultDpi); + physHeight = qRound(dh * 25.4 / defaultDpi); + if (!params.isEmpty()) { + QRegExp mmWidthRegexp(QLatin1String("^mmWidth=(\\d+)$")); + if (params.indexOf(mmWidthRegexp) != -1) + physWidth = mmWidthRegexp.cap(1).toInt(); + + QRegExp mmHeightRegexp(QLatin1String("^mmHeight=(\\d+)$")); + if (params.indexOf(mmHeightRegexp) != -1) + physHeight = mmHeightRegexp.cap(1).toInt(); } - QRegExp mmHeightRegexp(QLatin1String("^mmHeight=(\\d+)$")); - if (params.indexOf(mmHeightRegexp) == -1) { - physHeight = qRound(dh * 25.4 / defaultDpi); - } else { - physHeight = mmHeightRegexp.cap(1).toInt(); + if (QApplication::type() == QApplication::GuiServer) { + // create a hardware surface with our dimensions. In the old days, it was possible + // to get a pointer directly to the hw surface, so we could blit directly. Now, we + // have to use one indirection more, because it's not guaranteed that the hw surface + // is mappable into our process. + if (!createHwSurface(d, w, h)) + return false; } - // create a hardware surface with our dimensions. In the old days, it was possible - // to get a pointer directly to the hw surface, so we could blit directly. Now, we - // have to use one indirection more, because it's not guaranteed that the hw surface - // is mappable into our process. - if (!createHwSurface(d, w, h)) - return false; - // create an in-memory linear surface that is used by QWS. QWS will blit directly in here. if (!createMemSurface(d, w, h)) return false; @@ -338,72 +415,84 @@ bool QQnxScreen::connect(const QString &displaySpec) // the overall size of the in-memory buffer is linestep * height size = mapsize = lstep * h; - // create a QNX drawing context - if (!createContext(d)) - return false; - - // we're always using a software cursor for now. Initialize it here. - QScreenCursor::initSoftwareCursor(); - // done, the driver should be connected to the display now. return true; } -/*! \reimp - */ +/*! + \reimp +*/ void QQnxScreen::disconnect() { - if (d->context) - gf_context_free(d->context); - - if (d->memSurface) - gf_surface_free(d->memSurface); - - if (d->hwSurface) - gf_surface_free(d->hwSurface); - - if (d->layer) - gf_layer_detach(d->layer); - - if (d->display) - gf_display_detach(d->display); - - if (d->device) - gf_dev_detach(d->device); - - d->memSurface = 0; - d->hwSurface = 0; - d->context = 0; - d->layer = 0; - d->display = 0; - d->device = 0; + d->cleanup(); } -/*! \reimp - */ +/*! + \reimp +*/ void QQnxScreen::shutdownDevice() { } - -/*! \reimp - QQnxScreen doesn't support setting the mode, use io-display instead. - */ +/*! + \reimp + QQnxScreen doesn't support setting the mode, use io-display instead. +*/ void QQnxScreen::setMode(int,int,int) { qWarning("QQnxScreen: Unable to change mode, use io-display instead."); } -/*! \reimp - */ +/*! + \reimp +*/ bool QQnxScreen::supportsDepth(int depth) const { - // only 32-bit for the moment - return depth == 32; + gf_modeinfo_t displayMode; + for (int i = 0; gf_display_query_mode(d->display, i, &displayMode) == GF_ERR_OK; ++i) { + switch (displayMode.primary_format) { +#if defined(QT_QWS_DEPTH_32) || defined(QT_QWS_DEPTH_GENERIC) + case GF_FORMAT_ARGB8888: + case GF_FORMAT_BGRA8888: + if (depth == 32) + return true; + break; +#endif +#if defined(QT_QWS_DEPTH_24) + case GF_FORMAT_BGR888: + if (depth == 24) + return true; + break; +#endif +#if defined(QT_QWS_DEPTH_16) || defined(QT_QWS_DEPTH_GENERIC) + case GF_FORMAT_PACK_RGB565: + case GF_FORMAT_PKLE_RGB565: + case GF_FORMAT_PKBE_RGB565: + if (depth == 16) + return true; + break; +#endif + default: + break; + } + } + + return false; } -/*! \reimp - */ +/*! + \reimp +*/ +void QQnxScreen::blank(bool on) +{ + int ret = gf_display_set_dpms(d->display, on ? GF_DPMS_OFF : GF_DPMS_ON); + if (ret != GF_ERR_OK) + qWarning("QQnxScreen: gf_display_set_dpms() failed with error code %d", ret); +} + +/*! + \reimp +*/ void QQnxScreen::exposeRegion(QRegion r, int changing) { // here is where the actual magic happens. QWS will call exposeRegion whenever @@ -414,6 +503,10 @@ void QQnxScreen::exposeRegion(QRegion r, int changing) QScreen::exposeRegion(r, changing); // now our in-memory surface should be up to date with the latest changes. + + if (!d->hwSurface) + return; + // the code below copies the region from the in-memory surface to the hardware. // just get the bounding rectangle of the region. Most screen updates are rectangular @@ -432,16 +525,14 @@ void QQnxScreen::exposeRegion(QRegion r, int changing) // blit the changed region from the memory surface to the hardware surface ret = gf_draw_blit2(d->context, d->memSurface, d->hwSurface, - br.x(), br.y(), br.right(), br.bottom(), br.x(), br.y()); - if (ret != GF_ERR_OK) { + br.x(), br.y(), br.right(), br.bottom(), br.x(), br.y()); + if (ret != GF_ERR_OK) qWarning("QQnxScreen: gf_draw_blit2() failed with error code %d", ret); - } // flush all drawing commands (in our case, a single blit) ret = gf_draw_flush(d->context); - if (ret != GF_ERR_OK) { + if (ret != GF_ERR_OK) qWarning("QQnxScreen: gf_draw_flush() failed with error code %d", ret); - } // tell QNX that we're done drawing. gf_draw_end(d->context); diff --git a/src/gui/embedded/qscreenqnx_qws.h b/src/gui/embedded/qscreenqnx_qws.h index 38c0ac9..6f6d18a 100644 --- a/src/gui/embedded/qscreenqnx_qws.h +++ b/src/gui/embedded/qscreenqnx_qws.h @@ -66,6 +66,7 @@ public: void shutdownDevice(); void setMode(int,int,int); bool supportsDepth(int) const; + void blank(bool on); void exposeRegion(QRegion r, int changing); diff --git a/src/gui/embedded/qwindowsystem_qws.cpp b/src/gui/embedded/qwindowsystem_qws.cpp index 0e4e27c..36802a8 100644 --- a/src/gui/embedded/qwindowsystem_qws.cpp +++ b/src/gui/embedded/qwindowsystem_qws.cpp @@ -2494,7 +2494,7 @@ QWSWindow *QWSServer::windowAt(const QPoint& pos) } #ifndef QT_NO_QWS_KEYBOARD -static int keyUnicode(int keycode) +static inline int keyUnicode(int keycode) { int code = 0xffff; @@ -2550,18 +2550,16 @@ void QWSServer::sendKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers mod void QWSServerPrivate::sendKeyEventUnfiltered(int unicode, int keycode, Qt::KeyboardModifiers modifiers, bool isPress, bool autoRepeat) { + QWSWindow *win = keyboardGrabber ? keyboardGrabber : qwsServerPrivate->focusw; - QWSKeyEvent event; - QWSWindow *win = keyboardGrabber ? keyboardGrabber : - qwsServerPrivate->focusw; - - event.simpleData.window = win ? win->winId() : 0; - - event.simpleData.unicode = #ifndef QT_NO_QWS_KEYBOARD - unicode < 0 ? keyUnicode(keycode) : + if (unicode < 0) + unicode = keyUnicode(keycode); #endif - unicode; + + QWSKeyEvent event; + event.simpleData.window = win ? win->winId() : 0; + event.simpleData.unicode = unicode; event.simpleData.keycode = keycode; event.simpleData.modifiers = modifiers; event.simpleData.is_press = isPress; @@ -4127,11 +4125,11 @@ void QWSServer::processKeyEvent(int unicode, int keycode, Qt::KeyboardModifiers #endif // If we press a key and it's going to be blocked, wake up the screen - if ( block && isPress ) - qwsServerPrivate->_q_screenSaverWake(); - - if ( block ) + if (block) { + if (isPress) + qwsServerPrivate->_q_screenSaverWake(); return; + } if (keyFilters) { for (int i = 0; i < keyFilters->size(); ++i) { diff --git a/src/gui/embedded/qwslock.cpp b/src/gui/embedded/qwslock.cpp index 8eeb4e0..f9ea000 100644 --- a/src/gui/embedded/qwslock.cpp +++ b/src/gui/embedded/qwslock.cpp @@ -45,14 +45,15 @@ #include "qwssignalhandler_p.h" -#include <qglobal.h> -#include <qdebug.h> +#include <stdint.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> +#ifndef QT_POSIX_IPC #include <sys/sem.h> +#endif #include <sys/time.h> #include <time.h> #ifdef Q_OS_LINUX @@ -60,179 +61,211 @@ #endif #include <unistd.h> +#include <private/qcore_unix_p.h> + QT_BEGIN_NAMESPACE #ifdef QT_NO_SEMAPHORE #error QWSLock currently requires semaphores #endif -#ifndef Q_OS_BSD4 -union semun { - int val; - struct semid_ds *buf; - unsigned short *array; - struct seminfo *__buf; -}; +#ifdef QT_POSIX_IPC +#include <QtCore/QAtomicInt> + +static QBasicAtomicInt localUniqueId = Q_BASIC_ATOMIC_INITIALIZER(1); #endif -QWSLock::QWSLock() +QWSLock::QWSLock(int id) : semId(id) { - semId = semget(IPC_PRIVATE, 3, IPC_CREAT | 0666); - - if (semId == -1) { - perror("QWSLock::QWSLock"); - qFatal("Unable to create semaphore"); - } - QWSSignalHandler::instance()->addSemaphore(semId); + static unsigned short initialValues[3] = { 1, 1, 0 }; - semun semval; - semval.val = 1; +#ifndef QT_NO_QWS_SIGNALHANDLER + QWSSignalHandler::instance()->addWSLock(this); +#endif - if (semctl(semId, BackingStore, SETVAL, semval) == -1) { - perror("QWSLock::QWSLock"); - qFatal("Unable to initialize backingstore semaphore"); +#ifndef QT_POSIX_IPC + if (semId == -1) { + semId = semget(IPC_PRIVATE, 3, IPC_CREAT | 0666); + if (semId == -1) { + perror("QWSLock::QWSLock"); + qFatal("Unable to create semaphore"); + } + + qt_semun semval; + semval.array = initialValues; + if (semctl(semId, 0, SETALL, semval) == -1) { + perror("QWSLock::QWSLock"); + qFatal("Unable to initialize semaphores"); + } } - lockCount[BackingStore] = 0; +#else + sems[0] = sems[1] = sems[2] = SEM_FAILED; + owned = false; - if (semctl(semId, Communication, SETVAL, semval) == -1) { - perror("QWSLock::QWSLock"); - qFatal("Unable to initialize communication semaphore"); + if (semId == -1) { + // ### generate really unique IDs + semId = (getpid() << 16) + (localUniqueId.fetchAndAddRelaxed(1) % ushort(-1)); + owned = true; } - lockCount[Communication] = 0; - semval.val = 0; - if (semctl(semId, RegionEvent, SETVAL, semval) == -1) { - perror("QWSLock::QWSLock"); - qFatal("Unable to initialize region event semaphore"); + QByteArray pfx = "/qwslock_" + QByteArray::number(semId, 16) + '_'; + QByteArray keys[3] = { pfx + "BackingStore", pfx + "Communication", pfx + "RegionEvent" }; + for (int i = 0; i < 3; ++i) { + if (owned) + sem_unlink(keys[i].constData()); + do { + sems[i] = sem_open(keys[i].constData(), (owned ? O_CREAT : 0), 0666, initialValues[i]); + } while (sems[i] == SEM_FAILED && errno == EINTR); + if (sems[i] == SEM_FAILED) { + perror("QWSLock::QWSLock"); + qFatal("Unable to %s semaphore", (owned ? "create" : "open")); + } } -} +#endif -QWSLock::QWSLock(int id) -{ - semId = id; - QWSSignalHandler::instance()->addSemaphore(semId); lockCount[0] = lockCount[1] = 0; } QWSLock::~QWSLock() { - if (semId == -1) - return; - QWSSignalHandler::instance()->removeSemaphore(semId); -} +#ifndef QT_NO_QWS_SIGNALHANDLER + QWSSignalHandler::instance()->removeWSLock(this); +#endif -static bool forceLock(int semId, int semNum, int) -{ - int ret; - do { - sembuf sops = { semNum, -1, 0 }; - - // As the BackingStore lock is a mutex, and only one process may own - // the lock, it's safe to use SEM_UNDO. On the other hand, the - // Communication lock is locked by the client but unlocked by the - // server and therefore can't use SEM_UNDO. - if (semNum == QWSLock::BackingStore) - sops.sem_flg |= SEM_UNDO; - - ret = semop(semId, &sops, 1); - if (ret == -1 && errno != EINTR) - qDebug("QWSLock::lock: %s", strerror(errno)); - } while (ret == -1 && errno == EINTR); - - return (ret != -1); + if (semId != -1) { +#ifndef QT_POSIX_IPC + qt_semun semval; + semval.val = 0; + semctl(semId, 0, IPC_RMID, semval); + semId = -1; +#else + // emulate the SEM_UNDO behavior for the BackingStore lock + while (hasLock(BackingStore)) + unlock(BackingStore); + + QByteArray pfx = "/qwslock_" + QByteArray::number(semId, 16) + '_'; + QByteArray keys[3] = { pfx + "BackingStore", pfx + "Communication", pfx + "RegionEvent" }; + for (int i = 0; i < 3; ++i) { + if (sems[i] != SEM_FAILED) { + sem_close(sems[i]); + sems[i] = SEM_FAILED; + } + if (owned) + sem_unlink(keys[i].constData()); + } +#endif + } } -static bool up(int semId, int semNum) +bool QWSLock::up(unsigned short semNum) { int ret; - do { - sembuf sops = { semNum, 1, 0 }; - ret = semop(semId, &sops, 1); - if (ret == -1 && errno != EINTR) - qDebug("QWSLock::up: %s", strerror(errno)); - } while (ret == -1 && errno == EINTR); - - return (ret != -1); + +#ifndef QT_POSIX_IPC + sembuf sops = { semNum, 1, 0 }; + // As the BackingStore lock is a mutex, and only one process may own + // the lock, it's safe to use SEM_UNDO. On the other hand, the + // Communication lock is locked by the client but unlocked by the + // server and therefore can't use SEM_UNDO. + if (semNum == BackingStore) + sops.sem_flg |= SEM_UNDO; + + EINTR_LOOP(ret, semop(semId, &sops, 1)); +#else + ret = sem_post(sems[semNum]); +#endif + if (ret == -1) { + qDebug("QWSLock::up(): %s", strerror(errno)); + return false; + } + + return true; } -static bool down(int semId, int semNum) +bool QWSLock::down(unsigned short semNum, int) { int ret; - do { - sembuf sops = { semNum, -1, 0 }; - ret = semop(semId, &sops, 1); - if (ret == -1 && errno != EINTR) - qDebug("QWSLock::down: %s", strerror(errno)); - } while (ret == -1 && errno == EINTR); - - return (ret != -1); + +#ifndef QT_POSIX_IPC + sembuf sops = { semNum, -1, 0 }; + // As the BackingStore lock is a mutex, and only one process may own + // the lock, it's safe to use SEM_UNDO. On the other hand, the + // Communication lock is locked by the client but unlocked by the + // server and therefore can't use SEM_UNDO. + if (semNum == BackingStore) + sops.sem_flg |= SEM_UNDO; + + EINTR_LOOP(ret, semop(semId, &sops, 1)); +#else + EINTR_LOOP(ret, sem_wait(sems[semNum])); +#endif + if (ret == -1) { + qDebug("QWSLock::down(): %s", strerror(errno)); + return false; + } + + return true; } -static int getValue(int semId, int semNum) +int QWSLock::getValue(unsigned short semNum) const { int ret; - do { - ret = semctl(semId, semNum, GETVAL, 0); - if (ret == -1 && errno != EINTR) - qDebug("QWSLock::getValue: %s", strerror(errno)); - } while (ret == -1 && errno == EINTR); - +#ifndef QT_POSIX_IPC + ret = semctl(semId, semNum, GETVAL, 0); +#else + if (sem_getvalue(sems[semNum], &ret) == -1) + ret = -1; +#endif + if (ret == -1) + qDebug("QWSLock::getValue(): %s", strerror(errno)); return ret; } bool QWSLock::lock(LockType type, int timeout) { if (type == RegionEvent) - return up(semId, RegionEvent); + return up(type); - if (hasLock(type)) { + if (lockCount[type] > 0) { ++lockCount[type]; return true; } - if (!forceLock(semId, type, timeout)) - return false; - ++lockCount[type]; - return true; + if (down(type, timeout)) { + ++lockCount[type]; + return true; + } + + return false; } bool QWSLock::hasLock(LockType type) { if (type == RegionEvent) - return (getValue(semId, RegionEvent) == 0); + return getValue(type) == 0; - return (lockCount[type] > 0); + return lockCount[type] > 0; } void QWSLock::unlock(LockType type) { if (type == RegionEvent) { - down(semId, RegionEvent); + down(type, -1); return; } - if (hasLock(type)) { + if (lockCount[type] > 0) { --lockCount[type]; - if (hasLock(type)) + if (lockCount[type] > 0) return; } - const int semNum = type; - int ret; - do { - sembuf sops = {semNum, 1, 0}; - if (semNum == QWSLock::BackingStore) - sops.sem_flg |= SEM_UNDO; - - ret = semop(semId, &sops, 1); - if (ret == -1 && errno != EINTR) - qDebug("QWSLock::unlock: %s", strerror(errno)); - } while (ret == -1 && errno == EINTR); + up(type); } bool QWSLock::wait(LockType type, int timeout) { - bool ok = forceLock(semId, type, timeout); + bool ok = down(type, timeout); if (ok) unlock(type); return ok; diff --git a/src/gui/embedded/qwslock_p.h b/src/gui/embedded/qwslock_p.h index 9a7f279..71a4cca 100644 --- a/src/gui/embedded/qwslock_p.h +++ b/src/gui/embedded/qwslock_p.h @@ -55,17 +55,20 @@ #include <qglobal.h> -QT_BEGIN_NAMESPACE - #ifndef QT_NO_QWS_MULTIPROCESS +#ifdef QT_POSIX_IPC +# include <semaphore.h> +#endif + +QT_BEGIN_NAMESPACE + class QWSLock { public: enum LockType { BackingStore, Communication, RegionEvent }; - QWSLock(); - QWSLock(int lockId); + QWSLock(int lockId = -1); ~QWSLock(); bool lock(LockType type, int timeout = -1); @@ -75,11 +78,20 @@ public: int id() const { return semId; } private: + bool up(unsigned short semNum); + bool down(unsigned short semNum, int timeout); + int getValue(unsigned short semNum) const; + int semId; int lockCount[2]; +#ifdef QT_POSIX_IPC + sem_t *sems[3]; + bool owned; +#endif }; - QT_END_NAMESPACE + #endif // QT_NO_QWS_MULTIPROCESS + #endif // QWSLOCK_P_H diff --git a/src/gui/embedded/qwssharedmemory.cpp b/src/gui/embedded/qwssharedmemory.cpp index 66bedee..853de61 100644 --- a/src/gui/embedded/qwssharedmemory.cpp +++ b/src/gui/embedded/qwssharedmemory.cpp @@ -43,51 +43,102 @@ #if !defined(QT_NO_QWS_MULTIPROCESS) +#include <sys/types.h> +#include <sys/ipc.h> +#ifndef QT_POSIX_IPC #include <sys/shm.h> +#else +#include <sys/mman.h> +#include <sys/stat.h> +#endif +#include <fcntl.h> +#include <unistd.h> + +#include <private/qcore_unix_p.h> + +//#define QT_SHM_DEBUG QT_BEGIN_NAMESPACE -QWSSharedMemory::QWSSharedMemory() - : shmBase(0), shmSize(0), character(0), shmId(-1), key(-1) +#ifdef QT_POSIX_IPC +#include <QtCore/QAtomicInt> + +static QBasicAtomicInt localUniqueId = Q_BASIC_ATOMIC_INITIALIZER(1); + +static inline QByteArray makeKey(int id) { + return "/qwsshm_" + QByteArray::number(id, 16); } +#endif +QWSSharedMemory::QWSSharedMemory() + : shmId(-1), shmBase(0), shmSize(0) +#ifdef QT_POSIX_IPC + , hand(-1) +#endif +{ +} QWSSharedMemory::~QWSSharedMemory() { detach(); } -/* - man page says: - On Linux, it is possible to attach a shared memory segment even if it - is already marked to be deleted. However, POSIX.1-2001 does not spec- - ify this behaviour and many other implementations do not support it. -*/ - bool QWSSharedMemory::create(int size) { if (shmId != -1) detach(); - shmId = shmget(IPC_PRIVATE, size, IPC_CREAT|0600); +#ifndef QT_POSIX_IPC + shmId = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600); +#else + // ### generate really unique IDs + shmId = (getpid() << 16) + (localUniqueId.fetchAndAddRelaxed(1) % ushort(-1)); + QByteArray shmName = makeKey(shmId); + EINTR_LOOP(hand, shm_open(shmName.constData(), O_RDWR | O_CREAT, 0660)); + if (hand != -1) { + // the size may only be set once; ignore errors + int ret; + EINTR_LOOP(ret, ftruncate(hand, size)); + if (ret == -1) + shmId = -1; + } else { + shmId = -1; + } +#endif if (shmId == -1) { #ifdef QT_SHM_DEBUG - perror("QWSSharedMemory::create allocating shared memory"); + perror("QWSSharedMemory::create():"); qWarning("Error allocating shared memory of size %d", size); #endif + detach(); return false; } - shmBase = shmat(shmId,0,0); + +#ifndef QT_POSIX_IPC + shmBase = shmat(shmId, 0, 0); + // On Linux, it is possible to attach a shared memory segment even if it + // is already marked to be deleted. However, POSIX.1-2001 does not specify + // this behaviour and many other implementations do not support it. shmctl(shmId, IPC_RMID, 0); - if (shmBase == (void*)-1) { +#else + // grab the size + QT_STATBUF st; + if (QT_FSTAT(hand, &st) != -1) { + shmSize = st.st_size; + // grab the memory + shmBase = mmap(0, shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, hand, 0); + } +#endif + if (shmBase == (void*)-1 || !shmBase) { #ifdef QT_SHM_DEBUG - perror("QWSSharedMemory::create attaching to shared memory"); + perror("QWSSharedMemory::create():"); qWarning("Error attaching to shared memory id %d", shmId); #endif - shmBase = 0; + detach(); return false; } + return true; } @@ -95,91 +146,85 @@ bool QWSSharedMemory::attach(int id) { if (shmId == id) return id != -1; - if (shmId != -1) - detach(); - shmBase = shmat(id,0,0); - if (shmBase == (void*)-1) { + detach(); + + if (id == -1) + return false; + + shmId = id; +#ifndef QT_POSIX_IPC + shmBase = shmat(shmId, 0, 0); +#else + QByteArray shmName = makeKey(shmId); + EINTR_LOOP(hand, shm_open(shmName.constData(), O_RDWR, 0660)); + if (hand != -1) { + // grab the size + QT_STATBUF st; + if (QT_FSTAT(hand, &st) != -1) { + shmSize = st.st_size; + // grab the memory + shmBase = mmap(0, shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, hand, 0); + } + } +#endif + if (shmBase == (void*)-1 || !shmBase) { #ifdef QT_SHM_DEBUG - perror("QWSSharedMemory::attach attaching to shared memory"); - qWarning("Error attaching to shared memory 0x%x of size %d", - id, size()); + perror("QWSSharedMemory::attach():"); + qWarning("Error attaching to shared memory id %d", shmId); #endif - shmBase = 0; + detach(); return false; } - shmId = id; + return true; } - -void QWSSharedMemory::detach () +void QWSSharedMemory::detach() { - if (!shmBase) - return; - shmdt (shmBase); +#ifndef QT_POSIX_IPC + if (shmBase && shmBase != (void*)-1) + shmdt(shmBase); +#else + if (shmBase && shmBase != (void*)-1) + munmap(shmBase, shmSize); + if (hand > 0) { + // get the number of current attachments + int shm_nattch = 0; + QT_STATBUF st; + if (QT_FSTAT(hand, &st) == 0) { + // subtract 2 from linkcount: one for our own open and one for the dir entry + shm_nattch = st.st_nlink - 2; + } + qt_safe_close(hand); + // if there are no attachments then unlink the shared memory + if (shm_nattch == 0) { + QByteArray shmName = makeKey(shmId); + shm_unlink(shmName.constData()); + } + } +#endif shmBase = 0; shmSize = 0; shmId = -1; } -void QWSSharedMemory::setPermissions (mode_t mode) -{ - struct shmid_ds shm; - shmctl (shmId, IPC_STAT, &shm); - shm.shm_perm.mode = mode; - shmctl (shmId, IPC_SET, &shm); -} - -int QWSSharedMemory::size () const +int QWSSharedMemory::size() const { - struct shmid_ds shm; - shmctl (shmId, IPC_STAT, &shm); - return shm.shm_segsz; -} - - -// old API - - - -QWSSharedMemory::QWSSharedMemory (int size, const QString &filename, char c) -{ - shmSize = size; - shmFile = filename; - shmBase = 0; - shmId = -1; - character = c; - key = ftok (shmFile.toLatin1().constData(), c); -} - - - -bool QWSSharedMemory::create () -{ - shmId = shmget (key, shmSize, IPC_CREAT | 0666); - return (shmId != -1); -} - -void QWSSharedMemory::destroy () -{ - if (shmId != -1) - shmctl(shmId, IPC_RMID, 0); -} - -bool QWSSharedMemory::attach () -{ - if (shmId == -1) - shmId = shmget (key, shmSize, 0); - - shmBase = shmat (shmId, 0, 0); - if ((long)shmBase == -1) - shmBase = 0; + if (shmId == -1) + return 0; + +#ifndef QT_POSIX_IPC + if (!shmSize) { + struct shmid_ds shm; + shmctl(shmId, IPC_STAT, &shm); + shmSize = shm.shm_segsz; + } +#endif - return (long)shmBase != 0; + return shmSize; } - QT_END_NAMESPACE #endif // QT_NO_QWS_MULTIPROCESS diff --git a/src/gui/embedded/qwssharedmemory_p.h b/src/gui/embedded/qwssharedmemory_p.h index 31e69c4..42ef6c8 100644 --- a/src/gui/embedded/qwssharedmemory_p.h +++ b/src/gui/embedded/qwssharedmemory_p.h @@ -53,49 +53,34 @@ // We mean it. // -#include "qplatformdefs.h" -#include "QtCore/qstring.h" +#include <qplatformdefs.h> QT_BEGIN_NAMESPACE #if !defined(QT_NO_QWS_MULTIPROCESS) -class QWSSharedMemory { +class QWSSharedMemory +{ public: - QWSSharedMemory(); ~QWSSharedMemory(); - void setPermissions(mode_t mode); - int size() const; - void *address() { return shmBase; } - - int id() const { return shmId; } - - void detach(); - bool create(int size); bool attach(int id); + void detach(); - //bool create(int size, const QString &filename, char c = 'Q'); - //bool attach(const QString &filename, char c = 'Q'); -// old API - - QWSSharedMemory(int, const QString &, char c = 'Q'); - void * base() { return address(); } - - bool create(); - void destroy(); + int id() const { return shmId; } - bool attach(); + void *address() const { return shmBase; } + int size() const; private: - void *shmBase; - int shmSize; - QString shmFile; - char character; int shmId; - key_t key; + void *shmBase; + mutable int shmSize; +#ifdef QT_POSIX_IPC + int hand; +#endif }; #endif // QT_NO_QWS_MULTIPROCESS diff --git a/src/gui/embedded/qwssignalhandler.cpp b/src/gui/embedded/qwssignalhandler.cpp index 526b363..b13a57d 100644 --- a/src/gui/embedded/qwssignalhandler.cpp +++ b/src/gui/embedded/qwssignalhandler.cpp @@ -43,25 +43,14 @@ #ifndef QT_NO_QWS_SIGNALHANDLER +#include "qlock_p.h" +#include "qwslock_p.h" + #include <sys/types.h> -#ifndef QT_NO_QWS_MULTIPROCESS -# include <sys/ipc.h> -# include <sys/sem.h> -#endif #include <signal.h> QT_BEGIN_NAMESPACE -#ifndef Q_OS_BSD4 -union semun { - int val; - struct semid_ds *buf; - unsigned short *array; - struct seminfo *__buf; -}; -#endif - - class QWSSignalHandlerPrivate : public QWSSignalHandler { public: @@ -95,42 +84,33 @@ QWSSignalHandler::QWSSignalHandler() QWSSignalHandler::~QWSSignalHandler() { -#ifndef QT_NO_QWS_MULTIPROCESS - while (!semaphores.isEmpty()) - removeSemaphore(semaphores.last()); -#endif + clear(); } -#ifndef QT_NO_QWS_MULTIPROCESS -void QWSSignalHandler::removeSemaphore(int semno) +void QWSSignalHandler::clear() { - const int index = semaphores.lastIndexOf(semno); - if (index != -1) { - semun semval; - semval.val = 0; - semctl(semaphores.at(index), 0, IPC_RMID, semval); - semaphores.remove(index); - } +#if !defined(QT_NO_QWS_MULTIPROCESS) + // it is safe to call d-tors directly here since, on normal exit, + // lists should be empty; otherwise, we don't care about semi-alive objects + // and the only important thing here is to unregister the system semaphores. + while (!locks.isEmpty()) + locks.takeLast()->~QLock(); + while (!wslocks.isEmpty()) + wslocks.takeLast()->~QWSLock(); +#endif + objects.clear(); } -#endif // QT_NO_QWS_MULTIPROCESS void QWSSignalHandler::handleSignal(int signum) { QWSSignalHandler *h = instance(); - - signal(signum, h->oldHandlers[signum]); - -#ifndef QT_NO_QWS_MULTIPROCESS - semun semval; - semval.val = 0; - for (int i = 0; i < h->semaphores.size(); ++i) - semctl(h->semaphores.at(i), 0, IPC_RMID, semval); -#endif - - h->objects.clear(); + if (h) { + signal(signum, h->oldHandlers[signum]); + h->clear(); + } raise(signum); } QT_END_NAMESPACE -#endif // QT_QWS_NO_SIGNALHANDLER +#endif // QT_NO_QWS_SIGNALHANDLER diff --git a/src/gui/embedded/qwssignalhandler_p.h b/src/gui/embedded/qwssignalhandler_p.h index dda9c76..217eda1 100644 --- a/src/gui/embedded/qwssignalhandler_p.h +++ b/src/gui/embedded/qwssignalhandler_p.h @@ -57,14 +57,17 @@ #ifndef QT_NO_QWS_SIGNALHANDLER -#include <QtCore/qmap.h> -#include <QtCore/qvector.h> +#include <QtCore/qhash.h> +#include <QtCore/qlist.h> #include <QtCore/qobjectcleanuphandler.h> QT_BEGIN_NAMESPACE typedef void (*qt_sighandler_t)(int); +class QLock; +class QWSLock; + class QWSSignalHandlerPrivate; class Q_GUI_EXPORT QWSSignalHandler @@ -75,17 +78,24 @@ public: ~QWSSignalHandler(); #ifndef QT_NO_QWS_MULTIPROCESS - inline void addSemaphore(int semno) { semaphores.append(semno); } - void removeSemaphore(int semno); + inline void addLock(QLock *lock) { locks.append(lock); } + inline void removeLock(QLock *lock) { locks.removeOne(lock); } + inline void addWSLock(QWSLock *wslock) { wslocks.append(wslock); } + inline void removeWSLock(QWSLock *wslock) { wslocks.removeOne(wslock); } #endif inline void addObject(QObject *object) { (void)objects.add(object); } private: QWSSignalHandler(); + + void clear(); + static void handleSignal(int signal); - QMap<int, qt_sighandler_t> oldHandlers; + + QHash<int, qt_sighandler_t> oldHandlers; #ifndef QT_NO_QWS_MULTIPROCESS - QVector<int> semaphores; + QList<QLock *> locks; + QList<QWSLock *> wslocks; #endif QObjectCleanupHandler objects; diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 96c0c05..eaa8ac2 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -451,8 +451,8 @@ static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal valu static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> &factor, qreal min, qreal minPref, qreal pref, qreal maxPref, qreal max) { - qreal lower; - qreal upper; + qreal lower = 0; + qreal upper = 0; switch (factor.first) { case QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred: diff --git a/src/gui/graphicsview/qgraphicsgridlayout.cpp b/src/gui/graphicsview/qgraphicsgridlayout.cpp index ee315fc..fb4bd32 100644 --- a/src/gui/graphicsview/qgraphicsgridlayout.cpp +++ b/src/gui/graphicsview/qgraphicsgridlayout.cpp @@ -600,6 +600,20 @@ void QGraphicsGridLayout::removeAt(int index) } /*! + \since 4.8 + + Removes the layout item \a item without destroying it. + Ownership of the item is transferred to the caller. + + \sa addItem() +*/ +void QGraphicsGridLayout::removeItem(QGraphicsLayoutItem *item) +{ + Q_D(QGraphicsGridLayout); + int index = d->engine.indexOf(item); + removeAt(index); +} +/*! \reimp */ void QGraphicsGridLayout::invalidate() diff --git a/src/gui/graphicsview/qgraphicsgridlayout.h b/src/gui/graphicsview/qgraphicsgridlayout.h index 2d5d2a8..771c8a0 100644 --- a/src/gui/graphicsview/qgraphicsgridlayout.h +++ b/src/gui/graphicsview/qgraphicsgridlayout.h @@ -109,11 +109,12 @@ public: int columnCount() const; QGraphicsLayoutItem *itemAt(int row, int column) const; - + // inherited from QGraphicsLayout int count() const; QGraphicsLayoutItem *itemAt(int index) const; void removeAt(int index); + void removeItem(QGraphicsLayoutItem *item); void invalidate(); diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 466cc47..0c218fc 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1171,24 +1171,26 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, const Q // Update focus scope item ptr in new scope. QGraphicsItem *newFocusScopeItem = subFocusItem ? subFocusItem : parentFocusScopeItem; if (newFocusScopeItem && newParent) { - if (subFocusItem) { - // Find the subFocusItem's topmost focus scope. - QGraphicsItem *ancestorScope = 0; - QGraphicsItem *p = subFocusItem->d_ptr->parent; - while (p) { - if (p->d_ptr->flags & QGraphicsItem::ItemIsFocusScope) - ancestorScope = p; - if (p->d_ptr->flags & QGraphicsItem::ItemIsPanel) - break; - p = p->d_ptr->parent; - } - if (ancestorScope) - newFocusScopeItem = ancestorScope; - } - QGraphicsItem *p = newParent; while (p) { if (p->d_ptr->flags & QGraphicsItem::ItemIsFocusScope) { + if (subFocusItem && subFocusItem != q_ptr) { + // Find the subFocusItem's topmost focus scope within the new parent's focusscope + QGraphicsItem *ancestorScope = 0; + QGraphicsItem *p2 = subFocusItem->d_ptr->parent; + while (p2 && p2 != p) { + if (p2->d_ptr->flags & QGraphicsItem::ItemIsFocusScope) + ancestorScope = p2; + if (p2->d_ptr->flags & QGraphicsItem::ItemIsPanel) + break; + if (p2 == q_ptr) + break; + p2 = p2->d_ptr->parent; + } + if (ancestorScope) + newFocusScopeItem = ancestorScope; + } + p->d_ptr->focusScopeItem = newFocusScopeItem; newFocusScopeItem->d_ptr->focusScopeItemChange(true); // Ensure the new item is no longer the subFocusItem. The @@ -1710,12 +1712,12 @@ void QGraphicsItem::setParentItem(QGraphicsItem *newParent) return; const QVariant newParentVariant(itemChange(QGraphicsItem::ItemParentChange, - qVariantFromValue<QGraphicsItem *>(newParent))); - newParent = qVariantValue<QGraphicsItem *>(newParentVariant); + QVariant::fromValue<QGraphicsItem *>(newParent))); + newParent = qvariant_cast<QGraphicsItem *>(newParentVariant); if (newParent == d_ptr->parent) return; - const QVariant thisPointerVariant(qVariantFromValue<QGraphicsItem *>(this)); + const QVariant thisPointerVariant(QVariant::fromValue<QGraphicsItem *>(this)); d_ptr->setParentItemHelper(newParent, &newParentVariant, &thisPointerVariant); } @@ -2139,7 +2141,7 @@ void QGraphicsItem::setToolTip(const QString &toolTip) */ QCursor QGraphicsItem::cursor() const { - return qVariantValue<QCursor>(d_ptr->extra(QGraphicsItemPrivate::ExtraCursor)); + return qvariant_cast<QCursor>(d_ptr->extra(QGraphicsItemPrivate::ExtraCursor)); } /*! @@ -2159,8 +2161,8 @@ QCursor QGraphicsItem::cursor() const */ void QGraphicsItem::setCursor(const QCursor &cursor) { - const QVariant cursorVariant(itemChange(ItemCursorChange, qVariantFromValue<QCursor>(cursor))); - d_ptr->setExtra(QGraphicsItemPrivate::ExtraCursor, qVariantValue<QCursor>(cursorVariant)); + const QVariant cursorVariant(itemChange(ItemCursorChange, QVariant::fromValue<QCursor>(cursor))); + d_ptr->setExtra(QGraphicsItemPrivate::ExtraCursor, qvariant_cast<QCursor>(cursorVariant)); d_ptr->hasCursor = 1; if (d_ptr->scene) { d_ptr->scene->d_func()->allItemsUseDefaultCursor = false; @@ -2286,7 +2288,7 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo // Schedule redrawing if (update) { - QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); + QGraphicsItemCache *c = (QGraphicsItemCache *)qvariant_cast<void *>(extra(ExtraCacheData)); if (c) c->purge(); if (scene) { @@ -3728,7 +3730,7 @@ void QGraphicsItem::setPos(const QPointF &pos) } // Notify the item that the position is changing. - const QVariant newPosVariant(itemChange(ItemPositionChange, qVariantFromValue<QPointF>(pos))); + const QVariant newPosVariant(itemChange(ItemPositionChange, QVariant::fromValue<QPointF>(pos))); QPointF newPos = newPosVariant.toPointF(); if (newPos == d_ptr->pos) return; @@ -4082,7 +4084,7 @@ void QGraphicsItem::setTransformOriginPoint(const QPointF &origin) if (d_ptr->flags & ItemSendsGeometryChanges) { // Notify the item that the origin point is changing. const QVariant newOriginVariant(itemChange(ItemTransformOriginPointChange, - qVariantFromValue<QPointF>(origin))); + QVariant::fromValue<QPointF>(origin))); newOrigin = newOriginVariant.toPointF(); } @@ -4101,7 +4103,7 @@ void QGraphicsItem::setTransformOriginPoint(const QPointF &origin) // Send post-notification. if (d_ptr->flags & ItemSendsGeometryChanges) - itemChange(ItemTransformOriginPointHasChanged, qVariantFromValue<QPointF>(newOrigin)); + itemChange(ItemTransformOriginPointHasChanged, QVariant::fromValue<QPointF>(newOrigin)); } /*! @@ -4363,8 +4365,8 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) } // Notify the item that the transformation matrix is changing. - const QVariant newMatrixVariant = qVariantFromValue<QMatrix>(newTransform.toAffine()); - newTransform = QTransform(qVariantValue<QMatrix>(itemChange(ItemMatrixChange, newMatrixVariant))); + const QVariant newMatrixVariant = QVariant::fromValue<QMatrix>(newTransform.toAffine()); + newTransform = QTransform(qvariant_cast<QMatrix>(itemChange(ItemMatrixChange, newMatrixVariant))); if (d_ptr->transformData->transform == newTransform) return; @@ -4372,7 +4374,7 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) d_ptr->setTransformHelper(newTransform); // Send post-notification. - itemChange(ItemTransformHasChanged, qVariantFromValue<QTransform>(newTransform)); + itemChange(ItemTransformHasChanged, QVariant::fromValue<QTransform>(newTransform)); } /*! @@ -4415,8 +4417,8 @@ void QGraphicsItem::setTransform(const QTransform &matrix, bool combine) // Notify the item that the transformation matrix is changing. const QVariant newTransformVariant(itemChange(ItemTransformChange, - qVariantFromValue<QTransform>(newTransform))); - newTransform = qVariantValue<QTransform>(newTransformVariant); + QVariant::fromValue<QTransform>(newTransform))); + newTransform = qvariant_cast<QTransform>(newTransformVariant); if (d_ptr->transformData->transform == newTransform) return; @@ -5269,7 +5271,7 @@ QRegion QGraphicsItem::boundingRegion(const QTransform &itemToDeviceTransform) c qreal QGraphicsItem::boundingRegionGranularity() const { return d_ptr->hasBoundingRegionGranularity - ? qVariantValue<qreal>(d_ptr->extra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity)) + ? qvariant_cast<qreal>(d_ptr->extra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity)) : 0; } @@ -5305,7 +5307,7 @@ void QGraphicsItem::setBoundingRegionGranularity(qreal granularity) } d_ptr->hasBoundingRegionGranularity = 1; d_ptr->setExtra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity, - qVariantFromValue<qreal>(granularity)); + QVariant::fromValue<qreal>(granularity)); } /*! @@ -5480,7 +5482,7 @@ void QGraphicsItemPrivate::removeChild(QGraphicsItem *child) */ QGraphicsItemCache *QGraphicsItemPrivate::maybeExtraItemCache() const { - return (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); + return (QGraphicsItemCache *)qvariant_cast<void *>(extra(ExtraCacheData)); } /*! @@ -5488,11 +5490,11 @@ QGraphicsItemCache *QGraphicsItemPrivate::maybeExtraItemCache() const */ QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const { - QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); + QGraphicsItemCache *c = (QGraphicsItemCache *)qvariant_cast<void *>(extra(ExtraCacheData)); if (!c) { QGraphicsItemPrivate *that = const_cast<QGraphicsItemPrivate *>(this); c = new QGraphicsItemCache; - that->setExtra(ExtraCacheData, qVariantFromValue<void *>(c)); + that->setExtra(ExtraCacheData, QVariant::fromValue<void *>(c)); } return c; } @@ -5502,7 +5504,7 @@ QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const */ void QGraphicsItemPrivate::removeExtraItemCache() { - QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); + QGraphicsItemCache *c = (QGraphicsItemCache *)qvariant_cast<void *>(extra(ExtraCacheData)); if (c) { c->purge(); delete c; @@ -7395,15 +7397,19 @@ void QGraphicsItem::updateMicroFocus() if (QWidget *fw = QApplication::focusWidget()) { if (scene()) { for (int i = 0 ; i < scene()->views().count() ; ++i) { - if (scene()->views().at(i) == fw) - if (QInputContext *inputContext = fw->inputContext()) + if (scene()->views().at(i) == fw) { + if (QInputContext *inputContext = fw->inputContext()) { inputContext->update(); - } - } #ifndef QT_NO_ACCESSIBILITY - // ##### is this correct - QAccessible::updateAccessibility(fw, 0, QAccessible::StateChanged); + // ##### is this correct + if (toGraphicsObject()) + QAccessible::updateAccessibility(toGraphicsObject(), 0, QAccessible::StateChanged); #endif + break; + } + } + } + } } #endif } @@ -8126,6 +8132,8 @@ QPen QAbstractGraphicsShapeItem::pen() const void QAbstractGraphicsShapeItem::setPen(const QPen &pen) { Q_D(QAbstractGraphicsShapeItem); + if (d->pen == pen) + return; prepareGeometryChange(); d->pen = pen; d->boundingRect = QRectF(); @@ -8156,6 +8164,8 @@ QBrush QAbstractGraphicsShapeItem::brush() const void QAbstractGraphicsShapeItem::setBrush(const QBrush &brush) { Q_D(QAbstractGraphicsShapeItem); + if (d->brush == brush) + return; d->brush = brush; update(); } @@ -9286,6 +9296,8 @@ QPen QGraphicsLineItem::pen() const void QGraphicsLineItem::setPen(const QPen &pen) { Q_D(QGraphicsLineItem); + if (d->pen == pen) + return; prepareGeometryChange(); d->pen = pen; update(); diff --git a/src/gui/graphicsview/qgraphicsitemanimation.cpp b/src/gui/graphicsview/qgraphicsitemanimation.cpp index 9761cab..104e21c 100644 --- a/src/gui/graphicsview/qgraphicsitemanimation.cpp +++ b/src/gui/graphicsview/qgraphicsitemanimation.cpp @@ -49,20 +49,20 @@ The QGraphicsItemAnimation class animates a QGraphicsItem. You can schedule changes to the item's transformation matrix at - specified steps. The QGraphicsItemAnimation class has a - current step value. When this value changes the transformations - scheduled at that step are performed. The current step of the + specified steps. The QGraphicsItemAnimation class has a + current step value. When this value changes the transformations + scheduled at that step are performed. The current step of the animation is set with the \c setStep() function. QGraphicsItemAnimation will do a simple linear interpolation - between the nearest adjacent scheduled changes to calculate the + between the nearest adjacent scheduled changes to calculate the matrix. For instance, if you set the position of an item at values 0.0 and 1.0, the animation will show the item moving in a straight - line between these positions. The same is true for scaling and + line between these positions. The same is true for scaling and rotation. It is usual to use the class with a QTimeLine. The timeline's - \l{QTimeLine::}{valueChanged()} signal is then connected to the + \l{QTimeLine::}{valueChanged()} signal is then connected to the \c setStep() slot. For example, you can set up an item for rotation by calling \c setRotationAt() for different step values. The animations timeline is set with the setTimeLine() function. @@ -286,7 +286,7 @@ QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::posList() const QList<QPair<qreal, QPointF> > list; for (int i = 0; i < d->xPosition.size(); ++i) list << QPair<qreal, QPointF>(d->xPosition.at(i).step, QPointF(d->xPosition.at(i).value, d->yPosition.at(i).value)); - + return list; } @@ -343,7 +343,7 @@ QList<QPair<qreal, qreal> > QGraphicsItemAnimation::rotationList() const QList<QPair<qreal, qreal> > list; for (int i = 0; i < d->rotation.size(); ++i) list << QPair<qreal, qreal>(d->rotation.at(i).step, d->rotation.at(i).value); - + return list; } @@ -395,7 +395,7 @@ QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::translationList() const QList<QPair<qreal, QPointF> > list; for (int i = 0; i < d->xTranslation.size(); ++i) list << QPair<qreal, QPointF>(d->xTranslation.at(i).step, QPointF(d->xTranslation.at(i).value, d->yTranslation.at(i).value)); - + return list; } @@ -447,7 +447,7 @@ QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::scaleList() const QList<QPair<qreal, QPointF> > list; for (int i = 0; i < d->horizontalScale.size(); ++i) list << QPair<qreal, QPointF>(d->horizontalScale.at(i).step, QPointF(d->horizontalScale.at(i).value, d->verticalScale.at(i).value)); - + return list; } @@ -499,7 +499,7 @@ QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::shearList() const QList<QPair<qreal, QPointF> > list; for (int i = 0; i < d->horizontalShear.size(); ++i) list << QPair<qreal, QPointF>(d->horizontalShear.at(i).step, QPointF(d->horizontalShear.at(i).value, d->verticalShear.at(i).value)); - + return list; } diff --git a/src/gui/graphicsview/qgraphicslayout.cpp b/src/gui/graphicsview/qgraphicslayout.cpp index f983955..d01c3af 100644 --- a/src/gui/graphicsview/qgraphicslayout.cpp +++ b/src/gui/graphicsview/qgraphicslayout.cpp @@ -145,7 +145,7 @@ QT_BEGIN_NAMESPACE /*! Contructs a QGraphicsLayout object. - + \a parent is passed to QGraphicsLayoutItem's constructor and the QGraphicsLayoutItem's isLayout argument is set to \e true. @@ -259,7 +259,7 @@ void QGraphicsLayout::activate() return; d->activateRecursive(this); - + // we don't call activate on a sublayout, but somebody might. // Therefore, we walk to the parentitem of the toplevel layout. QGraphicsLayoutItem *parentItem = this; @@ -367,7 +367,7 @@ void QGraphicsLayout::updateGeometry() widget. QGraphicsLayout uses this event handler to listen for layout related events such as geometry changes, layout changes or layout direction changes. - + \a e is a pointer to the event. You can reimplement this event handler to track similar events for your @@ -466,9 +466,8 @@ void QGraphicsLayout::addChildLayoutItem(QGraphicsLayoutItem *layoutItem) static bool g_instantInvalidatePropagation = false; /*! - \internal \since 4.8 - \see instantInvalidatePropagation + \sa instantInvalidatePropagation() Calling this function with \a enable set to true will enable a feature that makes propagation of invalidation up to ancestor layout items to be done in @@ -479,7 +478,7 @@ static bool g_instantInvalidatePropagation = false; invalid (not activated). This is the recommended behaviour. If not set it will also propagate up the parentLayoutItem() hierarchy, but - it will stop at the \i first \i widget it encounters, and post a layout + it will stop at the \e{first widget} it encounters, and post a layout request to the widget. When the layout request is consumed, this might cause it to continue propagation up to the parentLayoutItem() of the widget. It will continue in this fashion until it has reached a widget with @@ -496,9 +495,8 @@ void QGraphicsLayout::setInstantInvalidatePropagation(bool enable) } /*! - \internal \since 4.8 - \see setInstantInvalidatePropagation + \sa setInstantInvalidatePropagation() returns true if the complete widget/layout hierarchy is rearranged in one go. */ diff --git a/src/gui/graphicsview/qgraphicslayout_p.cpp b/src/gui/graphicsview/qgraphicslayout_p.cpp index de2bd6a..a68dd4e 100644 --- a/src/gui/graphicsview/qgraphicslayout_p.cpp +++ b/src/gui/graphicsview/qgraphicslayout_p.cpp @@ -136,13 +136,13 @@ static bool removeLayoutItemFromLayout(QGraphicsLayout *lay, QGraphicsLayoutItem /*! \internal - This function is called from subclasses to add a layout item \a layoutItem + This function is called from subclasses to add a layout item \a layoutItem to a layout. It takes care of automatically reparenting graphics items, if needed. If \a layoutItem is a is already in a layout, it will remove it from that layout. - + */ void QGraphicsLayoutPrivate::addChildLayoutItem(QGraphicsLayoutItem *layoutItem) { @@ -150,14 +150,14 @@ void QGraphicsLayoutPrivate::addChildLayoutItem(QGraphicsLayoutItem *layoutItem) if (QGraphicsLayoutItem *maybeLayout = layoutItem->parentLayoutItem()) { if (maybeLayout->isLayout()) removeLayoutItemFromLayout(static_cast<QGraphicsLayout*>(maybeLayout), layoutItem); - } + } layoutItem->setParentLayoutItem(q); if (layoutItem->isLayout()) { if (QGraphicsItem *parItem = parentItem()) { static_cast<QGraphicsLayout*>(layoutItem)->d_func()->reparentChildItems(parItem); } } else { - if (QGraphicsItem *item = layoutItem->graphicsItem()) { + if (QGraphicsItem *item = layoutItem->graphicsItem()) { QGraphicsItem *newParent = parentItem(); QGraphicsItem *oldParent = item->parentItem(); if (oldParent == newParent || !newParent) @@ -187,7 +187,7 @@ void QGraphicsLayoutPrivate::activateRecursive(QGraphicsLayoutItem *item) layout->invalidate(); // ### LOOKS SUSPICIOUSLY WRONG!!??? } } - + for (int i = layout->count() - 1; i >= 0; --i) { QGraphicsLayoutItem *childItem = layout->itemAt(i); if (childItem) @@ -199,5 +199,5 @@ void QGraphicsLayoutPrivate::activateRecursive(QGraphicsLayoutItem *item) QT_END_NAMESPACE - + #endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicslayout_p.h b/src/gui/graphicsview/qgraphicslayout_p.h index bb154ac..586cbc7 100644 --- a/src/gui/graphicsview/qgraphicslayout_p.h +++ b/src/gui/graphicsview/qgraphicslayout_p.h @@ -84,7 +84,7 @@ class QLayoutStyleInfo public: inline QLayoutStyleInfo() { invalidate(); } inline QLayoutStyleInfo(QStyle *style, QWidget *widget) - : m_valid(true), m_style(style), m_widget(widget) + : m_valid(true), m_style(style), m_widget(widget) { Q_ASSERT(style); if (widget) //### @@ -112,7 +112,7 @@ public: return m_defaultSpacing[o - 1]; } - inline qreal perItemSpacing(QSizePolicy::ControlType control1, + inline qreal perItemSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation) const { @@ -132,7 +132,7 @@ class Q_AUTOTEST_EXPORT QGraphicsLayoutPrivate : public QGraphicsLayoutItemPriva Q_DECLARE_PUBLIC(QGraphicsLayout) public: - QGraphicsLayoutPrivate() : QGraphicsLayoutItemPrivate(0, true), left(-1.0), top(-1.0), right(-1.0), bottom(-1.0), + QGraphicsLayoutPrivate() : QGraphicsLayoutItemPrivate(0, true), left(-1.0), top(-1.0), right(-1.0), bottom(-1.0), activated(true) { } void reparentChildItems(QGraphicsItem *newParent); @@ -148,7 +148,7 @@ public: QT_END_NAMESPACE - + #endif //QT_NO_GRAPHICSVIEW #endif diff --git a/src/gui/graphicsview/qgraphicslayoutitem.cpp b/src/gui/graphicsview/qgraphicslayoutitem.cpp index 963df8c..0631df8 100644 --- a/src/gui/graphicsview/qgraphicslayoutitem.cpp +++ b/src/gui/graphicsview/qgraphicslayoutitem.cpp @@ -296,10 +296,6 @@ bool QGraphicsLayoutItemPrivate::hasHeightForWidth() const bool QGraphicsLayoutItemPrivate::hasWidthForHeight() const { - // enable this code when we add QSizePolicy::hasWidthForHeight() (For 4.8) -#if 1 - return false; -#else Q_Q(const QGraphicsLayoutItem); if (isLayout) { const QGraphicsLayout *l = static_cast<const QGraphicsLayout *>(q); @@ -316,7 +312,6 @@ bool QGraphicsLayoutItemPrivate::hasWidthForHeight() const } } return q->sizePolicy().hasWidthForHeight(); -#endif } /*! @@ -710,7 +705,7 @@ void QGraphicsLayoutItem::setMaximumHeight(qreal height) is equivalent to the item's position in parent coordinates). You must reimplement this function in a subclass of QGraphicsLayoutItem to - receive geometry updates. The layout will call this function when it does a + receive geometry updates. The layout will call this function when it does a rearrangement. If \a rect is outside of the bounds of minimumSize and maximumSize, it diff --git a/src/gui/graphicsview/qgraphicslayoutitem.h b/src/gui/graphicsview/qgraphicslayoutitem.h index 50cea4b..12c1b89 100644 --- a/src/gui/graphicsview/qgraphicslayoutitem.h +++ b/src/gui/graphicsview/qgraphicslayoutitem.h @@ -118,7 +118,7 @@ protected: private: QSizeF *effectiveSizeHints(const QSizeF &constraint) const; Q_DECLARE_PRIVATE(QGraphicsLayoutItem) - + friend class QGraphicsLayout; }; diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp index 002aa8e..bffa470 100644 --- a/src/gui/graphicsview/qgraphicsproxywidget.cpp +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -266,8 +266,8 @@ void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneMouseEvent } if (!lastWidgetUnderMouse) { - QApplicationPrivate::dispatchEnterLeave(embeddedMouseGrabber ? embeddedMouseGrabber : widget, 0); - lastWidgetUnderMouse = widget; + QApplicationPrivate::dispatchEnterLeave(embeddedMouseGrabber ? embeddedMouseGrabber : receiver, 0); + lastWidgetUnderMouse = receiver; } // Map event position from us to the receiver diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 27639f9..1551944 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -245,6 +245,10 @@ #include <QtGui/qtransform.h> #include <QtGui/qinputcontext.h> #include <QtGui/qgraphicseffect.h> +#ifndef QT_NO_ACCESSIBILITY +# include <QtGui/qaccessible.h> +#endif + #include <private/qapplication_p.h> #include <private/qobject_p.h> #ifdef Q_WS_X11 @@ -837,6 +841,14 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item, if (item) focusItem = item; updateInputMethodSensitivityInViews(); + +#ifndef QT_NO_ACCESSIBILITY + if (focusItem) { + if (QGraphicsObject *focusObj = focusItem->toGraphicsObject()) { + QAccessible::updateAccessibility(focusObj, 0, QAccessible::Focus); + } + } +#endif if (item) { QFocusEvent event(QEvent::FocusIn, focusReason); sendEvent(item, &event); @@ -1627,7 +1639,8 @@ QGraphicsScene::~QGraphicsScene() Q_D(QGraphicsScene); // Remove this scene from qApp's global scene list. - qApp->d_func()->scene_list.removeAll(this); + if (!QApplicationPrivate::is_app_closing) + qApp->d_func()->scene_list.removeAll(this); clear(); @@ -2544,8 +2557,8 @@ void QGraphicsScene::addItem(QGraphicsItem *item) // Notify the item that its scene is changing, and allow the item to // react. const QVariant newSceneVariant(item->itemChange(QGraphicsItem::ItemSceneChange, - qVariantFromValue<QGraphicsScene *>(this))); - QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(newSceneVariant); + QVariant::fromValue<QGraphicsScene *>(this))); + QGraphicsScene *targetScene = qvariant_cast<QGraphicsScene *>(newSceneVariant); if (targetScene != this) { if (targetScene && item->d_ptr->scene != targetScene) targetScene->addItem(item); @@ -2956,8 +2969,8 @@ void QGraphicsScene::removeItem(QGraphicsItem *item) // Notify the item that it's scene is changing to 0, allowing the item to // react. const QVariant newSceneVariant(item->itemChange(QGraphicsItem::ItemSceneChange, - qVariantFromValue<QGraphicsScene *>(0))); - QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(newSceneVariant); + QVariant::fromValue<QGraphicsScene *>(0))); + QGraphicsScene *targetScene = qvariant_cast<QGraphicsScene *>(newSceneVariant); if (targetScene != 0 && targetScene != this) { targetScene->addItem(item); return; @@ -3462,7 +3475,8 @@ bool QGraphicsScene::event(QEvent *event) break; } case QEvent::Leave: - d->leaveScene(); + // hackieshly unpacking the viewport pointer from the leave event. + d->leaveScene(reinterpret_cast<QWidget *>(event->d)); break; case QEvent::GraphicsSceneHelp: helpEvent(static_cast<QGraphicsSceneHelpEvent *>(event)); @@ -3935,20 +3949,19 @@ bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEv Handles all actions necessary to clean up the scene when the mouse leaves the view. */ -void QGraphicsScenePrivate::leaveScene() +void QGraphicsScenePrivate::leaveScene(QWidget *viewport) { - Q_Q(QGraphicsScene); #ifndef QT_NO_TOOLTIP QToolTip::hideText(); #endif + QGraphicsView *view = qobject_cast<QGraphicsView *>(viewport->parent()); // Send HoverLeave events to all existing hover items, topmost first. - QGraphicsView *senderWidget = qobject_cast<QGraphicsView *>(q->sender()); QGraphicsSceneHoverEvent hoverEvent; - hoverEvent.setWidget(senderWidget); + hoverEvent.setWidget(viewport); - if (senderWidget) { + if (view) { QPoint cursorPos = QCursor::pos(); - hoverEvent.setScenePos(senderWidget->mapToScene(senderWidget->mapFromGlobal(cursorPos))); + hoverEvent.setScenePos(view->mapToScene(viewport->mapFromGlobal(cursorPos))); hoverEvent.setLastScenePos(hoverEvent.scenePos()); hoverEvent.setScreenPos(cursorPos); hoverEvent.setLastScreenPos(hoverEvent.screenPos()); @@ -4369,25 +4382,8 @@ static void _q_paintIntoCache(QPixmap *pix, QGraphicsItem *item, const QRegion & static inline bool transformIsSimple(const QTransform& transform) { QTransform::TransformationType type = transform.type(); - if (type == QTransform::TxNone || type == QTransform::TxTranslate) { + if (type <= QTransform::TxScale) { return true; - } else if (type == QTransform::TxScale) { - // Check for 0 and 180 degree rotations. - // (0 might happen after 4 rotations of 90 degrees). - qreal m11 = transform.m11(); - qreal m12 = transform.m12(); - qreal m21 = transform.m21(); - qreal m22 = transform.m22(); - if (m12 == 0.0f && m21 == 0.0f) { - if (m11 == 1.0f && m22 == 1.0f) - return true; // 0 degrees - else if (m11 == -1.0f && m22 == -1.0f) - return true; // 180 degrees. - if(m11 == 1.0f && m22 == -1.0f) - return true; // 0 degrees inverted y. - else if(m11 == -1.0f && m22 == 1.0f) - return true; // 180 degrees inverted y. - } } else if (type == QTransform::TxRotate) { // Check for 90, and 270 degree rotations. qreal m11 = transform.m11(); @@ -4965,6 +4961,19 @@ void QGraphicsScenePrivate::draw(QGraphicsItem *item, QPainter *painter, const Q if (painterStateProtection || restorePainterClip) painter->restore(); + + static int drawRect = qgetenv("QT_DRAW_SCENE_ITEM_RECTS").toInt(); + if (drawRect) { + QPen oldPen = painter->pen(); + QBrush oldBrush = painter->brush(); + quintptr ptr = reinterpret_cast<quintptr>(item); + const QColor color = QColor::fromHsv(ptr % 255, 255, 255); + painter->setPen(color); + painter->setBrush(Qt::NoBrush); + painter->drawRect(adjustedItemBoundingRect(item)); + painter->setPen(oldPen); + painter->setBrush(oldBrush); + } } // Draw children in front diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 08690b4..1e5c358 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -202,7 +202,7 @@ public: bool dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent); bool itemAcceptsHoverEvents_helper(const QGraphicsItem *item) const; - void leaveScene(); + void leaveScene(QWidget *viewport); void cloneDragDropEvent(QGraphicsSceneDragDropEvent *dest, QGraphicsSceneDragDropEvent *source); diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp index 9a1c132..979ce68 100644 --- a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp @@ -50,14 +50,14 @@ QGraphicsSceneBspTreeIndex index use a BSP(Binary Space Partitioning) implementation to discover items quickly. This implementation is - very efficient for static scene. It has a depth that you can set. + very efficient for static scenes. It has a depth that you can set. The depth directly affects performance and memory usage; the latter growing exponentially with the depth of the tree. With an optimal tree depth, the index can instantly determine the locality of items, even for scenes with thousands or millions of items. This also greatly improves rendering performance. - By default, the value is 0, in which case Qt will guess a reasonable + By default, the depth value is 0, in which case Qt will guess a reasonable default depth based on the size, location and number of items in the scene. If these parameters change frequently, however, you may experience slowdowns as the index retunes the depth internally. You can avoid diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 41b07c4..548f79f 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -80,9 +80,7 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < yourself, you can call setSceneRect(). This will adjust the scroll bars' ranges appropriately. Note that although the scene supports a virtually unlimited size, the range of the scroll bars will never exceed the range of - an integer (INT_MIN, INT_MAX). When the scene is larger than the scroll - bars' values, you can choose to use translate() to navigate the scene - instead. + an integer (INT_MIN, INT_MAX). QGraphicsView visualizes the scene by calling render(). By default, the items are drawn onto the viewport by using a regular QPainter, and using @@ -101,7 +99,8 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < convenience functions rotate(), scale(), translate() or shear(). The most two common transformations are scaling, which is used to implement zooming, and rotation. QGraphicsView keeps the center of the view fixed - during a transformation. + during a transformation. Because of the scene alignment (setAligment()), + translating the view will have no visual impact. You can interact with the items on the scene by using the mouse and keyboard. QGraphicsView translates the mouse and key events into \e scene @@ -2790,6 +2789,9 @@ bool QGraphicsView::viewportEvent(QEvent *event) d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.first()); } d->useLastMouseEvent = false; + // a hack to pass a viewport pointer to the scene inside the leave event + Q_ASSERT(event->d == 0); + event->d = reinterpret_cast<QEventPrivate *>(viewport()); QApplication::sendEvent(d->scene, event); break; #ifndef QT_NO_TOOLTIP @@ -3710,7 +3712,7 @@ void QGraphicsView::drawForeground(QPainter *painter, const QRectF &rect) } /*! - \obsolete + \obsolete Draws the items \a items in the scene using \a painter, after the background and before the foreground are drawn. \a numItems is the number diff --git a/src/gui/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h index 6179313..021ab2d 100644 --- a/src/gui/graphicsview/qgraphicsview_p.h +++ b/src/gui/graphicsview/qgraphicsview_p.h @@ -62,6 +62,7 @@ #include "qgraphicssceneevent.h" #include <QtGui/qstyleoption.h> #include <private/qabstractscrollarea_p.h> +#include <private/qapplication_p.h> QT_BEGIN_NAMESPACE @@ -180,21 +181,24 @@ public: inline void dispatchPendingUpdateRequests() { -#ifndef Q_WS_MAC - // QWidget::update() works slightly different on the Mac; it's not part of - // our backing store so it needs special threatment. - if (qt_widget_private(viewport)->paintOnScreen()) - QCoreApplication::sendPostedEvents(viewport, QEvent::UpdateRequest); - else - QCoreApplication::sendPostedEvents(viewport->window(), QEvent::UpdateRequest); -#else - // At this point either HIViewSetNeedsDisplay (Carbon) or setNeedsDisplay: YES (Cocoa) - // is called, which means there's a pending update request. We want to dispatch it - // now because otherwise graphics view updates would require two - // round-trips in the event loop before the item is painted. - extern void qt_mac_dispatchPendingUpdateRequests(QWidget *); - qt_mac_dispatchPendingUpdateRequests(viewport->window()); -#endif +#ifdef Q_WS_MAC + // QWidget::update() works slightly different on the Mac without the raster engine; + // it's not part of our backing store so it needs special threatment. + if (QApplicationPrivate::graphics_system_name != QLatin1String("raster")) { + // At this point either HIViewSetNeedsDisplay (Carbon) or setNeedsDisplay: YES (Cocoa) + // is called, which means there's a pending update request. We want to dispatch it + // now because otherwise graphics view updates would require two + // round-trips in the event loop before the item is painted. + extern void qt_mac_dispatchPendingUpdateRequests(QWidget *); + qt_mac_dispatchPendingUpdateRequests(viewport->window()); + } else +#endif // !Q_WS_MAC + { + if (qt_widget_private(viewport)->paintOnScreen()) + QCoreApplication::sendPostedEvents(viewport, QEvent::UpdateRequest); + else + QCoreApplication::sendPostedEvents(viewport->window(), QEvent::UpdateRequest); + } } void setUpdateClip(QGraphicsItem *); diff --git a/src/gui/graphicsview/qgraphicswidget_p.cpp b/src/gui/graphicsview/qgraphicswidget_p.cpp index dc0f7c0..ca6713b 100644 --- a/src/gui/graphicsview/qgraphicswidget_p.cpp +++ b/src/gui/graphicsview/qgraphicswidget_p.cpp @@ -46,6 +46,7 @@ #include <QtCore/qdebug.h> #include <QtCore/qnumeric.h> #include "qgraphicswidget_p.h" +#include "qgraphicslayoutitem_p.h" #include "qgraphicslayout.h" #include "qgraphicsscene_p.h" #include <QtGui/qapplication.h> @@ -427,8 +428,7 @@ static qreal minimumHeightForWidth(qreal width, qreal minh, qreal maxh, bool heightForWidth = true) { qreal minimumHeightForWidth = -1; - const QSizePolicy sp = widget->layout() ? widget->layout()->sizePolicy() : widget->sizePolicy(); - const bool hasHFW = sp.hasHeightForWidth(); + const bool hasHFW = QGraphicsLayoutItemPrivate::get(widget)->hasHeightForWidth(); if (hasHFW == heightForWidth) { minimumHeightForWidth = hasHFW ? widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(width, -1)).height() @@ -517,21 +517,31 @@ static void _q_boundGeometryToSizeConstraints(const QRectF &startGeometry, qreal width = qBound(min.width(), proposedRect.width(), max.width()); qreal height = qBound(min.height(), proposedRect.height(), max.height()); - QSizePolicy sp = widget->sizePolicy(); - if (const QGraphicsLayout *l = widget->layout()) { - sp = l->sizePolicy(); - } - const bool hasHFW = sp.hasHeightForWidth(); // || sp.hasWidthForHeight(); + const bool hasHFW = QGraphicsLayoutItemPrivate::get(widget)->hasHeightForWidth(); + const bool hasWFH = QGraphicsLayoutItemPrivate::get(widget)->hasWidthForHeight(); - const bool widthChanged = proposedRect.width() < widget->size().width(); - const bool heightChanged = proposedRect.height() < widget->size().height(); + const bool widthChanged = proposedRect.width() != widget->size().width(); + const bool heightChanged = proposedRect.height() != widget->size().height(); - if (hasHFW) { + if (hasHFW || hasWFH) { if (widthChanged || heightChanged) { - const qreal minh = min.height(); - const qreal maxh = max.height(); - const qreal proposedHFW = minimumHeightForWidth(width, minh, maxh, widget); - if (proposedHFW > proposedRect.height()) { + qreal minExtent; + qreal maxExtent; + qreal constraint; + qreal proposed; + if (hasHFW) { + minExtent = min.height(); + maxExtent = max.height(); + constraint = width; + proposed = proposedRect.height(); + } else { + // width for height + minExtent = min.width(); + maxExtent = max.width(); + constraint = height; + proposed = proposedRect.width(); + } + if (minimumHeightForWidth(constraint, minExtent, maxExtent, widget, hasHFW) > proposed) { QSizeF effectiveSize = closestAcceptableSize(QSizeF(width, height), widget); width = effectiveSize.width(); height = effectiveSize.height(); @@ -859,8 +869,6 @@ void QGraphicsWidgetPrivate::setWidth(qreal w) if (q->geometry().width() == w) return; - QRectF oldGeom = q->geometry(); - q->setGeometry(QRectF(q->x(), q->y(), w, height())); } @@ -884,8 +892,6 @@ void QGraphicsWidgetPrivate::setHeight(qreal h) if (q->geometry().height() == h) return; - QRectF oldGeom = q->geometry(); - q->setGeometry(QRectF(q->x(), q->y(), width(), h)); } diff --git a/src/gui/graphicsview/qgraphicswidget_p.h b/src/gui/graphicsview/qgraphicswidget_p.h index 6ea2586..fd96fce 100644 --- a/src/gui/graphicsview/qgraphicswidget_p.h +++ b/src/gui/graphicsview/qgraphicswidget_p.h @@ -182,12 +182,12 @@ public: return (attributes & (1 << bit)) != 0; } // 32 bits - quint32 refCountInvokeRelayout : 16; quint32 attributes : 10; quint32 inSetGeometry : 1; quint32 polished: 1; quint32 inSetPos : 1; quint32 autoFillBackground : 1; + quint32 refCountInvokeRelayout : 16; quint32 padding : 2; // feel free to use // Focus diff --git a/src/gui/graphicsview/qgridlayoutengine.cpp b/src/gui/graphicsview/qgridlayoutengine.cpp index e8611e3..66b4a5b 100644 --- a/src/gui/graphicsview/qgridlayoutengine.cpp +++ b/src/gui/graphicsview/qgridlayoutengine.cpp @@ -825,6 +825,15 @@ QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const return q_items.at(index); } +int QGridLayoutEngine::indexOf(QGraphicsLayoutItem *item) const +{ + for (int i = 0; i < q_items.size(); ++i) { + if (item == q_items.at(i)->layoutItem()) + return i; + } + return -1; +} + int QGridLayoutEngine::effectiveFirstRow(Qt::Orientation orientation) const { ensureEffectiveFirstAndLastRows(); @@ -1166,7 +1175,7 @@ QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHi //constraints to find the column widths q_rowData.calculateGeometries(0, rowCount(), height, sizehint_yy.data(), sizehint_heights.data(), 0, sizehint_totalBoxes[Ver], q_infos[Ver]); - ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], styleInfo, sizehint_yy.data(), sizehint_heights.data(), Qt::Vertical); + ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], styleInfo, sizehint_yy.data(), sizehint_heights.data(), Qt::Horizontal); sizeHintCalculated = true; } } diff --git a/src/gui/graphicsview/qgridlayoutengine_p.h b/src/gui/graphicsview/qgridlayoutengine_p.h index da20da0..f947d67 100644 --- a/src/gui/graphicsview/qgridlayoutengine_p.h +++ b/src/gui/graphicsview/qgridlayoutengine_p.h @@ -341,6 +341,7 @@ public: // returns the number of items inserted, which may be less than (rowCount * columnCount) int itemCount() const; QGridLayoutItem *itemAt(int index) const; + int indexOf(QGraphicsLayoutItem *item) const; int effectiveFirstRow(Qt::Orientation orientation = Qt::Vertical) const; int effectiveLastRow(Qt::Orientation orientation = Qt::Vertical) const; diff --git a/src/gui/gui.pro b/src/gui/gui.pro index cf492d6..8f72fea 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -5,9 +5,9 @@ DEFINES += QT_BUILD_GUI_LIB QT_NO_USING_NAMESPACE win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x65000000 irix-cc*:QMAKE_CXXFLAGS += -no_prelink -ptused -!win32:!embedded:!mac:!symbian:CONFIG += x11 +!win32:!embedded:!qpa:!mac:!symbian:CONFIG += x11 -unix:QMAKE_PKGCONFIG_REQUIRES = QtCore +unix|win32-g++*:QMAKE_PKGCONFIG_REQUIRES = QtCore include(../qbase.pri) @@ -63,21 +63,24 @@ symbian { neon:*-g++* { DEFINES += QT_HAVE_NEON HEADERS += $$NEON_HEADERS - SOURCES += $$NEON_SOURCES DRAWHELPER_NEON_ASM_FILES = $$NEON_ASM - neon_compiler.commands = $$QMAKE_CXX -c -mfpu=neon - neon_compiler.commands += $(CXXFLAGS) $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} + neon_compiler.commands = $$QMAKE_CXX -c + neon_compiler.commands += $(CXXFLAGS) -mfpu=neon $(INCPATH) ${QMAKE_FILE_IN} -o ${QMAKE_FILE_OUT} neon_compiler.dependency_type = TYPE_C neon_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} - neon_compiler.input = DRAWHELPER_NEON_ASM_FILES + neon_compiler.input = DRAWHELPER_NEON_ASM_FILES NEON_SOURCES neon_compiler.variable_out = OBJECTS neon_compiler.name = compiling[neon] ${QMAKE_FILE_IN} silent:neon_compiler.commands = @echo compiling[neon] ${QMAKE_FILE_IN} && $$neon_compiler.commands QMAKE_EXTRA_COMPILERS += neon_compiler } +win32:!contains(QT_CONFIG, directwrite) { + DEFINES += QT_NO_DIRECTWRITE +} + contains(QMAKE_MAC_XARCH, no) { DEFINES += QT_NO_MAC_XARCH } else { diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index d02d050..17b7da3 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -23,6 +23,7 @@ HEADERS += \ image/qpictureformatplugin.h \ image/qpixmap.h \ image/qpixmap_raster_p.h \ + image/qpixmap_blitter_p.h \ image/qpixmapcache.h \ image/qpixmapcache_p.h \ image/qpixmapdata_p.h \ @@ -53,6 +54,7 @@ SOURCES += \ image/qiconengineplugin.cpp \ image/qmovie.cpp \ image/qpixmap_raster.cpp \ + image/qpixmap_blitter.cpp \ image/qnativeimage.cpp \ image/qimagepixmapcleanuphooks.cpp \ image/qvolatileimage.cpp @@ -63,6 +65,9 @@ win32 { else:embedded { SOURCES += image/qpixmap_qws.cpp } +else:qpa { + SOURCES += image/qpixmap_qpa.cpp +} else:x11 { HEADERS += image/qpixmap_x11_p.h SOURCES += image/qpixmap_x11.cpp diff --git a/src/gui/image/qbitmap.cpp b/src/gui/image/qbitmap.cpp index bdd52d4..c0cab83 100644 --- a/src/gui/image/qbitmap.cpp +++ b/src/gui/image/qbitmap.cpp @@ -227,6 +227,14 @@ QBitmap::~QBitmap() } /*! + \fn void QBitmap::swap(QBitmap &other) + \since 4.8 + + Swaps bitmap \a other with this bitmap. This operation is very + fast and never fails. +*/ + +/*! Returns the bitmap as a QVariant. */ QBitmap::operator QVariant() const diff --git a/src/gui/image/qbitmap.h b/src/gui/image/qbitmap.h index 1c0661e..b37eed2 100644 --- a/src/gui/image/qbitmap.h +++ b/src/gui/image/qbitmap.h @@ -63,6 +63,7 @@ public: ~QBitmap(); QBitmap &operator=(const QPixmap &); + inline void swap(QBitmap &other) { QPixmap::swap(other); } // prevent QBitmap<->QPixmap swaps operator QVariant() const; inline void clear() { fill(Qt::color0); } diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp index 07de4d3..8840a83 100644 --- a/src/gui/image/qbmphandler.cpp +++ b/src/gui/image/qbmphandler.cpp @@ -97,8 +97,10 @@ static QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf) const int BMP_OLD = 12; // old Windows/OS2 BMP size -const int BMP_WIN = 40; // new Windows BMP size +const int BMP_WIN = 40; // Windows BMP v3 size const int BMP_OS2 = 64; // new OS/2 BMP size +const int BMP_WIN4 = 108; // Windows BMP v4 size +const int BMP_WIN5 = 124; // Windows BMP v5 size const int BMP_RGB = 0; // no compression const int BMP_RLE8 = 1; // run-length encoded, 8 bits @@ -109,7 +111,7 @@ const int BMP_BITFIELDS = 3; // RGB values encoded in dat static QDataStream &operator>>(QDataStream &s, BMP_INFOHDR &bi) { s >> bi.biSize; - if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2) { + if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2 || bi.biSize == BMP_WIN4 || bi.biSize == BMP_WIN5) { s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount; s >> bi.biCompression >> bi.biSizeImage; s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter; @@ -255,7 +257,57 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int image.setDotsPerMeterY(bi.biYPelsPerMeter); if (!d->isSequential()) - d->seek(startpos + BMP_FILEHDR_SIZE + bi.biSize); // goto start of colormap + d->seek(startpos + BMP_FILEHDR_SIZE + (bi.biSize >= BMP_WIN4? BMP_WIN : bi.biSize)); // goto start of colormap + + if (bi.biSize >= BMP_WIN4 || (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32))) { + Q_ASSERT(ncols == 0); + + if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask)) + return false; + if (d->read((char *)&green_mask, sizeof(green_mask)) != sizeof(green_mask)) + return false; + if (d->read((char *)&blue_mask, sizeof(blue_mask)) != sizeof(blue_mask)) + return false; + + // Read BMP v4+ header + if (bi.biSize >= BMP_WIN4) { + int alpha_mask = 0; + int CSType = 0; + int gamma_red = 0; + int gamma_green = 0; + int gamma_blue = 0; + int endpoints[9]; + + if (d->read((char *)&alpha_mask, sizeof(alpha_mask)) != sizeof(alpha_mask)) + return false; + if (d->read((char *)&CSType, sizeof(CSType)) != sizeof(CSType)) + return false; + if (d->read((char *)&endpoints, sizeof(endpoints)) != sizeof(endpoints)) + return false; + if (d->read((char *)&gamma_red, sizeof(gamma_red)) != sizeof(gamma_red)) + return false; + if (d->read((char *)&gamma_green, sizeof(gamma_green)) != sizeof(gamma_green)) + return false; + if (d->read((char *)&gamma_blue, sizeof(gamma_blue)) != sizeof(gamma_blue)) + return false; + + if (bi.biSize == BMP_WIN5) { + qint32 intent = 0; + qint32 profileData = 0; + qint32 profileSize = 0; + qint32 reserved = 0; + + if (d->read((char *)&intent, sizeof(intent)) != sizeof(intent)) + return false; + if (d->read((char *)&profileData, sizeof(profileData)) != sizeof(profileData)) + return false; + if (d->read((char *)&profileSize, sizeof(profileSize)) != sizeof(profileSize)) + return false; + if (d->read((char *)&reserved, sizeof(reserved)) != sizeof(reserved) || reserved != 0) + return false; + } + } + } if (ncols > 0) { // read color table uchar rgb[4]; @@ -268,12 +320,6 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int return false; } } else if (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32)) { - if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask)) - return false; - if (d->read((char *)&green_mask, sizeof(green_mask)) != sizeof(green_mask)) - return false; - if (d->read((char *)&blue_mask, sizeof(blue_mask)) != sizeof(blue_mask)) - return false; red_shift = calc_shift(red_mask); red_scale = 256 / ((red_mask >> red_shift) + 1); green_shift = calc_shift(green_mask); diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp index 96a7e4f..e24087c 100644 --- a/src/gui/image/qicon.cpp +++ b/src/gui/image/qicon.cpp @@ -613,6 +613,14 @@ QIcon &QIcon::operator=(const QIcon &other) } /*! + \fn void QIcon::swap(QIcon &other) + \since 4.8 + + Swaps icon \a other with this icon. This operation is very + fast and never fails. +*/ + +/*! Returns the icon as a QVariant. */ QIcon::operator QVariant() const diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h index cdecb8b..98bb2f8 100644 --- a/src/gui/image/qicon.h +++ b/src/gui/image/qicon.h @@ -71,6 +71,12 @@ public: explicit QIcon(QIconEngineV2 *engine); ~QIcon(); QIcon &operator=(const QIcon &other); +#ifdef Q_COMPILER_RVALUE_REFS + inline QIcon &operator=(QIcon &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QIcon &other) { qSwap(d, other.d); } + operator QVariant() const; QPixmap pixmap(const QSize &size, Mode mode = Normal, State state = Off) const; diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 5c83cf6..a3378de 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -69,6 +69,7 @@ #endif #include <private/qimage_p.h> +#include <private/qfont_p.h> QT_BEGIN_NAMESPACE @@ -123,9 +124,6 @@ const QVector<QRgb> *qt_image_colortable(const QImage &image) return &image.d->colortable; } -Q_GUI_EXPORT extern int qt_defaultDpiX(); -Q_GUI_EXPORT extern int qt_defaultDpiY(); - QBasicAtomicInt qimage_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1); QImageData::QImageData() @@ -143,42 +141,6 @@ QImageData::QImageData() { } -static int depthForFormat(QImage::Format format) -{ - int depth = 0; - switch(format) { - case QImage::Format_Invalid: - case QImage::NImageFormats: - Q_ASSERT(false); - case QImage::Format_Mono: - case QImage::Format_MonoLSB: - depth = 1; - break; - case QImage::Format_Indexed8: - depth = 8; - break; - case QImage::Format_RGB32: - case QImage::Format_ARGB32: - case QImage::Format_ARGB32_Premultiplied: - depth = 32; - break; - case QImage::Format_RGB555: - case QImage::Format_RGB16: - case QImage::Format_RGB444: - case QImage::Format_ARGB4444_Premultiplied: - depth = 16; - break; - case QImage::Format_RGB666: - case QImage::Format_ARGB6666_Premultiplied: - case QImage::Format_ARGB8565_Premultiplied: - case QImage::Format_ARGB8555_Premultiplied: - case QImage::Format_RGB888: - depth = 24; - break; - } - return depth; -} - /*! \fn QImageData * QImageData::create(const QSize &size, QImage::Format format, int numColors) \internal @@ -199,7 +161,7 @@ QImageData * QImageData::create(const QSize &size, QImage::Format format, int nu uint width = size.width(); uint height = size.height(); - uint depth = depthForFormat(format); + uint depth = qt_depthForFormat(format); switch (format) { case QImage::Format_Mono: @@ -879,7 +841,7 @@ QImageData *QImageData::create(uchar *data, int width, int height, int bpl, QIm return 0; } - const int depth = depthForFormat(format); + const int depth = qt_depthForFormat(format); const int calc_bytes_per_line = ((width * depth + 31)/32) * 4; const int min_bytes_per_line = (width * depth + 7)/8; @@ -1346,6 +1308,14 @@ QImage &QImage::operator=(const QImage &image) } /*! + \fn void QImage::swap(QImage &other) + \since 4.8 + + Swaps image \a other with this image. This operation is very + fast and never fails. +*/ + +/*! \internal */ int QImage::devType() const @@ -2039,6 +2009,89 @@ void QImage::fill(uint pixel) 0, 0, d->width, d->height, d->bytes_per_line); } + +/*! + \fn void QImage::fill(Qt::GlobalColor color) + \overload + \since 4.8 + + Fills the image with the given \a color, described as a standard global + color. + */ + +void QImage::fill(Qt::GlobalColor color) +{ + fill(QColor(color)); +} + + + +/*! + \fn void QImage::fill(const QColor &color) + + \overload + + Fills the entire image with the given \a color. + + If the depth of the image is 1, the image will be filled with 1 if + \a color equals Qt::color1; it will otherwise be filled with 0. + + If the depth of the image is 8, the image will be filled with the + index corresponding the \a color in the color table if present; it + will otherwise be filled with 0. + + \since 4.8 +*/ + +void QImage::fill(const QColor &color) +{ + if (!d) + return; + detach(); + + // In case we run out of memory + if (!d) + return; + + if (d->depth == 32) { + uint pixel = color.rgba(); + if (d->format == QImage::Format_ARGB32_Premultiplied) + pixel = PREMUL(pixel); + fill((uint) pixel); + + } else if (d->depth == 16 && d->format == QImage::Format_RGB16) { + qrgb565 p(color.rgba()); + fill((uint) p.rawValue()); + + } else if (d->depth == 1) { + if (color == Qt::color1) + fill((uint) 1); + else + fill((uint) 0); + + } else if (d->depth == 8) { + uint pixel = 0; + for (int i=0; i<d->colortable.size(); ++i) { + if (color.rgba() == d->colortable.at(i)) { + pixel = i; + break; + } + } + fill(pixel); + + } else { + QPainter p(this); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(rect(), color); + } + +} + + + + + + /*! Inverts all pixel values in the image. @@ -3796,6 +3849,26 @@ void qInitImageConversions() #endif } +void qGamma_correct_back_to_linear_cs(QImage *image) +{ + extern uchar qt_pow_rgb_gamma[256]; + + // gamma correct the pixels back to linear color space... + int h = image->height(); + int w = image->width(); + + for (int y=0; y<h; ++y) { + uint *pixels = (uint *) image->scanLine(y); + for (int x=0; x<w; ++x) { + uint p = pixels[x]; + uint r = qt_pow_rgb_gamma[qRed(p)]; + uint g = qt_pow_rgb_gamma[qGreen(p)]; + uint b = qt_pow_rgb_gamma[qBlue(p)]; + pixels[x] = (r << 16) | (g << 8) | b | 0xff000000; + } + } +} + /*! Returns a copy of the image in the given \a format. @@ -6254,7 +6327,7 @@ int QImage::bitPlaneCount() const bpc = 12; break; default: - bpc = depthForFormat(d->format); + bpc = qt_depthForFormat(d->format); break; } return bpc; diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index 02ff6e7..2aeb3de 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -140,6 +140,12 @@ public: ~QImage(); QImage &operator=(const QImage &); +#ifdef Q_COMPILER_RVALUE_REFS + inline QImage &operator=(QImage &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QImage &other) { qSwap(d, other.d); } + bool isNull() const; int devType() const; @@ -210,6 +216,9 @@ public: void setColorTable(const QVector<QRgb> colors); void fill(uint pixel); + void fill(const QColor &color); + void fill(Qt::GlobalColor color); + bool hasAlphaChannel() const; void setAlphaChannel(const QImage &alphaChannel); @@ -266,12 +275,13 @@ public: QString text(const QString &key = QString()) const; void setText(const QString &key, const QString &value); - // The following functions are obsolete as of 4.1 - QString text(const char* key, const char* lang=0) const; - QList<QImageTextKeyLang> textList() const; - QStringList textLanguages() const; - QString text(const QImageTextKeyLang&) const; - void setText(const char* key, const char* lang, const QString&); +#ifdef QT_DEPRECATED + QT_DEPRECATED QString text(const char* key, const char* lang=0) const; + QT_DEPRECATED QList<QImageTextKeyLang> textList() const; + QT_DEPRECATED QStringList textLanguages() const; + QT_DEPRECATED QString text(const QImageTextKeyLang&) const; + QT_DEPRECATED void setText(const char* key, const char* lang, const QString&); +#endif #endif #ifdef QT3_SUPPORT @@ -326,6 +336,7 @@ private: QImageData *d; friend class QRasterPixmapData; + friend class QBlittablePixmapData; friend class QPixmapCacheEntry; friend Q_GUI_EXPORT qint64 qt_image_id(const QImage &image); friend const QVector<QRgb> *qt_image_colortable(const QImage &image); diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index 2a805fc..7f24377 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -111,6 +111,43 @@ struct Q_GUI_EXPORT QImageData { // internal image data }; void qInitImageConversions(); +Q_GUI_EXPORT void qGamma_correct_back_to_linear_cs(QImage *image); + +inline int qt_depthForFormat(QImage::Format format) +{ + int depth = 0; + switch(format) { + case QImage::Format_Invalid: + case QImage::NImageFormats: + Q_ASSERT(false); + case QImage::Format_Mono: + case QImage::Format_MonoLSB: + depth = 1; + break; + case QImage::Format_Indexed8: + depth = 8; + break; + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + depth = 32; + break; + case QImage::Format_RGB555: + case QImage::Format_RGB16: + case QImage::Format_RGB444: + case QImage::Format_ARGB4444_Premultiplied: + depth = 16; + break; + case QImage::Format_RGB666: + case QImage::Format_ARGB6666_Premultiplied: + case QImage::Format_ARGB8565_Premultiplied: + case QImage::Format_ARGB8555_Premultiplied: + case QImage::Format_RGB888: + depth = 24; + break; + } + return depth; +} QT_END_NAMESPACE diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index 8f86020..411e5e9 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -1092,7 +1092,7 @@ QColor QImageReader::backgroundColor() const if (!d->initHandler()) return QColor(); if (d->handler->supportsOption(QImageIOHandler::BackgroundColor)) - return qVariantValue<QColor>(d->handler->option(QImageIOHandler::BackgroundColor)); + return qvariant_cast<QColor>(d->handler->option(QImageIOHandler::BackgroundColor)); return QColor(); } diff --git a/src/gui/image/qjpeghandler.cpp b/src/gui/image/qjpeghandler.cpp index 673a6d6..8fea456 100644 --- a/src/gui/image/qjpeghandler.cpp +++ b/src/gui/image/qjpeghandler.cpp @@ -74,6 +74,11 @@ extern "C" { #endif } +#if defined(JPEG_TRUE) && !defined(HAVE_BOOLEAN) +// this jpeglib.h uses JPEG_boolean +typedef JPEG_boolean boolean; +#endif + QT_BEGIN_NAMESPACE void QT_FASTCALL convert_rgb888_to_rgb32_C(quint32 *dst, const uchar *src, int len) diff --git a/src/gui/image/qmnghandler.cpp b/src/gui/image/qmnghandler.cpp index ed6952e..179fff8 100644 --- a/src/gui/image/qmnghandler.cpp +++ b/src/gui/image/qmnghandler.cpp @@ -481,7 +481,7 @@ void QMngHandler::setOption(ImageOption option, const QVariant & value) { Q_D(QMngHandler); if (option == QImageIOHandler::BackgroundColor) - d->setBackgroundColor(qVariantValue<QColor>(value)); + d->setBackgroundColor(qvariant_cast<QColor>(value)); } /*! \reimp */ diff --git a/src/gui/image/qmovie.cpp b/src/gui/image/qmovie.cpp index 639d13c..2a6104d 100644 --- a/src/gui/image/qmovie.cpp +++ b/src/gui/image/qmovie.cpp @@ -381,10 +381,14 @@ QFrameInfo QMoviePrivate::infoForFrame(int frameNumber) QPixmap aPixmap = QPixmap::fromImage(anImage); int aDelay = reader->nextImageDelay(); return QFrameInfo(aPixmap, aDelay); - } else { + } else if (frameNumber != 0) { // We've read all frames now. Return an end marker haveReadAll = true; return QFrameInfo::endMarker(); + } else { + // No readable frames + haveReadAll = true; + return QFrameInfo(); } } diff --git a/src/gui/image/qnativeimage.cpp b/src/gui/image/qnativeimage.cpp index eac1e2c..aebcbaf 100644 --- a/src/gui/image/qnativeimage.cpp +++ b/src/gui/image/qnativeimage.cpp @@ -45,6 +45,9 @@ #include "private/qpaintengine_raster_p.h" +#include "private/qapplication_p.h" +#include "private/qgraphicssystem_p.h" + #if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) #include <qx11info_x11.h> #include <sys/ipc.h> @@ -178,15 +181,17 @@ QNativeImage::QNativeImage(int width, int height, QImage::Format format,bool /* if (ok) { xshmimg->data = (char*)shmat(xshminfo.shmid, 0, 0); xshminfo.shmaddr = xshmimg->data; - if (shmctl(xshminfo.shmid, IPC_RMID, 0) == -1) - qWarning() << "Error while marking the shared memory segment to be destroyed"; ok = (xshminfo.shmaddr != (char*)-1); if (ok) image = QImage((uchar *)xshmimg->data, width, height, format); } xshminfo.readOnly = false; - if (ok) + if (ok) { ok = XShmAttach(X11->display, &xshminfo); + XSync(X11->display, False); + if (shmctl(xshminfo.shmid, IPC_RMID, 0) == -1) + qWarning() << "Error while marking the shared memory segment to be destroyed"; + } if (!ok) { qWarning() << "QNativeImage: Unable to attach to shared memory segment."; if (xshmimg->data) { @@ -241,8 +246,21 @@ QNativeImage::QNativeImage(int width, int height, QImage::Format format, bool /* : image(width, height, format) { - uint cgflags = kCGImageAlphaNoneSkipFirst; + switch (format) { + case QImage::Format_ARGB32: + cgflags = kCGImageAlphaFirst; + break; + case QImage::Format_ARGB32_Premultiplied: + case QImage::Format_ARGB8565_Premultiplied: + case QImage::Format_ARGB6666_Premultiplied: + case QImage::Format_ARGB8555_Premultiplied: + case QImage::Format_ARGB4444_Premultiplied: + cgflags = kCGImageAlphaPremultipliedFirst; + break; + default: + break; + } #ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version cgflags |= kCGBitmapByteOrder32Host; @@ -284,7 +302,11 @@ QNativeImage::~QNativeImage() QImage::Format QNativeImage::systemFormat() { +#ifdef Q_WS_QPA + return QApplicationPrivate::platformIntegration()->screens().at(0)->format(); +#else return QImage::Format_RGB32; +#endif } #endif // platforms diff --git a/src/gui/image/qpaintengine_pic.cpp b/src/gui/image/qpaintengine_pic.cpp index 60975ef..ec3a4a0 100644 --- a/src/gui/image/qpaintengine_pic.cpp +++ b/src/gui/image/qpaintengine_pic.cpp @@ -477,8 +477,6 @@ void QPicturePaintEngine::drawImage(const QRectF &r, const QImage &image, const writeCmdLength(pos, r, false); } -Q_GUI_EXPORT extern int qt_defaultDpi(); - void QPicturePaintEngine::drawTextItem(const QPointF &p , const QTextItem &ti) { Q_D(QPicturePaintEngine); diff --git a/src/gui/image/qpicture.cpp b/src/gui/image/qpicture.cpp index 7a1ee64..5df469a 100644 --- a/src/gui/image/qpicture.cpp +++ b/src/gui/image/qpicture.cpp @@ -46,6 +46,7 @@ #include <private/qfactoryloader_p.h> #include <private/qpaintengine_pic_p.h> +#include <private/qfont_p.h> #include "qdatastream.h" #include "qfile.h" @@ -108,8 +109,6 @@ void qt_format_text(const QFont &fnt, const QRectF &_r, const char *qt_mfhdr_tag = "QPIC"; // header tag static const quint16 mfhdr_maj = 11; // major version # static const quint16 mfhdr_min = 0; // minor version # -Q_GUI_EXPORT extern int qt_defaultDpiX(); -Q_GUI_EXPORT extern int qt_defaultDpiY(); /*! Constructs an empty picture. @@ -1030,6 +1029,14 @@ QPicture& QPicture::operator=(const QPicture &p) } /*! + \fn void QPicture::swap(QPicture &other) + \since 4.8 + + Swaps picture \a other with this picture. This operation is very + fast and never fails. +*/ + +/*! \internal Constructs a QPicturePrivate diff --git a/src/gui/image/qpicture.h b/src/gui/image/qpicture.h index 49a4c47..848a6f5 100644 --- a/src/gui/image/qpicture.h +++ b/src/gui/image/qpicture.h @@ -81,6 +81,11 @@ public: void setBoundingRect(const QRect &r); QPicture& operator=(const QPicture &p); +#ifdef Q_COMPILER_RVALUE_REFS + inline QPicture &operator=(QPicture &&other) + { qSwap(d_ptr, other.d_ptr); return *this; } +#endif + inline void swap(QPicture &other) { d_ptr.swap(other.d_ptr); } void detach(); bool isDetached() const; diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index ee79416..7071579 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -71,6 +71,10 @@ # include "private/qpixmap_mac_p.h" #endif +#ifdef Q_WS_QPA +# include "qplatformintegration_qpa.h" +#endif + #if defined(Q_WS_X11) # include "qx11info_x11.h" # include <private/qt_x11_p.h> @@ -98,12 +102,26 @@ static bool qt_pixmap_thread_test() qFatal("QPixmap: Must construct a QApplication before a QPaintDevice"); return false; } -#ifndef Q_WS_WIN + if (qApp->thread() != QThread::currentThread()) { - qWarning("QPixmap: It is not safe to use pixmaps outside the GUI thread"); - return false; - } + bool fail = false; +#if defined (Q_WS_X11) + if (!QApplication::testAttribute(Qt::AA_X11InitThreads)) + fail = true; +#elif defined (Q_WS_QPA) + if (!QApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedPixmaps)) { + printf("Lighthouse plugin does not support threaded pixmaps!\n"); + fail = true; + } +#else + if (QApplicationPrivate::graphics_system_name != QLatin1String("raster")) + fail = true; #endif + if (fail) { + qWarning("QPixmap: It is not safe to use pixmaps outside the GUI thread"); + return false; + } + } return true; } @@ -112,8 +130,16 @@ void QPixmap::init(int w, int h, Type type) init(w, h, int(type)); } +extern QApplication::Type qt_appType; + void QPixmap::init(int w, int h, int type) { + if (qt_appType == QApplication::Tty) { + qWarning("QPixmap: Cannot create a QPixmap when no GUI is being used"); + data = 0; + return; + } + if ((w > 0 && h > 0) || type == QPixmapData::BitmapType) data = QPixmapData::create(w, h, (QPixmapData::PixelType) type); else @@ -440,6 +466,14 @@ QPixmap &QPixmap::operator=(const QPixmap &pixmap) } /*! + \fn void QPixmap::swap(QPixmap &other) + \since 4.8 + + Swaps pixmap \a other with this pixmap. This operation is very + fast and never fails. +*/ + +/*! Returns the pixmap as a QVariant. */ QPixmap::operator QVariant() const @@ -1178,19 +1212,20 @@ QPixmap QPixmap::grabWidget(QWidget * widget, const QRect &rect) \warning This function is X11 specific; using it is non-portable. + \warning Since 4.8, pixmaps do not have an X11 handle unless + created with \l {QPixmap::}{fromX11Pixmap()}, or if the native + graphics system is explicitly enabled. + \sa detach() + \sa QApplication::setGraphicsSystem() */ Qt::HANDLE QPixmap::handle() const { #if defined(Q_WS_X11) const QPixmapData *pd = pixmapData(); - if (pd) { - if (pd->classId() == QPixmapData::X11Class) - return static_cast<const QX11PixmapData*>(pd)->handle(); - else - qWarning("QPixmap::handle(): Pixmap is not an X11 class pixmap"); - } + if (pd && pd->classId() == QPixmapData::X11Class) + return static_cast<const QX11PixmapData*>(pd)->handle(); #endif return 0; } @@ -1625,16 +1660,6 @@ QPixmap QPixmap::transformed(const QMatrix &matrix, Qt::TransformationMode mode) {Implicit Data Sharing} documentation. QPixmap objects can also be streamed. - Depending on the system, QPixmap is stored using a RGB32 or a - premultiplied alpha format. If the image has an alpha channel, and - if the system allows, the preferred format is premultiplied alpha. - Note also that QPixmap, unlike QImage, may be hardware dependent. - On X11, Mac and Symbian, a QPixmap is stored on the server side while - a QImage is stored on the client side (on Windows, these two classes - have an equivalent internal representation, i.e. both QImage and - QPixmap are stored on the client side and don't use any GDI - resources). - Note that the pixel data in a pixmap is internal and is managed by the underlying window system. Because QPixmap is a QPaintDevice subclass, QPainter can be used to draw directly onto pixmaps. @@ -1964,6 +1989,8 @@ int QPixmap::defaultDepth() return 32; #elif defined(Q_OS_SYMBIAN) return S60->screenDepth; +#elif defined(Q_WS_QPA) + return 32; //LITE: use graphicssystem (we should do that in general) #endif } diff --git a/src/gui/image/qpixmap.h b/src/gui/image/qpixmap.h index c25f3c1..f5479e7 100644 --- a/src/gui/image/qpixmap.h +++ b/src/gui/image/qpixmap.h @@ -83,6 +83,12 @@ public: ~QPixmap(); QPixmap &operator=(const QPixmap &); +#ifdef Q_COMPILER_RVALUE_REFS + inline QPixmap &operator=(QPixmap &&other) + { qSwap(data, other.data); return *this; } +#endif + inline void swap(QPixmap &other) { qSwap(data, other.data); } + operator QVariant() const; bool isNull() const; // ### Qt 5: make inline @@ -103,8 +109,10 @@ public: QBitmap mask() const; void setMask(const QBitmap &); - QPixmap alphaChannel() const; - void setAlphaChannel(const QPixmap &); +#ifdef QT_DEPRECATED + QT_DEPRECATED QPixmap alphaChannel() const; + QT_DEPRECATED void setAlphaChannel(const QPixmap &); +#endif bool hasAlpha() const; bool hasAlphaChannel() const; @@ -177,7 +185,9 @@ public: inline void scroll(int dx, int dy, int x, int y, int width, int height, QRegion *exposed = 0); void scroll(int dx, int dy, const QRect &rect, QRegion *exposed = 0); - int serialNumber() const; +#ifdef QT_DEPRECATED + QT_DEPRECATED int serialNumber() const; +#endif qint64 cacheKey() const; bool isDetached() const; diff --git a/src/gui/image/qpixmap_blitter.cpp b/src/gui/image/qpixmap_blitter.cpp new file mode 100644 index 0000000..f3a4318 --- /dev/null +++ b/src/gui/image/qpixmap_blitter.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpixmap_blitter_p.h" + +#include <qpainter.h> +#include <qimage.h> + +#include <private/qapplication_p.h> +#include <private/qgraphicssystem_p.h> +#include <private/qblittable_p.h> + +#include <private/qdrawhelper_p.h> +#include <private/qfont_p.h> + +#ifndef QT_NO_BLITTABLE +QT_BEGIN_NAMESPACE + +static int global_ser_no = 0; + +QBlittablePixmapData::QBlittablePixmapData() + : QPixmapData(QPixmapData::PixmapType,BlitterClass), m_engine(0), m_blittable(0) +#ifdef QT_BLITTER_RASTEROVERLAY + ,m_rasterOverlay(0), m_unmergedCopy(0) +#endif //QT_BLITTER_RASTEROVERLAY +{ + setSerialNumber(++global_ser_no); +} + +QBlittablePixmapData::~QBlittablePixmapData() +{ + delete m_blittable; + delete m_engine; +#ifdef QT_BLITTER_RASTEROVERLAY + delete m_rasterOverlay; + delete m_unmergedCopy; +#endif //QT_BLITTER_RASTEROVERLAY +} + +QBlittable *QBlittablePixmapData::blittable() const +{ + if (!m_blittable) { + QBlittablePixmapData *that = const_cast<QBlittablePixmapData *>(this); + that->m_blittable = this->createBlittable(QSize(w,h)); + } + + return m_blittable; +} + +void QBlittablePixmapData::setBlittable(QBlittable *blittable) +{ + resize(blittable->size().width(),blittable->size().height()); + m_blittable = blittable; +} + +void QBlittablePixmapData::resize(int width, int height) +{ + + delete m_blittable; + m_blittable = 0; + delete m_engine; + m_engine = 0; +#ifdef Q_WS_QPA + d = QApplicationPrivate::platformIntegration()->screens().at(0)->depth(); +#endif + w = width; + h = height; + is_null = (w <= 0 || h <= 0); +} + +int QBlittablePixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + switch (metric) { + case QPaintDevice::PdmWidth: + return w; + case QPaintDevice::PdmHeight: + return h; + case QPaintDevice::PdmWidthMM: + return qRound(w * 25.4 / qt_defaultDpiX()); + case QPaintDevice::PdmHeightMM: + return qRound(h * 25.4 / qt_defaultDpiY()); + case QPaintDevice::PdmDepth: + return 32; + case QPaintDevice::PdmDpiX: // fall-through + case QPaintDevice::PdmPhysicalDpiX: + return qt_defaultDpiX(); + case QPaintDevice::PdmDpiY: // fall-through + case QPaintDevice::PdmPhysicalDpiY: + return qt_defaultDpiY(); + default: + qWarning("QRasterPixmapData::metric(): Unhandled metric type %d", metric); + break; + } + + return 0; +} + +void QBlittablePixmapData::fill(const QColor &color) +{ + //jlind: todo: change when blittables can support non opaque fillRects + if (color.alpha() == 255 && blittable()->capabilities() & QBlittable::SolidRectCapability) { + blittable()->unlock(); + blittable()->fillRect(QRectF(0,0,w,h),color); + }else { + uint pixel; + switch (blittable()->lock()->format()) { + case QImage::Format_ARGB32_Premultiplied: + pixel = PREMUL(color.rgba()); + break; + case QImage::Format_ARGB8565_Premultiplied: + pixel = qargb8565(color.rgba()).rawValue(); + break; + case QImage::Format_ARGB8555_Premultiplied: + pixel = qargb8555(color.rgba()).rawValue(); + break; + case QImage::Format_ARGB6666_Premultiplied: + pixel = qargb6666(color.rgba()).rawValue(); + break; + case QImage::Format_ARGB4444_Premultiplied: + pixel = qargb4444(color.rgba()).rawValue(); + break; + default: + pixel = color.rgba(); + break; + } + //so premultiplied formats are supported and ARGB32 and RGB32 + blittable()->lock()->fill(pixel); + } + +} + +QImage *QBlittablePixmapData::buffer() +{ + return blittable()->lock(); +} + +QImage QBlittablePixmapData::toImage() const +{ + return blittable()->lock()->copy(); +} + +bool QBlittablePixmapData::hasAlphaChannel() const +{ + return blittable()->lock()->hasAlphaChannel(); +} + +void QBlittablePixmapData::fromImage(const QImage &image, + Qt::ImageConversionFlags flags) +{ + resize(image.width(),image.height()); + markRasterOverlay(QRect(0,0,w,h)); + QImage *thisImg = buffer(); + + QImage correctFormatPic = image; + if (correctFormatPic.format() != thisImg->format()) + correctFormatPic = correctFormatPic.convertToFormat(thisImg->format(), flags); + + uchar *mem = thisImg->bits(); + const uchar *bits = correctFormatPic.bits(); + int bytesCopied = 0; + while (bytesCopied < correctFormatPic.byteCount()) { + memcpy(mem,bits,correctFormatPic.bytesPerLine()); + mem += thisImg->bytesPerLine(); + bits += correctFormatPic.bytesPerLine(); + bytesCopied+=correctFormatPic.bytesPerLine(); + } +} + +QPaintEngine *QBlittablePixmapData::paintEngine() const +{ + if (!m_engine) { + QBlittablePixmapData *that = const_cast<QBlittablePixmapData *>(this); + that->m_engine = new QBlitterPaintEngine(that); + } + return m_engine; +} + +#ifdef QT_BLITTER_RASTEROVERLAY + +static bool showRasterOverlay = !qgetenv("QT_BLITTER_RASTEROVERLAY").isEmpty(); + +void QBlittablePixmapData::mergeOverlay() +{ + if (m_unmergedCopy || !showRasterOverlay) + return; + m_unmergedCopy = new QImage(buffer()->copy()); + QPainter p(buffer()); + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + p.drawImage(0,0,*overlay()); + p.end(); +} + +void QBlittablePixmapData::unmergeOverlay() +{ + if (!m_unmergedCopy || !showRasterOverlay) + return; + QPainter p(buffer()); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.drawImage(0,0,*m_unmergedCopy); + p.end(); + + delete m_unmergedCopy; + m_unmergedCopy = 0; +} + +QImage *QBlittablePixmapData::overlay() +{ + if (!m_rasterOverlay|| + m_rasterOverlay->size() != QSize(w,h)){ + m_rasterOverlay = new QImage(w,h,QImage::Format_ARGB32_Premultiplied); + m_rasterOverlay->fill(0x00000000); + uint color = (qrand() % 11)+7; + m_overlayColor = QColor(Qt::GlobalColor(color)); + m_overlayColor.setAlpha(0x88); + + } + return m_rasterOverlay; +} + +void QBlittablePixmapData::markRasterOverlayImpl(const QRectF &rect) +{ + if (!showRasterOverlay) + return; + QRectF transformationRect = clipAndTransformRect(rect); + if(!transformationRect.isEmpty()) { + QPainter p(overlay()); + p.setBrush(m_overlayColor); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(transformationRect,QBrush(m_overlayColor)); + } +} + +void QBlittablePixmapData::unmarkRasterOverlayImpl(const QRectF &rect) +{ + if (!showRasterOverlay) + return; + QRectF transformationRect = clipAndTransformRect(rect); + if (!transformationRect.isEmpty()) { + QPainter p(overlay()); + QColor color(0x00,0x00,0x00,0x00); + p.setBrush(color); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(transformationRect,QBrush(color)); + } +} + +QRectF QBlittablePixmapData::clipAndTransformRect(const QRectF &rect) const +{ + QRectF transformationRect = rect; + paintEngine(); + if (m_engine->state()) { + transformationRect = m_engine->state()->matrix.mapRect(rect); + const QClipData *clipData = m_engine->clip(); + if (clipData) { + if (clipData->hasRectClip) { + transformationRect &= clipData->clipRect; + } else if (clipData->hasRegionClip) { + const QVector<QRect> rects = clipData->clipRegion.rects(); + for (int i = 0; i < rects.size(); i++) { + transformationRect &= rects.at(i); + } + } + } + } + return transformationRect; +} + +#endif //QT_BLITTER_RASTEROVERLAY + +QT_END_NAMESPACE + +#endif //QT_NO_BLITTABLE diff --git a/src/gui/image/qpixmap_blitter_p.h b/src/gui/image/qpixmap_blitter_p.h new file mode 100644 index 0000000..07791e5 --- /dev/null +++ b/src/gui/image/qpixmap_blitter_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPIXMAP_BLITTER_P_H +#define QPIXMAP_BLITTER_P_H + +#include <private/qpixmapdata_p.h> +#include <private/qpaintengine_blitter_p.h> + +#ifndef QT_NO_BLITTABLE +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QBlittablePixmapData : public QPixmapData +{ +// Q_DECLARE_PRIVATE(QBlittablePixmapData); +public: + QBlittablePixmapData(); + ~QBlittablePixmapData(); + + virtual QBlittable *createBlittable(const QSize &size) const = 0; + QBlittable *blittable() const; + void setBlittable(QBlittable *blittable); + + void resize(int width, int height); + int metric(QPaintDevice::PaintDeviceMetric metric) const; + void fill(const QColor &color); + QImage *buffer(); + QImage toImage() const; + bool hasAlphaChannel() const; + void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + + QPaintEngine *paintEngine() const; + + void markRasterOverlay(const QRectF &); + void markRasterOverlay(const QPointF &, const QTextItem &); + void markRasterOverlay(const QVectorPath &); + void markRasterOverlay(const QRect *rects, int rectCount); + void markRasterOverlay(const QRectF *rects, int rectCount); + void unmarkRasterOverlay(const QRectF &); + +#ifdef QT_BLITTER_RASTEROVERLAY + void mergeOverlay(); + void unmergeOverlay(); + QImage *overlay(); + +#endif //QT_BLITTER_RASTEROVERLAY +protected: + QBlitterPaintEngine *m_engine; + QBlittable *m_blittable; + +#ifdef QT_BLITTER_RASTEROVERLAY + QImage *m_rasterOverlay; + QImage *m_unmergedCopy; + QColor m_overlayColor; + + void markRasterOverlayImpl(const QRectF &); + void unmarkRasterOverlayImpl(const QRectF &); + QRectF clipAndTransformRect(const QRectF &) const; +#endif //QT_BLITTER_RASTEROVERLAY + +}; + +inline void QBlittablePixmapData::markRasterOverlay(const QRectF &rect) +{ +#ifdef QT_BLITTER_RASTEROVERLAY + markRasterOverlayImpl(rect); +#else + Q_UNUSED(rect) +#endif +} + +inline void QBlittablePixmapData::markRasterOverlay(const QVectorPath &path) +{ +#ifdef QT_BLITTER_RASTEROVERLAY + markRasterOverlayImpl(path.convertToPainterPath().boundingRect()); +#else + Q_UNUSED(path) +#endif +} + +inline void QBlittablePixmapData::markRasterOverlay(const QPointF &pos, const QTextItem &ti) +{ +#ifdef QT_BLITTER_RASTEROVERLAY + QFontMetricsF fm(ti.font()); + QRectF rect = fm.tightBoundingRect(ti.text()); + rect.moveBottomLeft(pos); + markRasterOverlay(rect); +#else + Q_UNUSED(pos) + Q_UNUSED(ti) +#endif +} + +inline void QBlittablePixmapData::markRasterOverlay(const QRect *rects, int rectCount) +{ +#ifdef QT_BLITTER_RASTEROVERLAY + for (int i = 0; i < rectCount; i++) { + markRasterOverlay(rects[i]); + } +#else + Q_UNUSED(rects) + Q_UNUSED(rectCount) +#endif +} +inline void QBlittablePixmapData::markRasterOverlay(const QRectF *rects, int rectCount) +{ +#ifdef QT_BLITTER_RASTEROVERLAY + for (int i = 0; i < rectCount; i++) { + markRasterOverlay(rects[i]); + } +#else + Q_UNUSED(rects) + Q_UNUSED(rectCount) +#endif +} + +inline void QBlittablePixmapData::unmarkRasterOverlay(const QRectF &rect) +{ +#ifdef QT_BLITTER_RASTEROVERLAY + unmarkRasterOverlayImpl(rect); +#else + Q_UNUSED(rect) +#endif +} + +QT_END_NAMESPACE +#endif // QT_NO_BLITTABLE +#endif // QPIXMAP_BLITTER_P_H diff --git a/src/gui/image/qpixmap_mac.cpp b/src/gui/image/qpixmap_mac.cpp index 3afc724..47b6eef 100644 --- a/src/gui/image/qpixmap_mac.cpp +++ b/src/gui/image/qpixmap_mac.cpp @@ -54,6 +54,7 @@ #include <private/qpaintengine_mac_p.h> #include <private/qt_mac_p.h> #include <private/qt_cocoa_helpers_mac_p.h> +#include <private/qapplication_p.h> #include <limits.h> #include <string.h> @@ -73,12 +74,18 @@ static int qt_pixmap_serial = 0; Q_GUI_EXPORT quint32 *qt_mac_pixmap_get_base(const QPixmap *pix) { - return static_cast<QMacPixmapData*>(pix->data.data())->pixels; + if (QApplicationPrivate::graphics_system_name == QLatin1String("raster")) + return reinterpret_cast<quint32 *>(static_cast<QRasterPixmapData*>(pix->data.data())->buffer()->bits()); + else + return static_cast<QMacPixmapData*>(pix->data.data())->pixels; } Q_GUI_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix) { - return static_cast<QMacPixmapData*>(pix->data.data())->bytesPerRow; + if (QApplicationPrivate::graphics_system_name == QLatin1String("raster")) + return static_cast<QRasterPixmapData*>(pix->data.data())->buffer()->bytesPerLine(); + else + return static_cast<QMacPixmapData*>(pix->data.data())->bytesPerRow; } void qt_mac_cgimage_data_free(void *info, const void *memoryToFree, size_t) @@ -413,7 +420,11 @@ void QMacPixmapData::fill(const QColor &fillColor) *(dptr + i) = colr; } } - macSetHasAlpha(fillColor.alpha() != 255); + + // If we had an alpha channel from before, don't + // switch it off. Only go from no alpha to alpha: + if (fillColor.alpha() != 255) + macSetHasAlpha(true); } QPixmap QMacPixmapData::alphaChannel() const diff --git a/src/gui/image/qpixmap_qpa.cpp b/src/gui/image/qpixmap_qpa.cpp new file mode 100644 index 0000000..095dd3a --- /dev/null +++ b/src/gui/image/qpixmap_qpa.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qpixmap.h> +#include <private/qgraphicssystem_p.h> +#include <private/qapplication_p.h> + +QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h) +{ + return QApplicationPrivate::platformIntegration()->grabWindow(window, x, y, w, h); +} diff --git a/src/gui/image/qpixmap_raster.cpp b/src/gui/image/qpixmap_raster.cpp index db4c43c..6355222 100644 --- a/src/gui/image/qpixmap_raster.cpp +++ b/src/gui/image/qpixmap_raster.cpp @@ -41,6 +41,8 @@ #include "qpixmap.h" +#include <private/qfont_p.h> + #include "qpixmap_raster_p.h" #include "qnativeimage_p.h" #include "qimage_p.h" @@ -208,7 +210,13 @@ void QRasterPixmapData::fill(const QColor &color) else #endif toFormat = QImage::Format_ARGB32_Premultiplied; - image = QImage(image.width(), image.height(), toFormat); + + if (!image.isNull() && qt_depthForFormat(image.format()) == qt_depthForFormat(toFormat)) { + image.detach(); + image.d->format = toFormat; + } else { + image = QImage(image.width(), image.height(), toFormat); + } } switch (image.format()) { @@ -340,9 +348,6 @@ QPaintEngine* QRasterPixmapData::paintEngine() const return image.paintEngine(); } -Q_GUI_EXPORT extern int qt_defaultDpiX(); -Q_GUI_EXPORT extern int qt_defaultDpiY(); - int QRasterPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const { QImageData *d = image.d; diff --git a/src/gui/image/qpixmap_raster_symbian.cpp b/src/gui/image/qpixmap_raster_symbian.cpp index 7eb6eca..ae2682b 100644 --- a/src/gui/image/qpixmap_raster_symbian.cpp +++ b/src/gui/image/qpixmap_raster_symbian.cpp @@ -45,6 +45,7 @@ #include <private/qapplication_p.h> #include <private/qgraphicssystem_p.h> #include <private/qt_s60_p.h> +#include <private/qfont_p.h> #include <private/qpaintengine_raster_symbian_p.h> #include "qpixmap.h" diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp index 6456d03..77c2a2a 100644 --- a/src/gui/image/qpixmap_x11.cpp +++ b/src/gui/image/qpixmap_x11.cpp @@ -310,7 +310,7 @@ static int defaultScreen = -1; QPixmap member functions *****************************************************************************/ -static int qt_pixmap_serial = 0; +QBasicAtomicInt qt_pixmap_serial = Q_BASIC_ATOMIC_INITIALIZER(0); int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0; QX11PixmapData::QX11PixmapData(PixelType type) @@ -327,7 +327,7 @@ QPixmapData *QX11PixmapData::createCompatiblePixmapData() const void QX11PixmapData::resize(int width, int height) { - setSerialNumber(++qt_pixmap_serial); + setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); w = width; h = height; @@ -410,7 +410,7 @@ struct QX11AlphaDetector void QX11PixmapData::fromImage(const QImage &img, Qt::ImageConversionFlags flags) { - setSerialNumber(++qt_pixmap_serial); + setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); w = img.width(); h = img.height(); @@ -2013,7 +2013,7 @@ QPixmap QX11PixmapData::transformed(const QTransform &transform, x11Data->hd = (Qt::HANDLE)XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()), w, h, d); - x11Data->setSerialNumber(++qt_pixmap_serial); + x11Data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); #ifndef QT_NO_XRENDER if (X11->use_xrender) { @@ -2264,7 +2264,7 @@ void QX11PixmapData::copy(const QPixmapData *data, const QRect &rect) const QX11PixmapData *x11Data = static_cast<const QX11PixmapData*>(data); - setSerialNumber(++qt_pixmap_serial); + setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); flags &= ~Uninitialized; xinfo = x11Data->xinfo; @@ -2384,7 +2384,7 @@ QPixmap QPixmap::fromX11Pixmap(Qt::HANDLE pixmap, QPixmap::ShareMode mode) } QX11PixmapData *data = new QX11PixmapData(depth == 1 ? QPixmapData::BitmapType : QPixmapData::PixmapType); - data->setSerialNumber(++qt_pixmap_serial); + data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); data->flags = QX11PixmapData::Readonly; data->share_mode = mode; data->w = width; diff --git a/src/gui/image/qpixmapdata_p.h b/src/gui/image/qpixmapdata_p.h index f868bad..960eb8f 100644 --- a/src/gui/image/qpixmapdata_p.h +++ b/src/gui/image/qpixmapdata_p.h @@ -81,7 +81,8 @@ public: }; #endif enum ClassId { RasterClass, X11Class, MacClass, DirectFBClass, - OpenGLClass, OpenVGClass, RuntimeClass, CustomClass = 1024 }; + OpenGLClass, OpenVGClass, RuntimeClass, BlitterClass, + CustomClass = 1024 }; QPixmapData(PixelType pixelType, int classId); virtual ~QPixmapData(); diff --git a/src/gui/image/qpixmapdatafactory.cpp b/src/gui/image/qpixmapdatafactory.cpp index 060f7fd..ebe89e4 100644 --- a/src/gui/image/qpixmapdatafactory.cpp +++ b/src/gui/image/qpixmapdatafactory.cpp @@ -53,6 +53,9 @@ #ifdef Q_WS_MAC # include <private/qpixmap_mac_p.h> #endif +#ifdef Q_WS_QPA +# include <private/qpixmap_raster_p.h> +#endif #ifdef Q_OS_SYMBIAN # include <private/qpixmap_raster_symbian_p.h> #endif @@ -82,6 +85,8 @@ QPixmapData* QSimplePixmapDataFactory::create(QPixmapData::PixelType type) return new QRasterPixmapData(type); #elif defined(Q_WS_MAC) return new QMacPixmapData(type); +#elif defined(Q_WS_QPA) + return new QRasterPixmapData(type); #elif defined(Q_OS_SYMBIAN) return new QSymbianRasterPixmapData(type); #else diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 7f5cbe8..1714442 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -58,6 +58,29 @@ #include <pngconf.h> #endif +#if PNG_LIBPNG_VER >= 10400 && PNG_LIBPNG_VER <= 10502 \ + && defined(PNG_PEDANTIC_WARNINGS_SUPPORTED) +/* + Versions 1.4.0 to 1.5.2 of libpng declare png_longjmp_ptr to + have a noreturn attribute if PNG_PEDANTIC_WARNINGS_SUPPORTED + is enabled, but most declarations of longjmp in the wild do + not add this attribute. This causes problems when the png_jmpbuf + macro expands to calling png_set_longjmp_fn with a mismatched + longjmp, as compilers such as Clang will treat this as an error. + + To work around this we override the png_jmpbuf macro to cast + longjmp to a png_longjmp_ptr. +*/ +# undef png_jmpbuf +# ifdef PNG_SETJMP_SUPPORTED +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), (png_longjmp_ptr)longjmp, sizeof(jmp_buf))) +# else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +# endif +#endif + #ifdef Q_OS_WINCE #define CALLBACK_CALL_TYPE __cdecl #else @@ -100,6 +123,7 @@ public: float gamma; int quality; QString description; + QStringList readTexts; png_struct *png_ptr; png_info *info_ptr; @@ -108,6 +132,7 @@ public: bool readPngHeader(); bool readPngImage(QImage *image); + void readPngTexts(png_info *info); QImage::Format readImageFormat(); @@ -217,6 +242,7 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, float scre png_colorp palette = 0; int num_palette; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); + png_set_interlace_handling(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY) { // Black & White or 8-bit grayscale @@ -362,6 +388,38 @@ static void CALLBACK_CALL_TYPE qt_png_warning(png_structp /*png_ptr*/, png_const /*! \internal */ +void Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngTexts(png_info *info) +{ +#ifndef QT_NO_IMAGE_TEXT + png_textp text_ptr; + int num_text=0; + png_get_text(png_ptr, info, &text_ptr, &num_text); + + while (num_text--) { + QString key, value; + key = QString::fromLatin1(text_ptr->key); +#if defined(PNG_iTXt_SUPPORTED) + if (text_ptr->itxt_length) { + value = QString::fromUtf8(text_ptr->text, int(text_ptr->itxt_length)); + } else +#endif + { + value = QString::fromLatin1(text_ptr->text, int(text_ptr->text_length)); + } + if (!description.isEmpty()) + description += QLatin1String("\n\n"); + description += key + QLatin1String(": ") + value.simplified(); + readTexts.append(key); + readTexts.append(value); + text_ptr++; + } +#endif +} + + +/*! + \internal +*/ bool Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngHeader() { state = Error; @@ -394,35 +452,7 @@ bool Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngHeader() png_set_read_fn(png_ptr, this, iod_read_fn); png_read_info(png_ptr, info_ptr); -#ifndef QT_NO_IMAGE_TEXT - png_textp text_ptr; - int num_text=0; - png_get_text(png_ptr,info_ptr,&text_ptr,&num_text); - - while (num_text--) { - QString key, value; -#if defined(PNG_iTXt_SUPPORTED) && !defined(QT_NO_TEXTCODEC) - if (text_ptr->lang) { - QTextCodec *codec = QTextCodec::codecForName(text_ptr->lang); - if (codec) { - key = codec->toUnicode(text_ptr->lang_key); - value = codec->toUnicode(QByteArray(text_ptr->text, text_ptr->itxt_length)); - } else { - key = QString::fromLatin1(text_ptr->key); - value = QString::fromLatin1(QByteArray(text_ptr->text, int(text_ptr->text_length))); - } - } else -#endif - { - key = QString::fromLatin1(text_ptr->key); - value = QString::fromLatin1(QByteArray(text_ptr->text, int(text_ptr->text_length))); - } - if (!description.isEmpty()) - description += QLatin1String("\n\n"); - description += key + QLatin1String(": ") + value.simplified(); - text_ptr++; - } -#endif + readPngTexts(info_ptr); state = ReadHeader; return true; @@ -497,28 +527,15 @@ bool Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngImage(QImage *outImage) outImage->setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr,info_ptr)); outImage->setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr,info_ptr)); -#ifndef QT_NO_IMAGE_TEXT - png_textp text_ptr; - int num_text=0; - png_get_text(png_ptr,info_ptr,&text_ptr,&num_text); - while (num_text--) { - outImage->setText(text_ptr->key,0,QString::fromAscii(text_ptr->text)); - text_ptr++; - } + state = ReadingEnd; + png_read_end(png_ptr, end_info); - foreach (const QString &pair, description.split(QLatin1String("\n\n"))) { - int index = pair.indexOf(QLatin1Char(':')); - if (index >= 0 && pair.indexOf(QLatin1Char(' ')) < index) { - outImage->setText(QLatin1String("Description"), pair.simplified()); - } else { - QString key = pair.left(index); - outImage->setText(key, pair.mid(index + 2).simplified()); - } - } +#ifndef QT_NO_IMAGE_TEXT + readPngTexts(end_info); + for (int i = 0; i < readTexts.size()-1; i+=2) + outImage->setText(readTexts.at(i), readTexts.at(i+1)); #endif - state = ReadingEnd; - png_read_end(png_ptr, end_info); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); delete [] row_pointers; png_ptr = 0; @@ -647,29 +664,40 @@ static void set_text(const QImage &image, png_structp png_ptr, png_infop info_pt return; png_textp text_ptr = new png_text[text.size()]; + qMemSet(text_ptr, 0, text.size() * sizeof(png_text)); QMap<QString, QString>::ConstIterator it = text.constBegin(); int i = 0; while (it != text.constEnd()) { - QString t = it.value(); - if (t.length() < 40) - text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE; - else - text_ptr[i].compression = PNG_TEXT_COMPRESSION_zTXt; text_ptr[i].key = qstrdup(it.key().left(79).toLatin1().constData()); + bool noCompress = (it.value().length() < 40); -#ifndef PNG_iTXt_SUPPORTED - QByteArray value = it.value().toLatin1(); - text_ptr[i].text = qstrdup(value.constData()); - text_ptr[i].text_length = value.size(); -#else - QByteArray value = it.value().toUtf8(); - text_ptr[i].text = qstrdup(value.constData()); - text_ptr[i].text_length = 0; - text_ptr[i].itxt_length = value.size(); - text_ptr[i].lang = const_cast<char*>("UTF-8"); - text_ptr[i].lang_key = qstrdup(it.key().toUtf8().constData()); +#ifdef PNG_iTXt_SUPPORTED + bool needsItxt = false; + foreach(const QChar c, it.value()) { + uchar ch = c.cell(); + if (c.row() || (ch < 0x20 && ch != '\n') || (ch > 0x7e && ch < 0xa0)) { + needsItxt = true; + break; + } + } + + if (needsItxt) { + text_ptr[i].compression = noCompress ? PNG_ITXT_COMPRESSION_NONE : PNG_ITXT_COMPRESSION_zTXt; + QByteArray value = it.value().toUtf8(); + text_ptr[i].text = qstrdup(value.constData()); + text_ptr[i].itxt_length = value.size(); + text_ptr[i].lang = const_cast<char*>("UTF-8"); + text_ptr[i].lang_key = qstrdup(it.key().toUtf8().constData()); + } + else #endif + { + text_ptr[i].compression = noCompress ? PNG_TEXT_COMPRESSION_NONE : PNG_TEXT_COMPRESSION_zTXt; + QByteArray value = it.value().toLatin1(); + text_ptr[i].text = qstrdup(value.constData()); + text_ptr[i].text_length = value.size(); + } ++i; ++it; } diff --git a/src/gui/image/qtiffhandler.cpp b/src/gui/image/qtiffhandler.cpp index fd1c488..4dc9775 100644 --- a/src/gui/image/qtiffhandler.cpp +++ b/src/gui/image/qtiffhandler.cpp @@ -192,19 +192,16 @@ bool QTiffHandler::read(QImage *image) return false; } - uint16 bitsPerSample, samplesPerPixel, bitsPerPixel; - if (!TIFFGetFieldDefaulted(tiff, TIFFTAG_BITSPERSAMPLE, &bitsPerSample)) { - TIFFClose(tiff); - return false; - } - if (!TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel)) { - TIFFClose(tiff); - return false; - } - bitsPerPixel = bitsPerSample * samplesPerPixel; + // BitsPerSample defaults to 1 according to the TIFF spec. + uint16 bitPerSample; + if (!TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bitPerSample)) + bitPerSample = 1; + uint16 samplesPerPixel; // they may be e.g. grayscale with 2 samples per pixel + if (!TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel)) + samplesPerPixel = 1; bool grayscale = photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE; - if (grayscale && bitsPerPixel == 1) { + if (grayscale && bitPerSample == 1 && samplesPerPixel == 1) { if (image->size() != QSize(width, height) || image->format() != QImage::Format_Mono) *image = QImage(width, height, QImage::Format_Mono); QVector<QRgb> colortable(2); @@ -226,7 +223,7 @@ bool QTiffHandler::read(QImage *image) } } } else { - if ((grayscale || photometric == PHOTOMETRIC_PALETTE) && bitsPerPixel == 8) { + if ((grayscale || photometric == PHOTOMETRIC_PALETTE) && bitPerSample == 8 && samplesPerPixel == 1) { if (image->size() != QSize(width, height) || image->format() != QImage::Format_Indexed8) *image = QImage(width, height, QImage::Format_Indexed8); if (!image->isNull()) { diff --git a/src/gui/image/qvolatileimagedata.cpp b/src/gui/image/qvolatileimagedata.cpp index 67d1d1d..a2a7f71 100644 --- a/src/gui/image/qvolatileimagedata.cpp +++ b/src/gui/image/qvolatileimagedata.cpp @@ -68,6 +68,7 @@ QVolatileImageData::QVolatileImageData(void *, void *) } QVolatileImageData::QVolatileImageData(const QVolatileImageData &other) + : QSharedData() { image = other.image; // The detach is not mandatory here but we do it nonetheless in order to diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp index a46089b..a3779fc 100644 --- a/src/gui/image/qxpmhandler.cpp +++ b/src/gui/image/qxpmhandler.cpp @@ -54,12 +54,6 @@ #include "qplatformdefs.h" #endif -#include <stdlib.h> - -#if defined(Q_OS_WINCE) -#include "qguifunctions_wince.h" -#endif - QT_BEGIN_NAMESPACE static quint64 xpmHash(const QString &str) @@ -751,27 +745,15 @@ static const struct XPMRGBData { { QRGB(139,139, 0), "yellow4" }, { QRGB(154,205, 50), "yellowgreen" } }; -#if defined(Q_C_CALLBACKS) -extern "C" { -#endif -static int rgb_cmp(const void *d1, const void *d2) -{ - return qstricmp(((XPMRGBData *)d1)->name, ((XPMRGBData *)d2)->name); -} -#if defined(Q_C_CALLBACKS) -} -#endif +inline bool operator<(const char *name, const XPMRGBData &data) +{ return qstrcmp(name, data.name) < 0; } +inline bool operator<(const XPMRGBData &data, const char *name) +{ return qstrcmp(data.name, name) < 0; } -static bool qt_get_named_xpm_rgb(const char *name_no_space, QRgb *rgb) +static inline bool qt_get_named_xpm_rgb(const char *name_no_space, QRgb *rgb) { - XPMRGBData x; - x.name = name_no_space; - // Function bsearch() is supposed to be - // void *bsearch(const void *key, const void *base, ... - // So why (char*)? Are there broken bsearch() declarations out there? - XPMRGBData *r = (XPMRGBData *)bsearch((char *)&x, (char *)xpmRgbTbl, xpmRgbTblSize, - sizeof(XPMRGBData), rgb_cmp); - if (r) { + const XPMRGBData *r = qBinaryFind(xpmRgbTbl, xpmRgbTbl + xpmRgbTblSize, name_no_space); + if (r != xpmRgbTbl + xpmRgbTblSize) { *rgb = r->value; return true; } else { diff --git a/src/gui/inputmethod/inputmethod.pri b/src/gui/inputmethod/inputmethod.pri index 02e3e57..d439438 100644 --- a/src/gui/inputmethod/inputmethod.pri +++ b/src/gui/inputmethod/inputmethod.pri @@ -19,7 +19,7 @@ embedded { HEADERS += inputmethod/qwsinputcontext_p.h SOURCES += inputmethod/qwsinputcontext_qws.cpp } -mac:!embedded { +mac:!embedded:!qpa { HEADERS += inputmethod/qmacinputcontext_p.h SOURCES += inputmethod/qmacinputcontext_mac.cpp } diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index 79005ce..56338b2 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -874,13 +874,6 @@ void QCoeFepInputContext::applyFormat(QList<QInputMethodEvent::Attribute> *attri } } - if (attributes->size() == oldSize) { - // S60 didn't provide any format, so let's give our own instead. - attributes->append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, - 0, - m_preeditString.size(), - standardFormat(PreeditFormat))); - } } void QCoeFepInputContext::queueInputCapabilitiesChanged() diff --git a/src/gui/itemviews/itemviews.pri b/src/gui/itemviews/itemviews.pri index bbc1e98..149bfd6 100644 --- a/src/gui/itemviews/itemviews.pri +++ b/src/gui/itemviews/itemviews.pri @@ -4,6 +4,7 @@ HEADERS += \ itemviews/qabstractitemview.h \ itemviews/qabstractitemview_p.h \ itemviews/qheaderview.h \ + itemviews/qidentityproxymodel.h \ itemviews/qlistview.h \ itemviews/qlistview_p.h \ itemviews/qbsptree_p.h \ @@ -44,6 +45,7 @@ HEADERS += \ SOURCES += \ itemviews/qabstractitemview.cpp \ itemviews/qheaderview.cpp \ + itemviews/qidentityproxymodel.cpp \ itemviews/qlistview.cpp \ itemviews/qbsptree.cpp \ itemviews/qtableview.cpp \ diff --git a/src/gui/itemviews/qabstractitemdelegate.cpp b/src/gui/itemviews/qabstractitemdelegate.cpp index bdd73e0..34d3a25 100644 --- a/src/gui/itemviews/qabstractitemdelegate.cpp +++ b/src/gui/itemviews/qabstractitemdelegate.cpp @@ -362,7 +362,7 @@ bool QAbstractItemDelegate::helpEvent(QHelpEvent *event, case QEvent::ToolTip: { QHelpEvent *he = static_cast<QHelpEvent*>(event); QVariant tooltip = index.data(Qt::ToolTipRole); - if (qVariantCanConvert<QString>(tooltip)) { + if (tooltip.canConvert<QString>()) { QToolTip::showText(he->globalPos(), tooltip.toString(), view); return true; } @@ -376,7 +376,7 @@ bool QAbstractItemDelegate::helpEvent(QHelpEvent *event, case QEvent::WhatsThis: { QHelpEvent *he = static_cast<QHelpEvent*>(event); QVariant whatsthis = index.data(Qt::WhatsThisRole); - if (qVariantCanConvert<QString>(whatsthis)) { + if (whatsthis.canConvert<QString>()) { QWhatsThis::showText(he->globalPos(), whatsthis.toString(), view); return true; } diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index 624b047..ded4d63 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -60,6 +60,7 @@ #include <private/qabstractitemmodel_p.h> #ifndef QT_NO_ACCESSIBILITY #include <qaccessible.h> +#include <qaccessible2.h> #endif #include <private/qsoftkeymanager_p.h> @@ -79,6 +80,7 @@ QAbstractItemViewPrivate::QAbstractItemViewPrivate() pressedAlreadySelected(false), viewportEnteredNeeded(false), state(QAbstractItemView::NoState), + stateBeforeAnimation(QAbstractItemView::NoState), editTriggers(QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed), lastTrigger(QAbstractItemView::NoEditTriggers), tabKeyNavigation(false), @@ -644,6 +646,8 @@ void QAbstractItemView::setModel(QAbstractItemModel *model) this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(_q_rowsInserted(QModelIndex,int,int))); disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(_q_columnsAboutToBeRemoved(QModelIndex,int,int))); disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)), @@ -661,7 +665,7 @@ void QAbstractItemView::setModel(QAbstractItemModel *model) "QAbstractItemView::setModel", "A model should return the exact same index " "(including its internal id/pointer) when asked for it twice in a row."); - Q_ASSERT_X(d->model->index(0,0).parent() == QModelIndex(), + Q_ASSERT_X(!d->model->index(0,0).parent().isValid(), "QAbstractItemView::setModel", "The parent of a top level index should be invalid"); @@ -674,6 +678,8 @@ void QAbstractItemView::setModel(QAbstractItemModel *model) this, SLOT(_q_headerDataChanged())); connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int))); + connect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(_q_rowsInserted(QModelIndex,int,int))); connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); connect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), @@ -1045,16 +1051,26 @@ void QAbstractItemView::reset() { Q_D(QAbstractItemView); d->delayedReset.stop(); //make sure we stop the timer - QList<QEditorInfo>::const_iterator it = d->editors.constBegin(); - for (; it != d->editors.constEnd(); ++it) - d->releaseEditor(it->editor); - d->editors.clear(); + foreach (const QEditorInfo &info, d->indexEditorHash) { + if (info.widget) + d->releaseEditor(info.widget.data()); + } + d->editorIndexHash.clear(); + d->indexEditorHash.clear(); d->persistent.clear(); d->currentIndexSet = false; setState(NoState); setRootIndex(QModelIndex()); if (d->selectionModel) d->selectionModel->reset(); +#ifndef QT_NO_ACCESSIBILITY +#ifdef Q_WS_X11 + if (QAccessible::isActive()) { + QAccessible::queryAccessibleInterface(this)->table2Interface()->modelReset(); + QAccessible::updateAccessibility(this, 0, QAccessible::TableModelChanged); + } +#endif +#endif } /*! @@ -2342,7 +2358,7 @@ void QAbstractItemView::keyPressEvent(QKeyEvent *event) case Qt::Key_Return: // Propagate the enter if you couldn't edit the item and there are no // current editors (if there are editors, the event was most likely propagated from it). - if (!edit(currentIndex(), EditKeyPressed, event) && d->editors.isEmpty()) + if (!edit(currentIndex(), EditKeyPressed, event) && d->editorIndexHash.isEmpty()) event->ignore(); break; #else @@ -2527,7 +2543,7 @@ bool QAbstractItemView::edit(const QModelIndex &index, EditTrigger trigger, QEve if (!d->isIndexValid(index)) return false; - if (QWidget *w = (d->persistent.isEmpty() ? static_cast<QWidget*>(0) : d->editorForIndex(index).editor.data())) { + if (QWidget *w = (d->persistent.isEmpty() ? static_cast<QWidget*>(0) : d->editorForIndex(index).widget.data())) { if (w->focusPolicy() == Qt::NoFocus) return false; w->setFocus(); @@ -2587,15 +2603,15 @@ void QAbstractItemView::updateEditorData() void QAbstractItemView::updateEditorGeometries() { Q_D(QAbstractItemView); - if(d->editors.isEmpty()) + if(d->editorIndexHash.isEmpty()) return; QStyleOptionViewItemV4 option = d->viewOptionsV4(); - QList<QEditorInfo>::iterator it = d->editors.begin(); + QEditorIndexHash::iterator it = d->editorIndexHash.begin(); QWidgetList editorsToRelease; QWidgetList editorsToHide; - while (it != d->editors.end()) { - QModelIndex index = it->index; - QWidget *editor = it->editor; + while (it != d->editorIndexHash.end()) { + QModelIndex index = it.value(); + QWidget *editor = it.key(); if (index.isValid() && editor) { option.rect = visualRect(index); if (option.rect.isValid()) { @@ -2608,13 +2624,14 @@ void QAbstractItemView::updateEditorGeometries() } ++it; } else { - it = d->editors.erase(it); + d->indexEditorHash.remove(it.value()); + it = d->editorIndexHash.erase(it); editorsToRelease << editor; } } //we hide and release the editor outside of the loop because it might change the focus and try - //to change the d->editors list. + //to change the editors hashes. for (int i = 0; i < editorsToHide.count(); ++i) { editorsToHide.at(i)->hide(); } @@ -2801,7 +2818,7 @@ void QAbstractItemView::editorDestroyed(QObject *editor) */ void QAbstractItemView::setHorizontalStepsPerItem(int steps) { - Q_UNUSED(steps); + Q_UNUSED(steps) // do nothing } @@ -2830,7 +2847,7 @@ int QAbstractItemView::horizontalStepsPerItem() const */ void QAbstractItemView::setVerticalStepsPerItem(int steps) { - Q_UNUSED(steps); + Q_UNUSED(steps) // do nothing } @@ -2954,7 +2971,7 @@ int QAbstractItemView::sizeHintForRow(int row) const { Q_D(const QAbstractItemView); - if (row < 0 || row >= d->model->rowCount() || !model()) + if (row < 0 || row >= d->model->rowCount(d->root)) return -1; ensurePolished(); @@ -2965,8 +2982,8 @@ int QAbstractItemView::sizeHintForRow(int row) const QModelIndex index; for (int c = 0; c < colCount; ++c) { index = d->model->index(row, c, d->root); - if (QWidget *editor = d->editorForIndex(index).editor) - height = qMax(height, editor->size().height()); + if (QWidget *editor = d->editorForIndex(index).widget.data()) + height = qMax(height, editor->height()); int hint = d->delegateForIndex(index)->sizeHint(option, index).height(); height = qMax(height, hint); } @@ -2985,7 +3002,7 @@ int QAbstractItemView::sizeHintForColumn(int column) const { Q_D(const QAbstractItemView); - if (column < 0 || column >= d->model->columnCount() || !model()) + if (column < 0 || column >= d->model->columnCount(d->root)) return -1; ensurePolished(); @@ -2996,7 +3013,7 @@ int QAbstractItemView::sizeHintForColumn(int column) const QModelIndex index; for (int r = 0; r < rows; ++r) { index = d->model->index(r, column, d->root); - if (QWidget *editor = d->editorForIndex(index).editor) + if (QWidget *editor = d->editorForIndex(index).widget.data()) width = qMax(width, editor->sizeHint().width()); int hint = d->delegateForIndex(index)->sizeHint(option, index).width(); width = qMax(width, hint); @@ -3032,8 +3049,7 @@ void QAbstractItemView::openPersistentEditor(const QModelIndex &index) void QAbstractItemView::closePersistentEditor(const QModelIndex &index) { Q_D(QAbstractItemView); - QWidget *editor = d->editorForIndex(index).editor; - if (editor) { + if (QWidget *editor = d->editorForIndex(index).widget.data()) { if (index == selectionModel()->currentIndex()) closeEditor(editor, QAbstractItemDelegate::RevertModelCache); d->persistent.remove(editor); @@ -3097,9 +3113,11 @@ void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget QWidget* QAbstractItemView::indexWidget(const QModelIndex &index) const { Q_D(const QAbstractItemView); - if (!d->isIndexValid(index)) - return 0; - return d->editorForIndex(index).editor; + if (d->isIndexValid(index)) + if (QWidget *editor = d->editorForIndex(index).widget.data()) + return editor; + + return 0; } /*! @@ -3161,12 +3179,12 @@ void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelInde // Single item changed Q_D(QAbstractItemView); if (topLeft == bottomRight && topLeft.isValid()) { - const QEditorInfo editorInfo = d->editorForIndex(topLeft); + const QEditorInfo &editorInfo = d->editorForIndex(topLeft); //we don't update the edit data if it is static - if (!editorInfo.isStatic && editorInfo.editor) { + if (!editorInfo.isStatic && editorInfo.widget) { QAbstractItemDelegate *delegate = d->delegateForIndex(topLeft); if (delegate) { - delegate->setEditorData(editorInfo.editor, topLeft); + delegate->setEditorData(editorInfo.widget.data(), topLeft); } } if (isVisible() && !d->delayedPendingLayout) { @@ -3240,12 +3258,17 @@ void QAbstractItemView::rowsAboutToBeRemoved(const QModelIndex &parent, int star } // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes - for (int i = d->editors.size() - 1; i >= 0; --i) { - const QModelIndex index = d->editors.at(i).index; - QWidget *editor = d->editors.at(i).editor; + QEditorIndexHash::iterator i = d->editorIndexHash.begin(); + while (i != d->editorIndexHash.end()) { + const QModelIndex index = i.value(); if (index.row() >= start && index.row() <= end && d->model->parent(index) == parent) { - d->editors.removeAt(i); - d->releaseEditor(editor); + QWidget *editor = i.key(); + QEditorInfo info = d->indexEditorHash.take(index); + i = d->editorIndexHash.erase(i); + if (info.widget) + d->releaseEditor(editor); + } else { + ++i; } } } @@ -3257,12 +3280,24 @@ void QAbstractItemView::rowsAboutToBeRemoved(const QModelIndex &parent, int star rows are those under the given \a parent from \a start to \a end inclusive. */ -void QAbstractItemViewPrivate::_q_rowsRemoved(const QModelIndex &, int, int) +void QAbstractItemViewPrivate::_q_rowsRemoved(const QModelIndex &index, int start, int end) { + Q_UNUSED(index) + Q_UNUSED(start) + Q_UNUSED(end) + Q_Q(QAbstractItemView); if (q->isVisible()) q->updateEditorGeometries(); q->setState(QAbstractItemView::NoState); +#ifndef QT_NO_ACCESSIBILITY +#ifdef Q_WS_X11 + if (QAccessible::isActive()) { + QAccessible::queryAccessibleInterface(q)->table2Interface()->rowsRemoved(index, start, end); + QAccessible::updateAccessibility(q, 0, QAccessible::TableModelChanged); + } +#endif +#endif } /*! @@ -3302,17 +3337,20 @@ void QAbstractItemViewPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &par } // Remove all affected editors; this is more efficient than waiting for updateGeometries() to clean out editors for invalid indexes - QList<QEditorInfo>::iterator it = editors.begin(); - while (it != editors.end()) { - QModelIndex index = it->index; + QEditorIndexHash::iterator it = editorIndexHash.begin(); + while (it != editorIndexHash.end()) { + QModelIndex index = it.value(); if (index.column() <= start && index.column() >= end && model->parent(index) == parent) { - QWidget *editor = it->editor; - it = editors.erase(it); - releaseEditor(editor); + QWidget *editor = it.key(); + QEditorInfo info = indexEditorHash.take(it.value()); + it = editorIndexHash.erase(it); + if (info.widget) + releaseEditor(editor); } else { ++it; } } + } /*! @@ -3322,27 +3360,72 @@ void QAbstractItemViewPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &par rows are those under the given \a parent from \a start to \a end inclusive. */ -void QAbstractItemViewPrivate::_q_columnsRemoved(const QModelIndex &, int, int) +void QAbstractItemViewPrivate::_q_columnsRemoved(const QModelIndex &index, int start, int end) { + Q_UNUSED(index) + Q_UNUSED(start) + Q_UNUSED(end) + Q_Q(QAbstractItemView); if (q->isVisible()) q->updateEditorGeometries(); q->setState(QAbstractItemView::NoState); +#ifndef QT_NO_ACCESSIBILITY +#ifdef Q_WS_X11 + if (QAccessible::isActive()) { + QAccessible::queryAccessibleInterface(q)->table2Interface()->columnsRemoved(index, start, end); + QAccessible::updateAccessibility(q, 0, QAccessible::TableModelChanged); + } +#endif +#endif } + /*! \internal This slot is called when rows have been inserted. */ -void QAbstractItemViewPrivate::_q_columnsInserted(const QModelIndex &, int, int) +void QAbstractItemViewPrivate::_q_rowsInserted(const QModelIndex &index, int start, int end) { + Q_UNUSED(index) + Q_UNUSED(start) + Q_UNUSED(end) + Q_Q(QAbstractItemView); - if (q->isVisible()) - q->updateEditorGeometries(); +#ifndef QT_NO_ACCESSIBILITY +#ifdef Q_WS_X11 + if (QAccessible::isActive()) { + QAccessible::queryAccessibleInterface(q)->table2Interface()->rowsInserted(index, start, end); + QAccessible::updateAccessibility(q, 0, QAccessible::TableModelChanged); + } +#endif +#endif } +/*! + \internal + + This slot is called when columns have been inserted. +*/ +void QAbstractItemViewPrivate::_q_columnsInserted(const QModelIndex &index, int start, int end) +{ + Q_UNUSED(index) + Q_UNUSED(start) + Q_UNUSED(end) + Q_Q(QAbstractItemView); + if (q->isVisible()) + q->updateEditorGeometries(); +#ifndef QT_NO_ACCESSIBILITY +#ifdef Q_WS_X11 + if (QAccessible::isActive()) { + QAccessible::queryAccessibleInterface(q)->table2Interface()->columnsInserted(index, start, end); + QAccessible::updateAccessibility(q, 0, QAccessible::TableModelChanged); + } +#endif +#endif +} /*! \internal @@ -3360,7 +3443,16 @@ void QAbstractItemViewPrivate::_q_modelDestroyed() */ void QAbstractItemViewPrivate::_q_layoutChanged() { + Q_Q(QAbstractItemView); doDelayedItemsLayout(); +#ifndef QT_NO_ACCESSIBILITY +#ifdef Q_WS_X11 + if (QAccessible::isActive()) { + QAccessible::queryAccessibleInterface(q)->table2Interface()->modelReset(); + QAccessible::updateAccessibility(q, 0, QAccessible::TableModelChanged); + } +#endif +#endif } /*! @@ -3394,7 +3486,7 @@ void QAbstractItemView::currentChanged(const QModelIndex ¤t, const QModelI if (previous.isValid()) { QModelIndex buddy = d->model->buddy(previous); - QWidget *editor = d->editorForIndex(buddy).editor; + QWidget *editor = d->editorForIndex(buddy).widget.data(); if (editor && !d->persistent.contains(editor)) { commitData(editor); if (current.row() != previous.row()) @@ -3685,7 +3777,7 @@ QItemSelectionModel::SelectionFlags QAbstractItemView::selectionCommand(const QM QItemSelectionModel::SelectionFlags QAbstractItemViewPrivate::multiSelectionCommand( const QModelIndex &index, const QEvent *event) const { - Q_UNUSED(index); + Q_UNUSED(index) if (event) { switch (event->type()) { @@ -3920,7 +4012,7 @@ QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index, const QStyleOptionViewItem &options) { Q_Q(QAbstractItemView); - QWidget *w = editorForIndex(index).editor; + QWidget *w = editorForIndex(index).widget.data(); if (!w) { QAbstractItemDelegate *delegate = delegateForIndex(index); if (!delegate) @@ -3951,6 +4043,7 @@ QWidget *QAbstractItemViewPrivate::editor(const QModelIndex &index, #endif } } + return w; } @@ -3959,11 +4052,11 @@ void QAbstractItemViewPrivate::updateEditorData(const QModelIndex &tl, const QMo // we are counting on having relatively few editors const bool checkIndexes = tl.isValid() && br.isValid(); const QModelIndex parent = tl.parent(); - QList<QEditorInfo>::const_iterator it = editors.constBegin(); - for (; it != editors.constEnd(); ++it) { - QWidget *editor = it->editor; - const QModelIndex index = it->index; - if (it->isStatic || editor == 0 || !index.isValid() || + QIndexEditorHash::const_iterator it = indexEditorHash.constBegin(); + for (; it != indexEditorHash.constEnd(); ++it) { + QWidget *editor = it.value().widget.data(); + const QModelIndex index = it.key(); + if (it.value().isStatic || !editor || !index.isValid() || (checkIndexes && (index.row() < tl.row() || index.row() > br.row() || index.column() < tl.column() || index.column() > br.column() @@ -4036,41 +4129,40 @@ void QAbstractItemViewPrivate::checkPersistentEditorFocus() } -QEditorInfo QAbstractItemViewPrivate::editorForIndex(const QModelIndex &index) const +const QEditorInfo & QAbstractItemViewPrivate::editorForIndex(const QModelIndex &index) const { - QList<QEditorInfo>::const_iterator it = editors.constBegin(); - for (; it != editors.constEnd(); ++it) { - if (it->index == index) - return *it; - } + static QEditorInfo nullInfo; - return QEditorInfo(); + QIndexEditorHash::const_iterator it = indexEditorHash.find(index); + if (it == indexEditorHash.end()) + return nullInfo; + + return it.value(); } QModelIndex QAbstractItemViewPrivate::indexForEditor(QWidget *editor) const { - QList<QEditorInfo>::const_iterator it = editors.constBegin(); - for (; it != editors.constEnd(); ++it) { - if (it->editor == editor) - return it->index; - } - return QModelIndex(); + QEditorIndexHash::const_iterator it = editorIndexHash.find(editor); + if (it == editorIndexHash.end()) + return QModelIndex(); + + return it.value(); } void QAbstractItemViewPrivate::removeEditor(QWidget *editor) { - QList<QEditorInfo>::iterator it = editors.begin(); - for (; it != editors.end(); ) { - if (it->editor == editor) - it = editors.erase(it); - else - ++it; + QEditorIndexHash::iterator it = editorIndexHash.find(editor); + if (it != editorIndexHash.end()) + { + indexEditorHash.remove(it.value()); + editorIndexHash.erase(it); } } void QAbstractItemViewPrivate::addEditor(const QModelIndex &index, QWidget *editor, bool isStatic) { - editors.append(QEditorInfo(index, editor, isStatic)); + editorIndexHash.insert(editor, index); + indexEditorHash.insert(index, QEditorInfo(editor, isStatic)); } bool QAbstractItemViewPrivate::sendDelegateEvent(const QModelIndex &index, QEvent *event) const @@ -4097,13 +4189,13 @@ bool QAbstractItemViewPrivate::openEditor(const QModelIndex &index, QEvent *even if (!w) return false; - if (event) - QApplication::sendEvent(w->focusProxy() ? w->focusProxy() : w, event); - q->setState(QAbstractItemView::EditingState); w->show(); w->setFocus(); + if (event) + QApplication::sendEvent(w->focusProxy() ? w->focusProxy() : w, event); + return true; } diff --git a/src/gui/itemviews/qabstractitemview.h b/src/gui/itemviews/qabstractitemview.h index cb7b78d..1d0c36e 100644 --- a/src/gui/itemviews/qabstractitemview.h +++ b/src/gui/itemviews/qabstractitemview.h @@ -355,6 +355,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_columnsAboutToBeRemoved(const QModelIndex&, int, int)) Q_PRIVATE_SLOT(d_func(), void _q_columnsRemoved(const QModelIndex&, int, int)) Q_PRIVATE_SLOT(d_func(), void _q_columnsInserted(const QModelIndex&, int, int)) + Q_PRIVATE_SLOT(d_func(), void _q_rowsInserted(const QModelIndex&, int, int)) Q_PRIVATE_SLOT(d_func(), void _q_rowsRemoved(const QModelIndex&, int, int)) Q_PRIVATE_SLOT(d_func(), void _q_modelDestroyed()) Q_PRIVATE_SLOT(d_func(), void _q_layoutChanged()) diff --git a/src/gui/itemviews/qabstractitemview_p.h b/src/gui/itemviews/qabstractitemview_p.h index cadc1f7..04babde 100644 --- a/src/gui/itemviews/qabstractitemview_p.h +++ b/src/gui/itemviews/qabstractitemview_p.h @@ -70,22 +70,18 @@ QT_BEGIN_NAMESPACE -struct QEditorInfo -{ - QEditorInfo() : isStatic(false) - { - } - - QEditorInfo(const QPersistentModelIndex &i, QWidget *e, bool b) : index(i), editor(e), isStatic(b) - { - } - - QPersistentModelIndex index; - QPointer<QWidget> editor; - bool isStatic; //true when called from setIndexWidget +struct QEditorInfo { + QEditorInfo(QWidget *e, bool s): widget(QWeakPointer<QWidget>(e)), isStatic(s) {} + QEditorInfo(): isStatic(false) {} + QWeakPointer<QWidget> widget; + bool isStatic; }; +// Fast associativity between Persistent editors and indices. +typedef QHash<QWidget *, QPersistentModelIndex> QEditorIndexHash; +typedef QHash<QPersistentModelIndex, QEditorInfo> QIndexEditorHash; + typedef QPair<QRect, QModelIndex> QItemViewPaintPair; typedef QList<QItemViewPaintPair> QItemViewPaintPairs; @@ -112,6 +108,7 @@ public: void init(); virtual void _q_rowsRemoved(const QModelIndex &parent, int start, int end); + virtual void _q_rowsInserted(const QModelIndex &parent, int start, int end); virtual void _q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end); virtual void _q_columnsRemoved(const QModelIndex &parent, int start, int end); virtual void _q_columnsInserted(const QModelIndex &parent, int start, int end); @@ -135,8 +132,9 @@ public: } void stopAutoScroll() { autoScrollTimer.stop(); autoScrollCount = 0;} - - bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index); +#ifndef QT_NO_DRAGANDDROP + virtual bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index); +#endif bool droppingOnItself(QDropEvent *event, const QModelIndex &index); QWidget *editor(const QModelIndex &index, const QStyleOptionViewItem &options); @@ -247,9 +245,9 @@ public: : q->horizontalOffset(), q->verticalOffset()); } - QEditorInfo editorForIndex(const QModelIndex &index) const; + const QEditorInfo &editorForIndex(const QModelIndex &index) const; inline bool hasEditor(const QModelIndex &index) const { - return editorForIndex(index).editor != 0; + return indexEditorHash.find(index) != indexEditorHash.constEnd(); } QModelIndex indexForEditor(QWidget *editor) const; @@ -352,7 +350,8 @@ public: QAbstractItemView::SelectionMode selectionMode; QAbstractItemView::SelectionBehavior selectionBehavior; - QList<QEditorInfo> editors; + QEditorIndexHash editorIndexHash; + QIndexEditorHash indexEditorHash; QSet<QWidget*> persistent; QWidget *currentlyCommittingEditor; @@ -367,6 +366,7 @@ public: bool viewportEnteredNeeded; QAbstractItemView::State state; + QAbstractItemView::State stateBeforeAnimation; QAbstractItemView::EditTriggers editTriggers; QAbstractItemView::EditTrigger lastTrigger; diff --git a/src/gui/itemviews/qabstractproxymodel.cpp b/src/gui/itemviews/qabstractproxymodel.cpp index 4f3dbf2..f00b7ee 100644 --- a/src/gui/itemviews/qabstractproxymodel.cpp +++ b/src/gui/itemviews/qabstractproxymodel.cpp @@ -45,6 +45,9 @@ #include "qitemselectionmodel.h" #include <private/qabstractproxymodel_p.h> +#include <QtCore/QSize> +#include <QtCore/QStringList> + QT_BEGIN_NAMESPACE @@ -270,6 +273,15 @@ bool QAbstractProxyModel::setData(const QModelIndex &index, const QVariant &valu /*! \reimp */ +bool QAbstractProxyModel::setItemData(const QModelIndex &index, const QMap< int, QVariant >& roles) +{ + Q_D(QAbstractProxyModel); + return d->model->setItemData(mapToSource(index), roles); +} + +/*! + \reimp + */ bool QAbstractProxyModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) { Q_D(QAbstractProxyModel); @@ -284,6 +296,99 @@ bool QAbstractProxyModel::setHeaderData(int section, Qt::Orientation orientation return d->model->setHeaderData(sourceSection, orientation, value, role); } +/*! + \reimp + \since 4.8 + */ +QModelIndex QAbstractProxyModel::buddy(const QModelIndex &index) const +{ + Q_D(const QAbstractProxyModel); + return mapFromSource(d->model->buddy(mapToSource(index))); +} + +/*! + \reimp + \since 4.8 + */ +bool QAbstractProxyModel::canFetchMore(const QModelIndex &parent) const +{ + Q_D(const QAbstractProxyModel); + return d->model->canFetchMore(mapToSource(parent)); +} + +/*! + \reimp + \since 4.8 + */ +void QAbstractProxyModel::fetchMore(const QModelIndex &parent) +{ + Q_D(QAbstractProxyModel); + d->model->fetchMore(mapToSource(parent)); +} + +/*! + \reimp + \since 4.8 + */ +void QAbstractProxyModel::sort(int column, Qt::SortOrder order) +{ + Q_D(QAbstractProxyModel); + d->model->sort(column, order); +} + +/*! + \reimp + \since 4.8 + */ +QSize QAbstractProxyModel::span(const QModelIndex &index) const +{ + Q_D(const QAbstractProxyModel); + return d->model->span(mapToSource(index)); +} + +/*! + \reimp + \since 4.8 + */ +bool QAbstractProxyModel::hasChildren(const QModelIndex &parent) const +{ + Q_D(const QAbstractProxyModel); + return d->model->hasChildren(mapToSource(parent)); +} + +/*! + \reimp + \since 4.8 + */ +QMimeData* QAbstractProxyModel::mimeData(const QModelIndexList &indexes) const +{ + Q_D(const QAbstractProxyModel); + QModelIndexList list; + foreach(const QModelIndex &index, indexes) + list << mapToSource(index); + return d->model->mimeData(list); +} + +/*! + \reimp + \since 4.8 + */ +QStringList QAbstractProxyModel::mimeTypes() const +{ + Q_D(const QAbstractProxyModel); + return d->model->mimeTypes(); +} + +/*! + \reimp + \since 4.8 + */ +Qt::DropActions QAbstractProxyModel::supportedDropActions() const +{ + Q_D(const QAbstractProxyModel); + return d->model->supportedDropActions(); +} + QT_END_NAMESPACE #include "moc_qabstractproxymodel.cpp" diff --git a/src/gui/itemviews/qabstractproxymodel.h b/src/gui/itemviews/qabstractproxymodel.h index de68d24..1c2c8ea 100644 --- a/src/gui/itemviews/qabstractproxymodel.h +++ b/src/gui/itemviews/qabstractproxymodel.h @@ -81,8 +81,20 @@ public: Qt::ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + bool setItemData(const QModelIndex& index, const QMap<int, QVariant> &roles); bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole); + QModelIndex buddy(const QModelIndex &index) const; + bool canFetchMore(const QModelIndex &parent) const; + void fetchMore(const QModelIndex &parent); + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + QSize span(const QModelIndex &index) const; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const; + + QMimeData* mimeData(const QModelIndexList &indexes) const; + QStringList mimeTypes() const; + Qt::DropActions supportedDropActions() const; + protected: QAbstractProxyModel(QAbstractProxyModelPrivate &, QObject *parent); diff --git a/src/gui/itemviews/qfileiconprovider.cpp b/src/gui/itemviews/qfileiconprovider.cpp index 2218fbb..84081c5 100644 --- a/src/gui/itemviews/qfileiconprovider.cpp +++ b/src/gui/itemviews/qfileiconprovider.cpp @@ -234,7 +234,7 @@ QIcon QFileIconProviderPrivate::getWinIcon(const QFileInfo &fileInfo) const const QString fileExtension = QLatin1Char('.') + fileInfo.suffix().toUpper(); QString key; - if (fileInfo.isFile() && !fileInfo.isExecutable() && !fileInfo.isSymLink()) + if (fileInfo.isFile() && !fileInfo.isExecutable() && !fileInfo.isSymLink() && fileExtension != QLatin1String(".ICO")) key = QLatin1String("qt_") + fileExtension; QPixmap pixmap; diff --git a/src/gui/itemviews/qheaderview.cpp b/src/gui/itemviews/qheaderview.cpp index 5e5f916..8b3c6e8 100644 --- a/src/gui/itemviews/qheaderview.cpp +++ b/src/gui/itemviews/qheaderview.cpp @@ -2109,7 +2109,7 @@ void QHeaderView::paintEvent(QPaintEvent *e) QVariant variant = d->model->headerData(logical, d->orientation, Qt::FontRole); - if (variant.isValid() && qVariantCanConvert<QFont>(variant)) { + if (variant.isValid() && variant.canConvert<QFont>()) { QFont sectionFont = qvariant_cast<QFont>(variant); painter.setFont(sectionFont); } @@ -2489,13 +2489,13 @@ void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logical opt.icon = qvariant_cast<QPixmap>(variant); QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation, Qt::ForegroundRole); - if (qVariantCanConvert<QBrush>(foregroundBrush)) + if (foregroundBrush.canConvert<QBrush>()) opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush)); QPointF oldBO = painter->brushOrigin(); QVariant backgroundBrush = d->model->headerData(logicalIndex, d->orientation, Qt::BackgroundRole); - if (qVariantCanConvert<QBrush>(backgroundBrush)) { + if (backgroundBrush.canConvert<QBrush>()) { opt.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush)); opt.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush)); painter->setBrushOrigin(opt.rect.topLeft()); @@ -2556,7 +2556,7 @@ QSize QHeaderView::sectionSizeFromContents(int logicalIndex) const QVariant var = d->model->headerData(logicalIndex, d->orientation, Qt::FontRole); QFont fnt; - if (var.isValid() && qVariantCanConvert<QFont>(var)) + if (var.isValid() && var.canConvert<QFont>()) fnt = qvariant_cast<QFont>(var); else fnt = font(); @@ -3278,9 +3278,17 @@ void QHeaderViewPrivate::clear() void QHeaderViewPrivate::flipSortIndicator(int section) { Q_Q(QHeaderView); - bool ascending = (sortIndicatorSection != section - || sortIndicatorOrder == Qt::DescendingOrder); - q->setSortIndicator(section, ascending ? Qt::AscendingOrder : Qt::DescendingOrder); + Qt::SortOrder sortOrder; + if (sortIndicatorSection == section) { + sortOrder = (sortIndicatorOrder == Qt::DescendingOrder) ? Qt::AscendingOrder : Qt::DescendingOrder; + } else { + const QVariant value = model->headerData(section, orientation, Qt::InitialSortOrderRole); + if (value.canConvert(QVariant::Int)) + sortOrder = static_cast<Qt::SortOrder>(value.toInt()); + else + sortOrder = Qt::AscendingOrder; + } + q->setSortIndicator(section, sortOrder); } void QHeaderViewPrivate::cascadingResize(int visual, int newSize) diff --git a/src/gui/itemviews/qidentityproxymodel.cpp b/src/gui/itemviews/qidentityproxymodel.cpp new file mode 100644 index 0000000..ee36b831 --- /dev/null +++ b/src/gui/itemviews/qidentityproxymodel.cpp @@ -0,0 +1,587 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com> +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qidentityproxymodel.h" + +#ifndef QT_NO_IDENTITYPROXYMODEL + +#include "qitemselectionmodel.h" +#include <private/qabstractproxymodel_p.h> + +QT_BEGIN_NAMESPACE + +class QIdentityProxyModelPrivate : public QAbstractProxyModelPrivate +{ + QIdentityProxyModelPrivate() + : ignoreNextLayoutAboutToBeChanged(false), + ignoreNextLayoutChanged(false) + { + + } + + Q_DECLARE_PUBLIC(QIdentityProxyModel) + + bool ignoreNextLayoutAboutToBeChanged; + bool ignoreNextLayoutChanged; + QList<QPersistentModelIndex> layoutChangePersistentIndexes; + QModelIndexList proxyIndexes; + + void _q_sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void _q_sourceRowsInserted(const QModelIndex &parent, int start, int end); + void _q_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void _q_sourceRowsRemoved(const QModelIndex &parent, int start, int end); + void _q_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); + void _q_sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); + + void _q_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end); + void _q_sourceColumnsInserted(const QModelIndex &parent, int start, int end); + void _q_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void _q_sourceColumnsRemoved(const QModelIndex &parent, int start, int end); + void _q_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); + void _q_sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest); + + void _q_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last); + + void _q_sourceLayoutAboutToBeChanged(); + void _q_sourceLayoutChanged(); + void _q_sourceModelAboutToBeReset(); + void _q_sourceModelReset(); + +}; + +/*! + \since 4.8 + \class QIdentityProxyModel + \brief The QIdentityProxyModel class proxies its source model unmodified + + \ingroup model-view + + QIdentityProxyModel can be used to forward the structure of a source model exactly, with no sorting, filtering or other transformation. + This is similar in concept to an identity matrix where A.I = A. + + Because it does no sorting or filtering, this class is most suitable to proxy models which transform the data() of the source model. + For example, a proxy model could be created to define the font used, or the background colour, or the tooltip etc. This removes the + need to implement all data handling in the same class that creates the structure of the model, and can also be used to create + re-usable components. + + This also provides a way to change the data in the case where a source model is supplied by a third party which can not be modified. + + \snippet doc/src/snippets/code/src_gui_itemviews_qidentityproxymodel.cpp 0 + + \sa QAbstractProxyModel, {Model/View Programming}, QAbstractItemModel + +*/ + +/*! + Constructs an identity model with the given \a parent. +*/ +QIdentityProxyModel::QIdentityProxyModel(QObject* parent) + : QAbstractProxyModel(*new QIdentityProxyModelPrivate, parent) +{ + +} + +/*! \internal + */ +QIdentityProxyModel::QIdentityProxyModel(QIdentityProxyModelPrivate &dd, QObject* parent) + : QAbstractProxyModel(dd, parent) +{ + +} + +/*! + Destroys this identity model. +*/ +QIdentityProxyModel::~QIdentityProxyModel() +{ +} + +/*! + \reimp + */ +int QIdentityProxyModel::columnCount(const QModelIndex& parent) const +{ + Q_ASSERT(parent.isValid() ? parent.model() == this : true); + Q_D(const QIdentityProxyModel); + return d->model->columnCount(mapToSource(parent)); +} + +/*! + \reimp + */ +bool QIdentityProxyModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) +{ + Q_ASSERT(parent.isValid() ? parent.model() == this : true); + Q_D(QIdentityProxyModel); + return d->model->dropMimeData(data, action, row, column, mapToSource(parent)); +} + +/*! + \reimp + */ +QModelIndex QIdentityProxyModel::index(int row, int column, const QModelIndex& parent) const +{ + Q_ASSERT(parent.isValid() ? parent.model() == this : true); + Q_D(const QIdentityProxyModel); + if (!hasIndex(row, column, parent)) + return QModelIndex(); + const QModelIndex sourceParent = mapToSource(parent); + const QModelIndex sourceIndex = d->model->index(row, column, sourceParent); + Q_ASSERT(sourceIndex.isValid()); + return mapFromSource(sourceIndex); +} + +/*! + \reimp + */ +bool QIdentityProxyModel::insertColumns(int column, int count, const QModelIndex& parent) +{ + Q_ASSERT(parent.isValid() ? parent.model() == this : true); + Q_D(QIdentityProxyModel); + return d->model->insertColumns(column, count, mapToSource(parent)); +} + +/*! + \reimp + */ +bool QIdentityProxyModel::insertRows(int row, int count, const QModelIndex& parent) +{ + Q_ASSERT(parent.isValid() ? parent.model() == this : true); + Q_D(QIdentityProxyModel); + return d->model->insertRows(row, count, mapToSource(parent)); +} + +/*! + \reimp + */ +QModelIndex QIdentityProxyModel::mapFromSource(const QModelIndex& sourceIndex) const +{ + Q_D(const QIdentityProxyModel); + if (!d->model || !sourceIndex.isValid()) + return QModelIndex(); + + Q_ASSERT(sourceIndex.model() == d->model); + return createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer()); +} + +/*! + \reimp + */ +QItemSelection QIdentityProxyModel::mapSelectionFromSource(const QItemSelection& selection) const +{ + Q_D(const QIdentityProxyModel); + QItemSelection proxySelection; + + if (!d->model) + return proxySelection; + + QItemSelection::const_iterator it = selection.constBegin(); + const QItemSelection::const_iterator end = selection.constEnd(); + for ( ; it != end; ++it) { + Q_ASSERT(it->model() == d->model); + const QItemSelectionRange range(mapFromSource(it->topLeft()), mapFromSource(it->bottomRight())); + proxySelection.append(range); + } + + return proxySelection; +} + +/*! + \reimp + */ +QItemSelection QIdentityProxyModel::mapSelectionToSource(const QItemSelection& selection) const +{ + Q_D(const QIdentityProxyModel); + QItemSelection sourceSelection; + + if (!d->model) + return sourceSelection; + + QItemSelection::const_iterator it = selection.constBegin(); + const QItemSelection::const_iterator end = selection.constEnd(); + for ( ; it != end; ++it) { + Q_ASSERT(it->model() == this); + const QItemSelectionRange range(mapToSource(it->topLeft()), mapToSource(it->bottomRight())); + sourceSelection.append(range); + } + + return sourceSelection; +} + +/*! + \reimp + */ +QModelIndex QIdentityProxyModel::mapToSource(const QModelIndex& proxyIndex) const +{ + Q_D(const QIdentityProxyModel); + if (!d->model || !proxyIndex.isValid()) + return QModelIndex(); + Q_ASSERT(proxyIndex.model() == this); + return d->model->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer()); +} + +/*! + \reimp + */ +QModelIndexList QIdentityProxyModel::match(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags) const +{ + Q_D(const QIdentityProxyModel); + Q_ASSERT(start.isValid() ? start.model() == this : true); + if (!d->model) + return QModelIndexList(); + + const QModelIndexList sourceList = d->model->match(mapToSource(start), role, value, hits, flags); + QModelIndexList::const_iterator it = sourceList.constBegin(); + const QModelIndexList::const_iterator end = sourceList.constEnd(); + QModelIndexList proxyList; + for ( ; it != end; ++it) + proxyList.append(mapFromSource(*it)); + return proxyList; +} + +/*! + \reimp + */ +QModelIndex QIdentityProxyModel::parent(const QModelIndex& child) const +{ + Q_ASSERT(child.isValid() ? child.model() == this : true); + const QModelIndex sourceIndex = mapToSource(child); + const QModelIndex sourceParent = sourceIndex.parent(); + return mapFromSource(sourceParent); +} + +/*! + \reimp + */ +bool QIdentityProxyModel::removeColumns(int column, int count, const QModelIndex& parent) +{ + Q_ASSERT(parent.isValid() ? parent.model() == this : true); + Q_D(QIdentityProxyModel); + return d->model->removeColumns(column, count, mapToSource(parent)); +} + +/*! + \reimp + */ +bool QIdentityProxyModel::removeRows(int row, int count, const QModelIndex& parent) +{ + Q_ASSERT(parent.isValid() ? parent.model() == this : true); + Q_D(QIdentityProxyModel); + return d->model->removeRows(row, count, mapToSource(parent)); +} + +/*! + \reimp + */ +int QIdentityProxyModel::rowCount(const QModelIndex& parent) const +{ + Q_ASSERT(parent.isValid() ? parent.model() == this : true); + Q_D(const QIdentityProxyModel); + return d->model->rowCount(mapToSource(parent)); +} + +/*! + \reimp + */ +void QIdentityProxyModel::setSourceModel(QAbstractItemModel* sourceModel) +{ + beginResetModel(); + + if (sourceModel) { + disconnect(sourceModel, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(_q_sourceRowsAboutToBeInserted(const QModelIndex &, int, int))); + disconnect(sourceModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(_q_sourceRowsInserted(const QModelIndex &, int, int))); + disconnect(sourceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(_q_sourceRowsAboutToBeRemoved(const QModelIndex &, int, int))); + disconnect(sourceModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(_q_sourceRowsRemoved(const QModelIndex &, int, int))); + disconnect(sourceModel, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)), + this, SLOT(_q_sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + disconnect(sourceModel, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), + this, SLOT(_q_sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + disconnect(sourceModel, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), + this, SLOT(_q_sourceColumnsAboutToBeInserted(const QModelIndex &, int, int))); + disconnect(sourceModel, SIGNAL(columnsInserted(const QModelIndex &, int, int)), + this, SLOT(_q_sourceColumnsInserted(const QModelIndex &, int, int))); + disconnect(sourceModel, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), + this, SLOT(_q_sourceColumnsAboutToBeRemoved(const QModelIndex &, int, int))); + disconnect(sourceModel, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), + this, SLOT(_q_sourceColumnsRemoved(const QModelIndex &, int, int))); + disconnect(sourceModel, SIGNAL(columnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)), + this, SLOT(_q_sourceColumnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + disconnect(sourceModel, SIGNAL(columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), + this, SLOT(_q_sourceColumnsMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + disconnect(sourceModel, SIGNAL(modelAboutToBeReset()), + this, SLOT(_q_sourceModelAboutToBeReset())); + disconnect(sourceModel, SIGNAL(modelReset()), + this, SLOT(_q_sourceModelReset())); + disconnect(sourceModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(_q_sourceDataChanged(const QModelIndex &, const QModelIndex &))); + disconnect(sourceModel, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), + this, SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int))); + disconnect(sourceModel, SIGNAL(layoutAboutToBeChanged()), + this, SLOT(_q_sourceLayoutAboutToBeChanged())); + disconnect(sourceModel, SIGNAL(layoutChanged()), + this, SLOT(_q_sourceLayoutChanged())); + } + + QAbstractProxyModel::setSourceModel(sourceModel); + + if (sourceModel) { + connect(sourceModel, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), + SLOT(_q_sourceRowsAboutToBeInserted(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), + SLOT(_q_sourceRowsInserted(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), + SLOT(_q_sourceRowsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + SLOT(_q_sourceRowsRemoved(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(rowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)), + SLOT(_q_sourceRowsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + connect(sourceModel, SIGNAL(rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), + SLOT(_q_sourceRowsMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + connect(sourceModel, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)), + SLOT(_q_sourceColumnsAboutToBeInserted(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(columnsInserted(const QModelIndex &, int, int)), + SLOT(_q_sourceColumnsInserted(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)), + SLOT(_q_sourceColumnsAboutToBeRemoved(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(columnsRemoved(const QModelIndex &, int, int)), + SLOT(_q_sourceColumnsRemoved(const QModelIndex &, int, int))); + connect(sourceModel, SIGNAL(columnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int)), + SLOT(_q_sourceColumnsAboutToBeMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + connect(sourceModel, SIGNAL(columnsMoved(const QModelIndex &, int, int, const QModelIndex &, int)), + SLOT(_q_sourceColumnsMoved(const QModelIndex &, int, int, const QModelIndex &, int))); + connect(sourceModel, SIGNAL(modelAboutToBeReset()), + SLOT(_q_sourceModelAboutToBeReset())); + connect(sourceModel, SIGNAL(modelReset()), + SLOT(_q_sourceModelReset())); + connect(sourceModel, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + SLOT(_q_sourceDataChanged(const QModelIndex &, const QModelIndex &))); + connect(sourceModel, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), + SLOT(_q_sourceHeaderDataChanged(Qt::Orientation,int,int))); + connect(sourceModel, SIGNAL(layoutAboutToBeChanged()), + SLOT(_q_sourceLayoutAboutToBeChanged())); + connect(sourceModel, SIGNAL(layoutChanged()), + SLOT(_q_sourceLayoutChanged())); + } + + endResetModel(); +} + +void QIdentityProxyModelPrivate::_q_sourceColumnsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_ASSERT(parent.isValid() ? parent.model() == model : true); + Q_Q(QIdentityProxyModel); + q->beginInsertColumns(q->mapFromSource(parent), start, end); +} + +void QIdentityProxyModelPrivate::_q_sourceColumnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) +{ + Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true); + Q_ASSERT(destParent.isValid() ? destParent.model() == model : true); + Q_Q(QIdentityProxyModel); + q->beginMoveColumns(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destParent), dest); +} + +void QIdentityProxyModelPrivate::_q_sourceColumnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Q_ASSERT(parent.isValid() ? parent.model() == model : true); + Q_Q(QIdentityProxyModel); + q->beginRemoveColumns(q->mapFromSource(parent), start, end); +} + +void QIdentityProxyModelPrivate::_q_sourceColumnsInserted(const QModelIndex &parent, int start, int end) +{ + Q_ASSERT(parent.isValid() ? parent.model() == model : true); + Q_Q(QIdentityProxyModel); + Q_UNUSED(parent) + Q_UNUSED(start) + Q_UNUSED(end) + q->endInsertColumns(); +} + +void QIdentityProxyModelPrivate::_q_sourceColumnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) +{ + Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true); + Q_ASSERT(destParent.isValid() ? destParent.model() == model : true); + Q_Q(QIdentityProxyModel); + Q_UNUSED(sourceParent) + Q_UNUSED(sourceStart) + Q_UNUSED(sourceEnd) + Q_UNUSED(destParent) + Q_UNUSED(dest) + q->endMoveColumns(); +} + +void QIdentityProxyModelPrivate::_q_sourceColumnsRemoved(const QModelIndex &parent, int start, int end) +{ + Q_ASSERT(parent.isValid() ? parent.model() == model : true); + Q_Q(QIdentityProxyModel); + Q_UNUSED(parent) + Q_UNUSED(start) + Q_UNUSED(end) + q->endRemoveColumns(); +} + +void QIdentityProxyModelPrivate::_q_sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + Q_ASSERT(topLeft.isValid() ? topLeft.model() == model : true); + Q_ASSERT(bottomRight.isValid() ? bottomRight.model() == model : true); + Q_Q(QIdentityProxyModel); + q->dataChanged(q->mapFromSource(topLeft), q->mapFromSource(bottomRight)); +} + +void QIdentityProxyModelPrivate::_q_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last) +{ + Q_Q(QIdentityProxyModel); + q->headerDataChanged(orientation, first, last); +} + +void QIdentityProxyModelPrivate::_q_sourceLayoutAboutToBeChanged() +{ + if (ignoreNextLayoutAboutToBeChanged) + return; + + Q_Q(QIdentityProxyModel); + + foreach(const QPersistentModelIndex &proxyPersistentIndex, q->persistentIndexList()) { + proxyIndexes << proxyPersistentIndex; + Q_ASSERT(proxyPersistentIndex.isValid()); + const QPersistentModelIndex srcPersistentIndex = q->mapToSource(proxyPersistentIndex); + Q_ASSERT(srcPersistentIndex.isValid()); + layoutChangePersistentIndexes << srcPersistentIndex; + } + + q->layoutAboutToBeChanged(); +} + +void QIdentityProxyModelPrivate::_q_sourceLayoutChanged() +{ + if (ignoreNextLayoutChanged) + return; + + Q_Q(QIdentityProxyModel); + + for (int i = 0; i < proxyIndexes.size(); ++i) { + q->changePersistentIndex(proxyIndexes.at(i), q->mapFromSource(layoutChangePersistentIndexes.at(i))); + } + + layoutChangePersistentIndexes.clear(); + proxyIndexes.clear(); + + q->layoutChanged(); +} + +void QIdentityProxyModelPrivate::_q_sourceModelAboutToBeReset() +{ + Q_Q(QIdentityProxyModel); + q->beginResetModel(); +} + +void QIdentityProxyModelPrivate::_q_sourceModelReset() +{ + Q_Q(QIdentityProxyModel); + q->endResetModel(); +} + +void QIdentityProxyModelPrivate::_q_sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end) +{ + Q_ASSERT(parent.isValid() ? parent.model() == model : true); + Q_Q(QIdentityProxyModel); + q->beginInsertRows(q->mapFromSource(parent), start, end); +} + +void QIdentityProxyModelPrivate::_q_sourceRowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) +{ + Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true); + Q_ASSERT(destParent.isValid() ? destParent.model() == model : true); + Q_Q(QIdentityProxyModel); + q->beginMoveRows(q->mapFromSource(sourceParent), sourceStart, sourceEnd, q->mapFromSource(destParent), dest); +} + +void QIdentityProxyModelPrivate::_q_sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + Q_ASSERT(parent.isValid() ? parent.model() == model : true); + Q_Q(QIdentityProxyModel); + q->beginRemoveRows(q->mapFromSource(parent), start, end); +} + +void QIdentityProxyModelPrivate::_q_sourceRowsInserted(const QModelIndex &parent, int start, int end) +{ + Q_ASSERT(parent.isValid() ? parent.model() == model : true); + Q_Q(QIdentityProxyModel); + Q_UNUSED(parent) + Q_UNUSED(start) + Q_UNUSED(end) + q->endInsertRows(); +} + +void QIdentityProxyModelPrivate::_q_sourceRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destParent, int dest) +{ + Q_ASSERT(sourceParent.isValid() ? sourceParent.model() == model : true); + Q_ASSERT(destParent.isValid() ? destParent.model() == model : true); + Q_Q(QIdentityProxyModel); + Q_UNUSED(sourceParent) + Q_UNUSED(sourceStart) + Q_UNUSED(sourceEnd) + Q_UNUSED(destParent) + Q_UNUSED(dest) + q->endMoveRows(); +} + +void QIdentityProxyModelPrivate::_q_sourceRowsRemoved(const QModelIndex &parent, int start, int end) +{ + Q_ASSERT(parent.isValid() ? parent.model() == model : true); + Q_Q(QIdentityProxyModel); + Q_UNUSED(parent) + Q_UNUSED(start) + Q_UNUSED(end) + q->endRemoveRows(); +} + +QT_END_NAMESPACE + +#include "moc_qidentityproxymodel.cpp" + +#endif // QT_NO_IDENTITYPROXYMODEL diff --git a/src/gui/itemviews/qidentityproxymodel.h b/src/gui/itemviews/qidentityproxymodel.h new file mode 100644 index 0000000..40ad673 --- /dev/null +++ b/src/gui/itemviews/qidentityproxymodel.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com> +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QIDENTITYPROXYMODEL_H +#define QIDENTITYPROXYMODEL_H + +#include <QtGui/qabstractproxymodel.h> + +#ifndef QT_NO_IDENTITYPROXYMODEL + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QIdentityProxyModelPrivate; + +class Q_GUI_EXPORT QIdentityProxyModel : public QAbstractProxyModel +{ + Q_OBJECT +public: + explicit QIdentityProxyModel(QObject* parent = 0); + ~QIdentityProxyModel(); + + int columnCount(const QModelIndex& parent = QModelIndex()) const; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; + QModelIndex mapFromSource(const QModelIndex& sourceIndex) const; + QModelIndex mapToSource(const QModelIndex& proxyIndex) const; + QModelIndex parent(const QModelIndex& child) const; + int rowCount(const QModelIndex& parent = QModelIndex()) const; + bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); + + QItemSelection mapSelectionFromSource(const QItemSelection& selection) const; + QItemSelection mapSelectionToSource(const QItemSelection& selection) const; + QModelIndexList match(const QModelIndex& start, int role, const QVariant& value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const; + void setSourceModel(QAbstractItemModel* sourceModel); + + bool insertColumns(int column, int count, const QModelIndex& parent = QModelIndex()); + bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()); + bool removeColumns(int column, int count, const QModelIndex& parent = QModelIndex()); + bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()); + +protected: + QIdentityProxyModel(QIdentityProxyModelPrivate &dd, QObject* parent); + +private: + Q_DECLARE_PRIVATE(QIdentityProxyModel) + Q_DISABLE_COPY(QIdentityProxyModel) + + Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeInserted(QModelIndex,int,int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsInserted(QModelIndex,int,int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsRemoved(QModelIndex,int,int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceRowsMoved(QModelIndex,int,int,QModelIndex,int)) + + Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeInserted(QModelIndex,int,int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsInserted(QModelIndex,int,int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeRemoved(QModelIndex,int,int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsRemoved(QModelIndex,int,int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceColumnsMoved(QModelIndex,int,int,QModelIndex,int)) + + Q_PRIVATE_SLOT(d_func(), void _q_sourceDataChanged(QModelIndex,QModelIndex)) + Q_PRIVATE_SLOT(d_func(), void _q_sourceHeaderDataChanged(Qt::Orientation orientation, int first, int last)) + + Q_PRIVATE_SLOT(d_func(), void _q_sourceLayoutAboutToBeChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_sourceLayoutChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_sourceModelAboutToBeReset()) + Q_PRIVATE_SLOT(d_func(), void _q_sourceModelReset()) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_IDENTITYPROXYMODEL + +#endif // QIDENTITYPROXYMODEL_H + diff --git a/src/gui/itemviews/qitemdelegate.cpp b/src/gui/itemviews/qitemdelegate.cpp index 1daf394..c1f957b 100644 --- a/src/gui/itemviews/qitemdelegate.cpp +++ b/src/gui/itemviews/qitemdelegate.cpp @@ -850,7 +850,7 @@ void QItemDelegate::drawBackground(QPainter *painter, painter->fillRect(option.rect, option.palette.brush(cg, QPalette::Highlight)); } else { QVariant value = index.data(Qt::BackgroundRole); - if (qVariantCanConvert<QBrush>(value)) { + if (value.canConvert<QBrush>()) { QPointF oldBO = painter->brushOrigin(); painter->setBrushOrigin(option.rect.topLeft()); painter->fillRect(option.rect, qvariant_cast<QBrush>(value)); @@ -1278,7 +1278,8 @@ bool QItemDelegate::editorEvent(QEvent *event, // make sure that we have the right event type if ((event->type() == QEvent::MouseButtonRelease) - || (event->type() == QEvent::MouseButtonDblClick)) { + || (event->type() == QEvent::MouseButtonDblClick) + || (event->type() == QEvent::MouseButtonPress)) { QRect checkRect = check(option, option.rect, Qt::Checked); QRect emptyRect; doLayout(option, &checkRect, &emptyRect, &emptyRect, false); @@ -1287,7 +1288,8 @@ bool QItemDelegate::editorEvent(QEvent *event, return false; // eat the double click events inside the check rect - if (event->type() == QEvent::MouseButtonDblClick) + if ((event->type() == QEvent::MouseButtonPress) + || (event->type() == QEvent::MouseButtonDblClick)) return true; } else if (event->type() == QEvent::KeyPress) { @@ -1326,7 +1328,7 @@ QStyleOptionViewItem QItemDelegate::setOptions(const QModelIndex &index, // set foreground brush value = index.data(Qt::ForegroundRole); - if (qVariantCanConvert<QBrush>(value)) + if (value.canConvert<QBrush>()) opt.palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value)); return opt; diff --git a/src/gui/itemviews/qitemselectionmodel.cpp b/src/gui/itemviews/qitemselectionmodel.cpp index e6209f7..0d0b5b1 100644 --- a/src/gui/itemviews/qitemselectionmodel.cpp +++ b/src/gui/itemviews/qitemselectionmodel.cpp @@ -212,6 +212,7 @@ bool QItemSelectionRange::intersects(const QItemSelectionRange &other) const { return (isValid() && other.isValid() && parent() == other.parent() + && model() == other.model() && ((top() <= other.top() && bottom() >= other.top()) || (top() >= other.top() && top() <= other.bottom())) && ((left() <= other.left() && right() >= other.left()) @@ -508,7 +509,7 @@ void QItemSelection::merge(const QItemSelection &other, QItemSelectionModel::Sel void QItemSelection::split(const QItemSelectionRange &range, const QItemSelectionRange &other, QItemSelection *result) { - if (range.parent() != other.parent()) + if (range.parent() != other.parent() || range.model() != other.model()) return; QModelIndex parent = other.parent(); @@ -634,6 +635,7 @@ void QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved(const QModelIndex &pare } QItemSelection deselected; + QItemSelection newParts; QItemSelection::iterator it = ranges.begin(); while (it != ranges.end()) { if (it->topLeft().parent() != parent) { // Check parents until reaching root or contained in range @@ -659,13 +661,20 @@ void QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved(const QModelIndex &pare deselected.append(QItemSelectionRange(model->index(start, it->right(), it->parent()), it->bottomRight())); *it = QItemSelectionRange(it->topLeft(), model->index(start - 1, it->right(), it->parent())); ++it; - } else { - if (it->top() < start && end < it->bottom()) // Middle intersection (do nothing) - deselected.append(QItemSelectionRange(model->index(start, it->right(), it->parent()), - model->index(end, it->left(), it->parent()))); + } else if (it->top() < start && end < it->bottom()) { // Middle intersection + // If the parent contains (1, 2, 3, 4, 5, 6, 7, 8) and [3, 4, 5, 6] is selected, + // and [4, 5] is removed, we need to split [3, 4, 5, 6] into [3], [4, 5] and [6]. + // [4, 5] is appended to deselected, and [3] and [6] remain part of the selection + // in ranges. + const QItemSelectionRange removedRange(model->index(start, it->right(), it->parent()), + model->index(end, it->left(), it->parent())); + deselected.append(removedRange); + QItemSelection::split(*it, removedRange, &newParts); + it = ranges.erase(it); + } else ++it; - } } + ranges.append(newParts); if (!deselected.isEmpty()) emit q->selectionChanged(QItemSelection(), deselected); @@ -1136,11 +1145,8 @@ void QItemSelectionModel::clearSelection() Q_D(QItemSelectionModel); if (d->ranges.count() == 0 && d->currentSelection.count() == 0) return; - QItemSelection selection = d->ranges; - selection.merge(d->currentSelection, d->currentCommand); - d->ranges.clear(); - d->currentSelection.clear(); - emit selectionChanged(QItemSelection(), selection); + + select(QItemSelection(), Clear); } diff --git a/src/gui/itemviews/qitemselectionmodel.h b/src/gui/itemviews/qitemselectionmodel.h index 43327aa..3f4aa72 100644 --- a/src/gui/itemviews/qitemselectionmodel.h +++ b/src/gui/itemviews/qitemselectionmodel.h @@ -101,6 +101,30 @@ public: { return (tl == other.tl && br == other.br); } inline bool operator!=(const QItemSelectionRange &other) const { return !operator==(other); } + inline bool operator<(const QItemSelectionRange &other) const + { + // Comparing parents will compare the models, but if two equivalent ranges + // in two different models have invalid parents, they would appear the same + if (other.tl.model() == tl.model()) { + // parent has to be calculated, so we only do so once. + const QModelIndex topLeftParent = tl.parent(); + const QModelIndex otherTopLeftParent = other.tl.parent(); + if (topLeftParent == otherTopLeftParent) { + if (other.tl.row() == tl.row()) { + if (other.tl.column() == tl.column()) { + if (other.br.row() == br.row()) { + return br.column() < other.br.column(); + } + return br.row() < other.br.row(); + } + return tl.column() < other.tl.column(); + } + return tl.row() < other.tl.row(); + } + return topLeftParent < otherTopLeftParent; + } + return tl.model() < other.tl.model(); + } inline bool isValid() const { diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index 8c83642..a0955d2 100644 --- a/src/gui/itemviews/qlistview.cpp +++ b/src/gui/itemviews/qlistview.cpp @@ -756,10 +756,13 @@ void QListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int e // if the parent is above d->root in the tree, nothing will happen QAbstractItemView::rowsAboutToBeRemoved(parent, start, end); if (parent == d->root) { - for (int i = d->hiddenRows.count() - 1; i >= 0; --i) { - int hiddenRow = d->hiddenRows.at(i).row(); + QSet<QPersistentModelIndex>::iterator it = d->hiddenRows.begin(); + while (it != d->hiddenRows.end()) { + int hiddenRow = it->row(); if (hiddenRow >= start && hiddenRow <= end) { - d->hiddenRows.remove(i); + it = d->hiddenRows.erase(it); + } else { + ++it; } } } @@ -1033,16 +1036,7 @@ void QListView::paintEvent(QPaintEvent *e) previousRow = row; } - if (const QWidget *widget = d->editorForIndex(*it).editor) { - QRegion itemGeometry(option.rect); - QRegion widgetGeometry(widget->geometry()); - painter.save(); - painter.setClipRegion(itemGeometry.subtracted(widgetGeometry)); - d->delegateForIndex(*it)->paint(&painter, option, *it); - painter.restore(); - } else { - d->delegateForIndex(*it)->paint(&painter, option, *it); - } + d->delegateForIndex(*it)->paint(&painter, option, *it); } #ifndef QT_NO_DRAGANDDROP @@ -1496,19 +1490,20 @@ void QListView::updateGeometries() // if the scroll bars are turned off, we resize the contents to the viewport if (d->movement == Static && !d->isWrapping()) { - d->layoutChildren(); // we need the viewport size to be updated + const QSize maxSize = maximumViewportSize(); if (d->flow == TopToBottom) { if (horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) { - d->setContentsSize(viewport()->width(), contentsSize().height()); + d->setContentsSize(maxSize.width(), contentsSize().height()); horizontalScrollBar()->setRange(0, 0); // we see all the contents anyway } } else { // LeftToRight if (verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOff) { - d->setContentsSize(contentsSize().width(), viewport()->height()); + d->setContentsSize(contentsSize().width(), maxSize.height()); verticalScrollBar()->setRange(0, 0); // we see all the contents anyway } } } + } /*! @@ -1833,6 +1828,14 @@ QAbstractItemView::DropIndicatorPosition QListViewPrivate::position(const QPoint else return QAbstractItemViewPrivate::position(pos, rect, idx); } + +bool QListViewPrivate::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex) +{ + if (viewMode == QListView::ListMode && flow == QListView::LeftToRight) + return static_cast<QListModeViewBase *>(commonListView)->dropOn(event, dropRow, dropCol, dropIndex); + else + return QAbstractItemViewPrivate::dropOn(event, dropRow, dropCol, dropIndex); +} #endif /* @@ -1841,12 +1844,12 @@ QAbstractItemView::DropIndicatorPosition QListViewPrivate::position(const QPoint void QCommonListViewBase::appendHiddenRow(int row) { - dd->hiddenRows.append(dd->model->index(row, 0, qq->rootIndex())); + dd->hiddenRows.insert(dd->model->index(row, 0, qq->rootIndex())); } void QCommonListViewBase::removeHiddenRow(int row) { - dd->hiddenRows.remove(dd->hiddenRows.indexOf(dd->model->index(row, 0, qq->rootIndex()))); + dd->hiddenRows.remove(dd->model->index(row, 0, qq->rootIndex())); } void QCommonListViewBase::updateHorizontalScrollBar(const QSize &step) @@ -1960,7 +1963,13 @@ void QListModeViewBase::dragMoveEvent(QDragMoveEvent *event) // ignore by default event->ignore(); - QModelIndex index = qq->indexAt(event->pos()); + // can't use indexAt, doesn't account for spacing. + QPoint p = event->pos(); + QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1); + rect.adjust(-dd->spacing(), -dd->spacing(), dd->spacing(), dd->spacing()); + const QVector<QModelIndex> intersectVector = dd->intersectingSet(rect); + QModelIndex index = intersectVector.count() > 0 + ? intersectVector.last() : QModelIndex(); dd->hover = index; if (!dd->droppingOnItself(event, index) && dd->canDecode(event)) { @@ -1968,10 +1977,11 @@ void QListModeViewBase::dragMoveEvent(QDragMoveEvent *event) if (index.isValid() && dd->showDropIndicator) { QRect rect = qq->visualRect(index); dd->dropIndicatorPosition = position(event->pos(), rect, index); + // if spacing, should try to draw between items, not just next to item. switch (dd->dropIndicatorPosition) { case QAbstractItemView::AboveItem: if (dd->isIndexDropEnabled(index.parent())) { - dd->dropIndicatorRect = QRect(rect.left(), rect.top(), 0, rect.height()); + dd->dropIndicatorRect = QRect(rect.left()-dd->spacing(), rect.top(), 0, rect.height()); event->accept(); } else { dd->dropIndicatorRect = QRect(); @@ -1979,7 +1989,7 @@ void QListModeViewBase::dragMoveEvent(QDragMoveEvent *event) break; case QAbstractItemView::BelowItem: if (dd->isIndexDropEnabled(index.parent())) { - dd->dropIndicatorRect = QRect(rect.right(), rect.top(), 0, rect.height()); + dd->dropIndicatorRect = QRect(rect.right()+dd->spacing(), rect.top(), 0, rect.height()); event->accept(); } else { dd->dropIndicatorRect = QRect(); @@ -2014,6 +2024,68 @@ void QListModeViewBase::dragMoveEvent(QDragMoveEvent *event) qq->startAutoScroll(); } +/*! + If the event hasn't already been accepted, determines the index to drop on. + + if (row == -1 && col == -1) + // append to this drop index + else + // place at row, col in drop index + + If it returns true a drop can be done, and dropRow, dropCol and dropIndex reflects the position of the drop. + \internal + */ +bool QListModeViewBase::dropOn(QDropEvent *event, int *dropRow, int *dropCol, QModelIndex *dropIndex) +{ + if (event->isAccepted()) + return false; + + QModelIndex index; + if (dd->viewport->rect().contains(event->pos())) { + // can't use indexAt, doesn't account for spacing. + QPoint p = event->pos(); + QRect rect(p.x() + horizontalOffset(), p.y() + verticalOffset(), 1, 1); + rect.adjust(-dd->spacing(), -dd->spacing(), dd->spacing(), dd->spacing()); + const QVector<QModelIndex> intersectVector = dd->intersectingSet(rect); + index = intersectVector.count() > 0 + ? intersectVector.last() : QModelIndex(); + if (!index.isValid()) + index = dd->root; + } + + // If we are allowed to do the drop + if (dd->model->supportedDropActions() & event->dropAction()) { + int row = -1; + int col = -1; + if (index != dd->root) { + dd->dropIndicatorPosition = position(event->pos(), qq->visualRect(index), index); + switch (dd->dropIndicatorPosition) { + case QAbstractItemView::AboveItem: + row = index.row(); + col = index.column(); + index = index.parent(); + break; + case QAbstractItemView::BelowItem: + row = index.row() + 1; + col = index.column(); + index = index.parent(); + break; + case QAbstractItemView::OnItem: + case QAbstractItemView::OnViewport: + break; + } + } else { + dd->dropIndicatorPosition = QAbstractItemView::OnViewport; + } + *dropIndex = index; + *dropRow = row; + *dropCol = col; + if (!dd->droppingOnItself(event, index)) + return true; + } + return false; +} + #endif //QT_NO_DRAGANDDROP void QListModeViewBase::updateVerticalScrollBar(const QSize &step) @@ -2107,7 +2179,7 @@ int QListModeViewBase::verticalOffset() const int value = verticalScrollBar()->value(); if (value >= segmentPositions.count()) return 0; - return segmentPositions.at(value); + return segmentPositions.at(value) - spacing(); } } else if (flow() == QListView::TopToBottom && !flowPositions.isEmpty()) { int value = verticalScrollBar()->value(); @@ -2155,14 +2227,14 @@ void QListModeViewBase::scrollContentsBy(int dx, int dy, bool scrollElasticBand) if (horizontal && flow() == QListView::TopToBottom && dx != 0) { int currentValue = qBound(0, horizontalValue, max); int previousValue = qBound(0, currentValue + dx, max); - int currentCoordinate = segmentPositions.at(currentValue); - int previousCoordinate = segmentPositions.at(previousValue); + int currentCoordinate = segmentPositions.at(currentValue) - spacing(); + int previousCoordinate = segmentPositions.at(previousValue) - spacing(); dx = previousCoordinate - currentCoordinate; } else if (vertical && flow() == QListView::LeftToRight && dy != 0) { int currentValue = qBound(0, verticalValue, max); int previousValue = qBound(0, currentValue + dy, max); - int currentCoordinate = segmentPositions.at(currentValue); - int previousCoordinate = segmentPositions.at(previousValue); + int currentCoordinate = segmentPositions.at(currentValue) - spacing(); + int previousCoordinate = segmentPositions.at(previousValue) - spacing(); dy = previousCoordinate - currentCoordinate; } } else { @@ -2330,6 +2402,8 @@ void QListModeViewBase::doStaticLayout(const QListViewLayoutInfo &info) segmentExtents.append(flowPosition); flowPosition = info.spacing + segStartPosition; segPosition += deltaSegPosition; + if (info.wrap) + segPosition += info.spacing; segmentPositions.append(segPosition); segmentStartRows.append(row); deltaSegPosition = 0; @@ -3094,7 +3168,11 @@ void QListView::currentChanged(const QModelIndex ¤t, const QModelIndex &pr if (QAccessible::isActive()) { if (current.isValid()) { int entry = visualIndex(current) + 1; +#ifdef Q_WS_X11 + QAccessible::updateAccessibility(this, entry, QAccessible::Focus); +#else QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus); +#endif } } #endif @@ -3113,12 +3191,20 @@ void QListView::selectionChanged(const QItemSelection &selected, QModelIndex sel = selected.indexes().value(0); if (sel.isValid()) { int entry = visualIndex(sel) + 1; +#ifdef Q_WS_X11 + QAccessible::updateAccessibility(this, entry, QAccessible::Selection); +#else QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection); +#endif } QModelIndex desel = deselected.indexes().value(0); if (desel.isValid()) { int entry = visualIndex(desel) + 1; +#ifdef Q_WS_X11 + QAccessible::updateAccessibility(this, entry, QAccessible::SelectionRemove); +#else QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove); +#endif } } #endif diff --git a/src/gui/itemviews/qlistview_p.h b/src/gui/itemviews/qlistview_p.h index 6b4a65d..6a7acf6 100644 --- a/src/gui/itemviews/qlistview_p.h +++ b/src/gui/itemviews/qlistview_p.h @@ -237,6 +237,7 @@ public: // WARNING: Plenty of duplicated code from QAbstractItemView{,Private}. QAbstractItemView::DropIndicatorPosition position(const QPoint &pos, const QRect &rect, const QModelIndex &idx) const; void dragMoveEvent(QDragMoveEvent *e); + bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index); #endif private: @@ -364,6 +365,7 @@ public: #ifndef QT_NO_DRAGANDDROP virtual QAbstractItemView::DropIndicatorPosition position(const QPoint &pos, const QRect &rect, const QModelIndex &idx) const; + bool dropOn(QDropEvent *event, int *row, int *col, QModelIndex *index); #endif inline void setGridSize(const QSize &size) { grid = size; } @@ -376,7 +378,10 @@ public: inline bool isSelectionRectVisible() const { return showElasticBand; } inline QModelIndex modelIndex(int row) const { return model->index(row, column, root); } - inline bool isHidden(int row) const { return hiddenRows.contains(model->index(row, 0, root)); } + inline bool isHidden(int row) const { + QModelIndex idx = model->index(row, 0, root); + return isPersistent(idx) && hiddenRows.contains(idx); + } inline bool isHiddenOrDisabled(int row) const { return isHidden(row) || !isIndexEnabled(modelIndex(row)); } inline void removeCurrentAndDisabled(QVector<QModelIndex> *indexes, const QModelIndex ¤t) const { @@ -430,7 +435,7 @@ public: QBasicTimer batchLayoutTimer; // used for hidden items - QVector<QPersistentModelIndex> hiddenRows; + QSet<QPersistentModelIndex> hiddenRows; int column; bool uniformItemSizes; diff --git a/src/gui/itemviews/qsortfilterproxymodel.cpp b/src/gui/itemviews/qsortfilterproxymodel.cpp index 415bd54..b749286 100644 --- a/src/gui/itemviews/qsortfilterproxymodel.cpp +++ b/src/gui/itemviews/qsortfilterproxymodel.cpp @@ -362,6 +362,7 @@ QModelIndex QSortFilterProxyModelPrivate::proxy_to_source(const QModelIndex &pro return QModelIndex(); // for now; we may want to be able to set a root index later if (proxy_index.model() != q_func()) { qWarning() << "QSortFilterProxyModel: index from wrong model passed to mapToSource"; + Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapToSource"); return QModelIndex(); } IndexMap::const_iterator it = index_to_iterator(proxy_index); @@ -379,6 +380,7 @@ QModelIndex QSortFilterProxyModelPrivate::source_to_proxy(const QModelIndex &sou return QModelIndex(); // for now; we may want to be able to set a root index later if (source_index.model() != model) { qWarning() << "QSortFilterProxyModel: index from wrong model passed to mapFromSource"; + Q_ASSERT(!"QSortFilterProxyModel: index from wrong model passed to mapFromSource"); return QModelIndex(); } QModelIndex source_parent = source_index.parent(); @@ -1500,7 +1502,7 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved( \l{Model Subclassing Reference}. \sa QAbstractProxyModel, QAbstractItemModel, {Model/View Programming}, - {Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example} + {Basic Sort/Filter Model Example}, {Custom Sort/Filter Model Example}, QIdentityProxyModel */ /*! diff --git a/src/gui/itemviews/qstringlistmodel.cpp b/src/gui/itemviews/qstringlistmodel.cpp index df7e7c6..367decc 100644 --- a/src/gui/itemviews/qstringlistmodel.cpp +++ b/src/gui/itemviews/qstringlistmodel.cpp @@ -290,8 +290,9 @@ QStringList QStringListModel::stringList() const */ void QStringListModel::setStringList(const QStringList &strings) { + emit beginResetModel(); lst = strings; - reset(); + emit endResetModel(); } /*! diff --git a/src/gui/itemviews/qstyleditemdelegate.cpp b/src/gui/itemviews/qstyleditemdelegate.cpp index 1eeb8f7..931d870 100644 --- a/src/gui/itemviews/qstyleditemdelegate.cpp +++ b/src/gui/itemviews/qstyleditemdelegate.cpp @@ -331,7 +331,7 @@ void QStyledItemDelegate::initStyleOption(QStyleOptionViewItem *option, option->displayAlignment = Qt::Alignment(value.toInt()); value = index.data(Qt::ForegroundRole); - if (qVariantCanConvert<QBrush>(value)) + if (value.canConvert<QBrush>()) option->palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value)); if (QStyleOptionViewItemV4 *v4 = qstyleoption_cast<QStyleOptionViewItemV4 *>(option)) { @@ -732,7 +732,8 @@ bool QStyledItemDelegate::editorEvent(QEvent *event, // make sure that we have the right event type if ((event->type() == QEvent::MouseButtonRelease) - || (event->type() == QEvent::MouseButtonDblClick)) { + || (event->type() == QEvent::MouseButtonDblClick) + || (event->type() == QEvent::MouseButtonPress)) { QStyleOptionViewItemV4 viewOpt(option); initStyleOption(&viewOpt, index); QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &viewOpt, widget); @@ -740,8 +741,8 @@ bool QStyledItemDelegate::editorEvent(QEvent *event, if (me->button() != Qt::LeftButton || !checkRect.contains(me->pos())) return false; - // eat the double click events inside the check rect - if (event->type() == QEvent::MouseButtonDblClick) + if ((event->type() == QEvent::MouseButtonPress) + || (event->type() == QEvent::MouseButtonDblClick)) return true; } else if (event->type() == QEvent::KeyPress) { diff --git a/src/gui/itemviews/qtableview.cpp b/src/gui/itemviews/qtableview.cpp index 056e633..6f532eb 100644 --- a/src/gui/itemviews/qtableview.cpp +++ b/src/gui/itemviews/qtableview.cpp @@ -926,14 +926,7 @@ void QTableViewPrivate::drawCell(QPainter *painter, const QStyleOptionViewItemV4 q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, q); - if (const QWidget *widget = editorForIndex(index).editor) { - painter->save(); - painter->setClipRect(widget->geometry()); - q->itemDelegate(index)->paint(painter, opt, index); - painter->restore(); - } else { - q->itemDelegate(index)->paint(painter, opt, index); - } + q->itemDelegate(index)->paint(painter, opt, index); } /*! @@ -1109,6 +1102,21 @@ void QTableView::setRootIndex(const QModelIndex &index) } /*! + \internal +*/ +void QTableView::doItemsLayout() +{ + Q_D(QTableView); + QAbstractItemView::doItemsLayout(); + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) + d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value()); + else + d->verticalHeader->setOffset(verticalScrollBar()->value()); + if (!d->verticalHeader->updatesEnabled()) + d->verticalHeader->setUpdatesEnabled(true); +} + +/*! \reimp */ void QTableView::setSelectionModel(QItemSelectionModel *selectionModel) @@ -1292,7 +1300,6 @@ void QTableView::paintEvent(QPaintEvent *event) const QPen gridPen = QPen(gridColor, 0, d->gridStyle); const QHeaderView *verticalHeader = d->verticalHeader; const QHeaderView *horizontalHeader = d->horizontalHeader; - const QStyle::State state = option.state; const bool alternate = d->alternatingColors; const bool rightToLeft = isRightToLeft(); @@ -1982,9 +1989,13 @@ QModelIndexList QTableView::selectedIndexes() const previous number of rows is specified by \a oldCount, and the new number of rows is specified by \a newCount. */ -void QTableView::rowCountChanged(int /*oldCount*/, int /*newCount*/ ) +void QTableView::rowCountChanged(int oldCount, int newCount ) { Q_D(QTableView); + //when removing rows, we need to disable updates for the header until the geometries have been + //updated and the offset has been adjusted, or we risk calling paintSection for all the sections + if (newCount < oldCount) + d->verticalHeader->setUpdatesEnabled(false); d->doDelayedItemsLayout(); } @@ -2166,7 +2177,7 @@ int QTableView::sizeHintForRow(int row) const option.rect.setWidth(columnWidth(index.column())); } - QWidget *editor = d->editorForIndex(index).editor; + QWidget *editor = d->editorForIndex(index).widget.data(); if (editor && d->persistent.contains(editor)) { hint = qMax(hint, editor->sizeHint().height()); int min = editor->minimumSize().height(); @@ -2219,7 +2230,7 @@ int QTableView::sizeHintForColumn(int column) const continue; index = d->model->index(logicalRow, column, d->root); - QWidget *editor = d->editorForIndex(index).editor; + QWidget *editor = d->editorForIndex(index).widget.data(); if (editor && d->persistent.contains(editor)) { hint = qMax(hint, editor->sizeHint().width()); int min = editor->minimumSize().width(); @@ -3153,10 +3164,16 @@ void QTableView::currentChanged(const QModelIndex ¤t, const QModelIndex &p #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive()) { if (current.isValid()) { +#ifdef Q_WS_X11 + Q_D(QTableView); + int entry = d->accessibleTable2Index(current); + QAccessible::updateAccessibility(this, entry, QAccessible::Focus); +#else int entry = visualIndex(current) + 1; if (horizontalHeader()) ++entry; QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus); +#endif } } #endif @@ -3169,22 +3186,33 @@ void QTableView::currentChanged(const QModelIndex ¤t, const QModelIndex &p void QTableView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { + Q_D(QTableView); #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive()) { // ### does not work properly for selection ranges. QModelIndex sel = selected.indexes().value(0); if (sel.isValid()) { +#ifdef Q_WS_X11 + int entry = d->accessibleTable2Index(sel); + QAccessible::updateAccessibility(this, entry, QAccessible::Selection); +#else int entry = visualIndex(sel); if (horizontalHeader()) ++entry; QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection); +#endif } QModelIndex desel = deselected.indexes().value(0); if (desel.isValid()) { +#ifdef Q_WS_X11 + int entry = d->accessibleTable2Index(sel); + QAccessible::updateAccessibility(this, entry, QAccessible::SelectionRemove); +#else int entry = visualIndex(sel); if (horizontalHeader()) ++entry; QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove); +#endif } } #endif diff --git a/src/gui/itemviews/qtableview.h b/src/gui/itemviews/qtableview.h index 7266dda..27b2dcc 100644 --- a/src/gui/itemviews/qtableview.h +++ b/src/gui/itemviews/qtableview.h @@ -71,6 +71,7 @@ public: void setModel(QAbstractItemModel *model); void setRootIndex(const QModelIndex &index); void setSelectionModel(QItemSelectionModel *selectionModel); + void doItemsLayout(); QHeaderView *horizontalHeader() const; QHeaderView *verticalHeader() const; diff --git a/src/gui/itemviews/qtableview_p.h b/src/gui/itemviews/qtableview_p.h index f973acf..dce0ed0 100644 --- a/src/gui/itemviews/qtableview_p.h +++ b/src/gui/itemviews/qtableview_p.h @@ -167,6 +167,11 @@ public: return horizontalHeader->logicalIndex(visualCol); } + inline int accessibleTable2Index(const QModelIndex &index) const { + return (index.row() + (horizontalHeader ? 1 : 0)) * (index.model()->columnCount() + (verticalHeader ? 1 : 0)) + + index.column() + (verticalHeader ? 1 : 0) + 1; + } + int sectionSpanEndLogical(const QHeaderView *header, int logical, int span) const; int sectionSpanSize(const QHeaderView *header, int logical, int span) const; bool spanContainsSection(const QHeaderView *header, int logical, int spanLogical, int span) const; diff --git a/src/gui/itemviews/qtreeview.cpp b/src/gui/itemviews/qtreeview.cpp index e3d71c6..9228ac8 100644 --- a/src/gui/itemviews/qtreeview.cpp +++ b/src/gui/itemviews/qtreeview.cpp @@ -54,6 +54,7 @@ #include <qdebug.h> #ifndef QT_NO_ACCESSIBILITY #include <qaccessible.h> +#include <qaccessible2.h> #endif #include <private/qtreeview_p.h> @@ -1493,7 +1494,7 @@ void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, // when the row contains an index widget which has focus, // we want to paint the entire row as active bool indexWidgetHasFocus = false; - if ((current.row() == index.row()) && !d->editors.isEmpty()) { + if ((current.row() == index.row()) && !d->editorIndexHash.isEmpty()) { const int r = index.row(); QWidget *fw = QApplication::focusWidget(); for (int c = 0; c < header->count(); ++c) { @@ -1669,14 +1670,7 @@ void QTreeView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, opt.state = oldState; } - if (const QWidget *widget = d->editorForIndex(modelIndex).editor) { - painter->save(); - painter->setClipRect(widget->geometry()); - d->delegateForIndex(modelIndex)->paint(painter, opt, modelIndex); - painter->restore(); - } else { - d->delegateForIndex(modelIndex)->paint(painter, opt, modelIndex); - } + d->delegateForIndex(modelIndex)->paint(painter, opt, modelIndex); } if (currentRowHasFocus) { @@ -2395,7 +2389,7 @@ void QTreeView::scrollContentsBy(int dx, int dy) int viewCount = d->viewport->height() / itemHeight; int maxDeltaY = qMin(d->viewItems.count(), viewCount); // no need to do a lot of work if we are going to redraw the whole thing anyway - if (qAbs(dy) > qAbs(maxDeltaY) && d->editors.isEmpty()) { + if (qAbs(dy) > qAbs(maxDeltaY) && d->editorIndexHash.isEmpty()) { verticalScrollBar()->update(); d->viewport->update(); return; @@ -2469,11 +2463,9 @@ void QTreeView::rowsInserted(const QModelIndex &parent, int start, int end) } const int parentItem = d->viewIndex(parent); - if (((parentItem != -1) && d->viewItems.at(parentItem).expanded && updatesEnabled()) + if (((parentItem != -1) && d->viewItems.at(parentItem).expanded) || (parent == d->root)) { d->doDelayedItemsLayout(); - } else if ((parentItem != -1) && d->viewItems.at(parentItem).expanded) { - d->doDelayedItemsLayout(); } else if (parentItem != -1 && (d->model->rowCount(parent) == end - start + 1)) { // the parent just went from 0 children to more. update to re-paint the decoration d->viewItems[parentItem].hasChildren = true; @@ -2727,7 +2719,7 @@ int QTreeView::sizeHintForColumn(int column) const continue; // we have no good size hint QModelIndex index = viewItems.at(i).index; index = index.sibling(index.row(), column); - QWidget *editor = d->editorForIndex(index).editor; + QWidget *editor = d->editorForIndex(index).widget.data(); if (editor && d->persistent.contains(editor)) { w = qMax(w, editor->sizeHint().width()); int min = editor->minimumSize().width(); @@ -2792,7 +2784,7 @@ int QTreeView::indexRowSizeHint(const QModelIndex &index) const continue; QModelIndex idx = d->model->index(indexRow, logicalColumn, parent); if (idx.isValid()) { - QWidget *editor = d->editorForIndex(idx).editor; + QWidget *editor = d->editorForIndex(idx).widget.data(); if (editor && d->persistent.contains(editor)) { height = qMax(height, editor->sizeHint().height()); int min = editor->minimumSize().height(); @@ -2871,13 +2863,13 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) if (emitSignal && animationsEnabled) prepareAnimatedOperation(item, QVariantAnimation::Forward); #endif //QT_NO_ANIMATION - QAbstractItemView::State oldState = state; + stateBeforeAnimation = state; q->setState(QAbstractItemView::ExpandingState); const QModelIndex index = viewItems.at(item).index; storeExpanded(index); viewItems[item].expanded = true; layout(item); - q->setState(oldState); + q->setState(stateBeforeAnimation); if (model->canFetchMore(index)) model->fetchMore(index); @@ -2892,20 +2884,36 @@ void QTreeViewPrivate::expand(int item, bool emitSignal) void QTreeViewPrivate::insertViewItems(int pos, int count, const QTreeViewItem &viewItem) { + Q_Q(QTreeView); viewItems.insert(pos, count, viewItem); QTreeViewItem *items = viewItems.data(); for (int i = pos + count; i < viewItems.count(); i++) if (items[i].parentItem >= pos) items[i].parentItem += count; +#ifndef QT_NO_ACCESSIBILITY +#ifdef Q_WS_X11 + if (QAccessible::isActive()) { + QAccessible::updateAccessibility(q, 0, QAccessible::TableModelChanged); + } +#endif +#endif } void QTreeViewPrivate::removeViewItems(int pos, int count) { + Q_Q(QTreeView); viewItems.remove(pos, count); QTreeViewItem *items = viewItems.data(); for (int i = pos; i < viewItems.count(); i++) if (items[i].parentItem >= pos) items[i].parentItem -= count; +#ifndef QT_NO_ACCESSIBILITY +#ifdef Q_WS_X11 + if (QAccessible::isActive()) { + QAccessible::updateAccessibility(q, 0, QAccessible::TableModelChanged); + } +#endif +#endif } #if 0 @@ -2946,7 +2954,7 @@ void QTreeViewPrivate::collapse(int item, bool emitSignal) prepareAnimatedOperation(item, QVariantAnimation::Backward); #endif //QT_NO_ANIMATION - QAbstractItemView::State oldState = state; + stateBeforeAnimation = state; q->setState(QAbstractItemView::CollapsingState); expandedIndexes.erase(it); viewItems[item].expanded = false; @@ -2956,7 +2964,7 @@ void QTreeViewPrivate::collapse(int item, bool emitSignal) index = viewItems[index].parentItem; } removeViewItems(item + 1, total); // collapse - q->setState(oldState); + q->setState(stateBeforeAnimation); if (emitSignal) { emit q->collapsed(modelIndex); @@ -3041,9 +3049,9 @@ QPixmap QTreeViewPrivate::renderTreeToPixmapForAnimation(const QRect &rect) cons //and now let's render the editors the editors QStyleOptionViewItemV4 option = viewOptionsV4(); - for (QList<QEditorInfo>::const_iterator it = editors.constBegin(); it != editors.constEnd(); ++it) { - QWidget *editor = it->editor; - QModelIndex index = it->index; + for (QEditorIndexHash::const_iterator it = editorIndexHash.constBegin(); it != editorIndexHash.constEnd(); ++it) { + QWidget *editor = it.key(); + const QModelIndex &index = it.value(); option.rect = q->visualRect(index); if (option.rect.isValid()) { @@ -3067,7 +3075,7 @@ QPixmap QTreeViewPrivate::renderTreeToPixmapForAnimation(const QRect &rect) cons void QTreeViewPrivate::_q_endAnimatedOperation() { Q_Q(QTreeView); - q->setState(QAbstractItemView::NoState); + q->setState(stateBeforeAnimation); q->updateGeometries(); viewport->update(); } @@ -3696,14 +3704,6 @@ void QTreeViewPrivate::_q_sortIndicatorChanged(int column, Qt::SortOrder order) */ void QTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { -#ifndef QT_NO_ACCESSIBILITY - if (QAccessible::isActive()) { - int entry = visualIndex(current) + 1; - if (header()) - ++entry; - QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus); - } -#endif QAbstractItemView::currentChanged(current, previous); if (allColumnsShowFocus()) { @@ -3720,6 +3720,19 @@ void QTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &pr viewport()->update(currentRect); } } +#ifndef QT_NO_ACCESSIBILITY + if (QAccessible::isActive() && current.isValid()) { +#ifdef Q_WS_X11 + int entry = (visualIndex(current) + (header()?1:0))*current.model()->columnCount()+current.column() + 1; + QAccessible::updateAccessibility(this, entry, QAccessible::Focus); +#else + int entry = visualIndex(current) + 1; + if (header()) + ++entry; + QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus); +#endif + } +#endif } /*! @@ -3728,26 +3741,38 @@ void QTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &pr void QTreeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { + QAbstractItemView::selectionChanged(selected, deselected); #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive()) { // ### does not work properly for selection ranges. QModelIndex sel = selected.indexes().value(0); if (sel.isValid()) { +#ifdef Q_WS_X11 + int entry = (visualIndex(sel) + (header()?1:0))*sel.model()->columnCount()+sel.column() + 1; + Q_ASSERT(entry > 0); + QAccessible::updateAccessibility(this, entry, QAccessible::Selection); +#else int entry = visualIndex(sel) + 1; if (header()) ++entry; QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection); +#endif } QModelIndex desel = deselected.indexes().value(0); if (desel.isValid()) { +#ifdef Q_WS_X11 + int entry = (visualIndex(desel) + (header()?1:0))*desel.model()->columnCount()+desel.column() + 1; + Q_ASSERT(entry > 0); + QAccessible::updateAccessibility(this, entry, QAccessible::SelectionRemove); +#else int entry = visualIndex(desel) + 1; if (header()) ++entry; QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove); +#endif } } #endif - QAbstractItemView::selectionChanged(selected, deselected); } int QTreeView::visualIndex(const QModelIndex &index) const diff --git a/src/gui/itemviews/qtreeview.h b/src/gui/itemviews/qtreeview.h index 26c7315..b77da4e 100644 --- a/src/gui/itemviews/qtreeview.h +++ b/src/gui/itemviews/qtreeview.h @@ -219,6 +219,9 @@ protected: private: friend class QAccessibleItemView; + friend class QAccessibleTable2; + friend class QAccessibleTree; + friend class QAccessibleTable2Cell; int visualIndex(const QModelIndex &index) const; Q_DECLARE_PRIVATE(QTreeView) diff --git a/src/gui/itemviews/qtreeview_p.h b/src/gui/itemviews/qtreeview_p.h index b6d8458..a9dc452 100644 --- a/src/gui/itemviews/qtreeview_p.h +++ b/src/gui/itemviews/qtreeview_p.h @@ -78,7 +78,7 @@ struct QTreeViewItem Q_DECLARE_TYPEINFO(QTreeViewItem, Q_MOVABLE_TYPE); -class QTreeViewPrivate : public QAbstractItemViewPrivate +class Q_GUI_EXPORT QTreeViewPrivate : public QAbstractItemViewPrivate { Q_DECLARE_PUBLIC(QTreeView) public: @@ -223,6 +223,10 @@ public: inline void invalidateHeightCache(int item) const { viewItems[item].height = 0; } + inline int accessibleTable2Index(const QModelIndex &index) const { + return (viewIndex(index) + (header ? 1 : 0)) * model->columnCount()+index.column() + 1; + } + // used for spanning rows QVector<QPersistentModelIndex> spanningIndexes; diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index 467eb9b..3c57368 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -32,8 +32,6 @@ HEADERS += \ kernel/qshortcutmap_p.h \ kernel/qsizepolicy.h \ kernel/qpalette.h \ - kernel/qsound.h \ - kernel/qsound_p.h \ kernel/qstackedlayout.h \ kernel/qtooltip.h \ kernel/qwhatsthis.h \ @@ -50,7 +48,7 @@ HEADERS += \ kernel/qgesturemanager_p.h \ kernel/qsoftkeymanager_p.h \ kernel/qsoftkeymanager_common_p.h \ - kernel/qguiplatformplugin_p.h + kernel/qguiplatformplugin_p.h \ SOURCES += \ kernel/qaction.cpp \ @@ -72,7 +70,6 @@ SOURCES += \ kernel/qpalette.cpp \ kernel/qshortcut.cpp \ kernel/qshortcutmap.cpp \ - kernel/qsound.cpp \ kernel/qstackedlayout.cpp \ kernel/qtooltip.cpp \ kernel/qguivariant.cpp \ @@ -202,7 +199,71 @@ embedded { } } -!embedded:!x11:mac { +!qpa { + HEADERS += \ + kernel/qsound.h \ + kernel/qsound_p.h + + SOURCES += \ + kernel/qsound.cpp +} + +qpa { + HEADERS += \ + kernel/qgenericpluginfactory_qpa.h \ + kernel/qgenericplugin_qpa.h \ + kernel/qeventdispatcher_qpa_p.h \ + kernel/qwindowsysteminterface_qpa.h \ + kernel/qwindowsysteminterface_qpa_p.h \ + kernel/qplatformintegration_qpa.h \ + kernel/qplatformscreen_qpa.h \ + kernel/qplatformintegrationfactory_qpa_p.h \ + kernel/qplatformintegrationplugin_qpa.h \ + kernel/qplatformwindow_qpa.h \ + kernel/qplatformwindowformat_qpa.h \ + kernel/qplatformglcontext_qpa.h \ + kernel/qdesktopwidget_qpa_p.h \ + kernel/qplatformeventloopintegration_qpa.h \ + kernel/qplatformcursor_qpa.h \ + kernel/qplatformclipboard_qpa.h \ + kernel/qplatformnativeinterface_qpa.h + + SOURCES += \ + kernel/qapplication_qpa.cpp \ + kernel/qclipboard_qpa.cpp \ + kernel/qcursor_qpa.cpp \ + kernel/qdnd_qws.cpp \ + kernel/qdesktopwidget_qpa.cpp \ + kernel/qgenericpluginfactory_qpa.cpp \ + kernel/qgenericplugin_qpa.cpp \ + kernel/qkeymapper_qws.cpp \ + kernel/qwidget_qpa.cpp \ + kernel/qeventdispatcher_qpa.cpp \ + kernel/qwindowsysteminterface_qpa.cpp \ + kernel/qplatformintegration_qpa.cpp \ + kernel/qplatformscreen_qpa.cpp \ + kernel/qplatformintegrationfactory_qpa.cpp \ + kernel/qplatformintegrationplugin_qpa.cpp \ + kernel/qplatformwindow_qpa.cpp \ + kernel/qplatformwindowformat_qpa.cpp \ + kernel/qplatformeventloopintegration_qpa.cpp \ + kernel/qplatformglcontext_qpa.cpp \ + kernel/qplatformcursor_qpa.cpp \ + kernel/qplatformclipboard_qpa.cpp \ + kernel/qplatformnativeinterface_qpa.cpp \ + kernel/qsessionmanager_qpa.cpp + + contains(QT_CONFIG, glib) { + SOURCES += \ + kernel/qeventdispatcher_glib_qpa.cpp + HEADERS += \ + kernel/qeventdispatcher_glib_qpa_p.h + QMAKE_CXXFLAGS += $$QT_CFLAGS_GLIB + LIBS_PRIVATE +=$$QT_LIBS_GLIB + } +} + +!embedded:!qpa:!x11:mac { SOURCES += \ kernel/qclipboard_mac.cpp \ kernel/qmime_mac.cpp \ @@ -218,7 +279,8 @@ embedded { qcocoaapplicationdelegate_mac_p.h \ qmacgesturerecognizer_mac_p.h \ qmultitouch_mac_p.h \ - qcocoasharedwindowmethods_mac_p.h + qcocoasharedwindowmethods_mac_p.h \ + qcocoaintrospection_p.h OBJECTIVE_SOURCES += \ kernel/qcursor_mac.mm \ @@ -238,7 +300,8 @@ embedded { kernel/qeventdispatcher_mac.mm \ kernel/qcocoawindowcustomthemeframe_mac.mm \ kernel/qmacgesturerecognizer_mac.mm \ - kernel/qmultitouch_mac.mm + kernel/qmultitouch_mac.mm \ + kernel/qcocoaintrospection_mac.mm HEADERS += \ kernel/qt_cocoa_helpers_mac_p.h \ diff --git a/src/gui/kernel/mac.pri b/src/gui/kernel/mac.pri index 1538510..21acd06 100644 --- a/src/gui/kernel/mac.pri +++ b/src/gui/kernel/mac.pri @@ -1,4 +1,4 @@ -!x11:!embedded:mac { +!x11:!embedded:!qpa:mac { LIBS_PRIVATE += -framework Carbon -lz *-mwerks:INCLUDEPATH += compat } diff --git a/src/gui/kernel/qactiongroup.cpp b/src/gui/kernel/qactiongroup.cpp index 8029dfb..cbab6aa 100644 --- a/src/gui/kernel/qactiongroup.cpp +++ b/src/gui/kernel/qactiongroup.cpp @@ -108,8 +108,8 @@ void QActionGroupPrivate::_q_actionHovered() \ingroup mainwindow-classes - In some situations it is useful to group actions together. For - example, if you have a \gui{Left Align} action, a \gui{Right + In some situations it is useful to group QAction objects together. + For example, if you have a \gui{Left Align} action, a \gui{Right Align} action, a \gui{Justify} action, and a \gui{Center} action, only one of these actions should be active at any one time. One simple way of achieving this is to group the actions together in diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 34ce9a8..15d37c3 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -157,8 +157,6 @@ QT_BEGIN_NAMESPACE Q_CORE_EXPORT void qt_call_post_routines(); -int QApplicationPrivate::app_compile_version = 0x040000; //we don't know exactly, but it's at least 4.0.0 - QApplication::Type qt_appType=QApplication::Tty; QApplicationPrivate *QApplicationPrivate::self = 0; @@ -173,8 +171,8 @@ bool QApplicationPrivate::autoSipEnabled = false; bool QApplicationPrivate::autoSipEnabled = true; #endif -QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, QApplication::Type type) - : QCoreApplicationPrivate(argc, argv) +QApplicationPrivate::QApplicationPrivate(int &argc, char **argv, QApplication::Type type, int flags) + : QCoreApplicationPrivate(argc, argv, flags) { application_type = type; qt_appType = type; @@ -456,6 +454,9 @@ QPalette *QApplicationPrivate::sys_pal = 0; // default system palette QPalette *QApplicationPrivate::set_pal = 0; // default palette set by programmer QGraphicsSystem *QApplicationPrivate::graphics_system = 0; // default graphics system +#if defined(Q_WS_QPA) +QPlatformIntegration *QApplicationPrivate::platform_integration = 0; +#endif QString QApplicationPrivate::graphics_system_name; // graphics system id - for delayed initialization bool QApplicationPrivate::runtime_graphics_system = false; @@ -518,7 +519,7 @@ inline bool QApplicationPrivate::isAlien(QWidget *widget) { if (!widget) return false; -#if defined(Q_WS_QWS) +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) return !widget->isWindow() # ifdef Q_BACKINGSTORE_SUBSURFACES && !(widget->d_func()->maybeTopData() && widget->d_func()->maybeTopData()->windowSurface) @@ -728,12 +729,12 @@ void QApplicationPrivate::process_cmdline() */ QApplication::QApplication(int &argc, char **argv) - : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient)) + : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, 0x040000)) { Q_D(QApplication); d->construct(); } QApplication::QApplication(int &argc, char **argv, int _internal) - : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient)) -{ Q_D(QApplication); d->construct(); QApplicationPrivate::app_compile_version = _internal;} + : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, _internal)) +{ Q_D(QApplication); d->construct(); } /*! @@ -762,12 +763,12 @@ QApplication::QApplication(int &argc, char **argv, int _internal) */ QApplication::QApplication(int &argc, char **argv, bool GUIenabled ) - : QCoreApplication(*new QApplicationPrivate(argc, argv, GUIenabled ? GuiClient : Tty)) + : QCoreApplication(*new QApplicationPrivate(argc, argv, GUIenabled ? GuiClient : Tty, 0x040000)) { Q_D(QApplication); d->construct(); } QApplication::QApplication(int &argc, char **argv, bool GUIenabled , int _internal) - : QCoreApplication(*new QApplicationPrivate(argc, argv, GUIenabled ? GuiClient : Tty)) -{ Q_D(QApplication); d->construct(); QApplicationPrivate::app_compile_version = _internal;} + : QCoreApplication(*new QApplicationPrivate(argc, argv, GUIenabled ? GuiClient : Tty, _internal)) +{ Q_D(QApplication); d->construct();} @@ -785,12 +786,12 @@ QApplication::QApplication(int &argc, char **argv, bool GUIenabled , int _intern \c -qws option). */ QApplication::QApplication(int &argc, char **argv, Type type) - : QCoreApplication(*new QApplicationPrivate(argc, argv, type)) + : QCoreApplication(*new QApplicationPrivate(argc, argv, type, 0x040000)) { Q_D(QApplication); d->construct(); } QApplication::QApplication(int &argc, char **argv, Type type , int _internal) - : QCoreApplication(*new QApplicationPrivate(argc, argv, type)) -{ Q_D(QApplication); d->construct(); QApplicationPrivate::app_compile_version = _internal;} + : QCoreApplication(*new QApplicationPrivate(argc, argv, type, _internal)) +{ Q_D(QApplication); d->construct(); } #if defined(Q_WS_X11) && !defined(QT_NO_EGL) static int qt_matchLibraryName(dl_phdr_info *info, size_t, void *data) @@ -896,7 +897,7 @@ static char *aargv[] = { (char*)"unknown", 0 }; This function is only available on X11. */ QApplication::QApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE colormap) - : QCoreApplication(*new QApplicationPrivate(aargc, aargv, GuiClient)) + : QCoreApplication(*new QApplicationPrivate(aargc, aargv, GuiClient, 0x040000)) { if (! dpy) qWarning("QApplication: Invalid Display* argument"); @@ -905,7 +906,7 @@ QApplication::QApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE colormap) } QApplication::QApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE colormap, int _internal) - : QCoreApplication(*new QApplicationPrivate(aargc, aargv, GuiClient)) + : QCoreApplication(*new QApplicationPrivate(aargc, aargv, GuiClient, _internal)) { if (! dpy) qWarning("QApplication: Invalid Display* argument"); @@ -930,7 +931,7 @@ QApplication::QApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE colormap, */ QApplication::QApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE colormap) - : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient)) + : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, 0x040000)) { if (! dpy) qWarning("QApplication: Invalid Display* argument"); @@ -940,7 +941,7 @@ QApplication::QApplication(Display *dpy, int &argc, char **argv, QApplication::QApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE colormap, int _internal) - : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient)) + : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, _internal)) { if (! dpy) qWarning("QApplication: Invalid Display* argument"); @@ -970,10 +971,11 @@ void QApplicationPrivate::initialize() QWidgetPrivate::mapper = new QWidgetMapper; QWidgetPrivate::allWidgets = new QWidgetSet; -#if !defined(Q_WS_X11) && !defined(Q_WS_QWS) +#if !defined(Q_WS_X11) && !defined(Q_WS_QWS) && !defined(Q_WS_QPA) // initialize the graphics system - on X11 this is initialized inside // qt_init() in qapplication_x11.cpp because of several reasons. // On QWS, the graphics system is set by the QScreen plugin. + // We don't use graphics systems in Qt QPA graphics_system = QGraphicsSystemFactory::create(graphics_system_name); #endif @@ -1290,8 +1292,8 @@ bool QApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventLis || event->type() == QEvent::LanguageChange || event->type() == QEvent::UpdateSoftKeys || event->type() == QEvent::InputMethod)) { - for (int i = 0; i < postedEvents->size(); ++i) { - const QPostEvent &cur = postedEvents->at(i); + for (QPostEventList::const_iterator it = postedEvents->constBegin(); it != postedEvents->constEnd(); ++it) { + const QPostEvent &cur = *it; if (cur.receiver != receiver || cur.event == 0 || cur.event->type() != event->type()) continue; if (cur.event->type() == QEvent::LayoutRequest @@ -1651,14 +1653,18 @@ QStyle* QApplication::setStyle(const QString& style) void QApplication::setGraphicsSystem(const QString &system) { -#ifdef QT_GRAPHICSSYSTEM_RUNTIME +#ifdef Q_WS_QPA + Q_UNUSED(system); +#else +# ifdef QT_GRAPHICSSYSTEM_RUNTIME if (QApplicationPrivate::graphics_system_name == QLatin1String("runtime")) { QRuntimeGraphicsSystem *r = static_cast<QRuntimeGraphicsSystem *>(QApplicationPrivate::graphics_system); r->setGraphicsSystem(system); } else -#endif +# endif QApplicationPrivate::graphics_system_name = system; +#endif } /*! @@ -2784,7 +2790,7 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave) { for (int i = 0; i < leaveList.size(); ++i) { w = leaveList.at(i); if (!QApplication::activeModalWidget() || QApplicationPrivate::tryModalHelper(w, 0)) { -#if defined(Q_WS_WIN) || defined(Q_WS_X11) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_MAC) if (leaveAfterRelease == w) leaveAfterRelease = 0; #endif @@ -2815,7 +2821,7 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave) { // Update cursor for alien/graphics widgets. const bool enterOnAlien = (enter && (isAlien(enter) || enter->testAttribute(Qt::WA_DontShowOnScreen))); -#if defined(Q_WS_X11) +#if defined(Q_WS_X11) || defined(Q_WS_QPA) //Whenever we leave an alien widget on X11, we need to reset its nativeParentWidget()'s cursor. // This is not required on Windows as the cursor is reset on every single mouse move. QWidget *parentOfLeavingCursor = 0; @@ -2839,7 +2845,15 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave) { if (!parentOfLeavingCursor->window()->graphicsProxyWidget()) #endif { +#if defined(Q_WS_X11) qt_x11_enforce_cursor(parentOfLeavingCursor,true); +#elif defined(Q_WS_QPA) + if (enter == QApplication::desktop()) { + qt_qpa_set_cursor(enter, true); + } else { + qt_qpa_set_cursor(parentOfLeavingCursor, true); + } +#endif } } #endif @@ -2863,6 +2877,8 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave) { qt_x11_enforce_cursor(cursorWidget, true); #elif defined(Q_OS_SYMBIAN) qt_symbian_set_cursor(cursorWidget, true); +#elif defined(Q_WS_QPA) + qt_qpa_set_cursor(cursorWidget, true); #endif } } @@ -3145,13 +3161,11 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, // Dispatch enter/leave if: // 1) the mouse grabber is an alien widget // 2) the button is released on an alien widget - QWidget *enter = 0; if (nativeGuard) enter = alienGuard ? alienWidget : nativeWidget; else // The receiver is typically deleted on mouse release with drag'n'drop. enter = QApplication::widgetAt(event->globalPos()); - dispatchEnterLeave(enter, leaveAfterRelease); leaveAfterRelease = 0; lastMouseReceiver = enter; @@ -3167,7 +3181,7 @@ bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, return result; } -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_MAC) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_MAC) || defined(Q_WS_QPA) /* This function should only be called when the widget changes visibility, i.e. when the \a widget is shown, hidden or deleted. This function does nothing @@ -3179,7 +3193,7 @@ extern QWidget *qt_button_down; void QApplicationPrivate::sendSyntheticEnterLeave(QWidget *widget) { #ifndef QT_NO_CURSOR -#ifdef Q_WS_QWS +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) if (!widget || widget->isWindow()) return; #else @@ -3307,7 +3321,7 @@ bool QApplication::desktopSettingsAware() one of the above events. If no keys are being held Qt::NoModifier is returned. - \sa mouseButtons() + \sa mouseButtons(), queryKeyboardModifiers() */ Qt::KeyboardModifiers QApplication::keyboardModifiers() @@ -3316,6 +3330,25 @@ Qt::KeyboardModifiers QApplication::keyboardModifiers() } /*! + \fn Qt::KeyboardModifiers QApplication::queryKeyboardModifiers() + + Queries and returns the state of the modifier keys on the keyboard. + Unlike keyboardModifiers, this method returns the actual keys held + on the input device at the time of calling the method. + + It does not rely on the keypress events having been received by this + process, which makes it possible to check the modifiers while moving + a window, for instance. Note that in most cases, you should use + keyboardModifiers(), which is faster and more accurate since it contains + the state of the modifiers as they were when the currently processed + event was received. + + \sa keyboardModifiers() + + \since 4.8 +*/ + +/*! Returns the current state of the buttons on the mouse. The current state is updated syncronously as the event queue is emptied of events that will spontaneously change the mouse state (QEvent::MouseButtonPress and @@ -3698,15 +3731,6 @@ void QApplication::changeOverrideCursor(const QCursor &cursor) if (qApp->d_func()->cursor_list.isEmpty()) return; qApp->d_func()->cursor_list.removeFirst(); -#ifdef QT_MAC_USE_COCOA - // We use native NSCursor stacks in Cocoa. The currentCursor is the - // top of this stack. So to avoid flickering of cursor, we have to - // change the cusor instead of pop-ing the existing OverrideCursor - // and pushing the new one. - qApp->d_func()->cursor_list.prepend(cursor); - qt_cocoaChangeOverrideCursor(cursor); - return; -#endif setOverrideCursor(cursor); } #endif @@ -3773,11 +3797,6 @@ bool QApplication::notify(QObject *receiver, QEvent *e) d->checkReceiverThread(receiver); #endif -#ifdef QT3_SUPPORT - if (e->type() == QEvent::ChildRemoved && !receiver->d_func()->pendingChildInsertedEvents.isEmpty()) - receiver->d_func()->removePendingChildInsertedEvents(static_cast<QChildEvent *>(e)->child()); -#endif // QT3_SUPPORT - // capture the current mouse/keyboard state if(e->spontaneous()) { if (e->type() == QEvent::KeyPress @@ -4442,6 +4461,24 @@ bool QApplication::notify(QObject *receiver, QEvent *e) break; } #endif // QT_NO_GESTURES +#ifdef QT_MAC_USE_COCOA + case QEvent::Enter: + if (receiver->isWidgetType()) { + QWidget *w = static_cast<QWidget *>(receiver); + if (w->testAttribute(Qt::WA_AcceptTouchEvents)) + qt_widget_private(w)->registerTouchWindow(true); + } + res = d->notify_helper(receiver, e); + break; + case QEvent::Leave: + if (receiver->isWidgetType()) { + QWidget *w = static_cast<QWidget *>(receiver); + if (w->testAttribute(Qt::WA_AcceptTouchEvents)) + qt_widget_private(w)->registerTouchWindow(false); + } + res = d->notify_helper(receiver, e); + break; +#endif default: res = d->notify_helper(receiver, e); break; @@ -5880,12 +5917,12 @@ Q_GUI_EXPORT void qt_translateRawTouchEvent(QWidget *window, #ifndef QT_NO_GESTURES QGestureManager* QGestureManager::instance() { - if (QApplicationPrivate *qAppPriv = QApplicationPrivate::instance()) { - if (!qAppPriv->gestureManager) - qAppPriv->gestureManager = new QGestureManager(qApp); - return qAppPriv->gestureManager; - } - return 0; + QApplicationPrivate *qAppPriv = QApplicationPrivate::instance(); + if (!qAppPriv) + return 0; + if (!qAppPriv->gestureManager) + qAppPriv->gestureManager = new QGestureManager(qApp); + return qAppPriv->gestureManager; } #endif // QT_NO_GESTURES @@ -6109,6 +6146,8 @@ QPixmap QApplicationPrivate::getPixmapCursor(Qt::CursorShape cshape) default: break; } +#else + Q_UNUSED(cshape); #endif return QPixmap(); } diff --git a/src/gui/kernel/qapplication.h b/src/gui/kernel/qapplication.h index cbf0117..1548849 100644 --- a/src/gui/kernel/qapplication.h +++ b/src/gui/kernel/qapplication.h @@ -79,6 +79,8 @@ template <typename T> class QList; class QLocale; #if defined(Q_WS_QWS) class QDecoration; +#elif defined(Q_WS_QPA) +class QPlatformNativeInterface; #endif #if defined(Q_OS_SYMBIAN) class QSymbianEvent; @@ -123,15 +125,15 @@ public: #endif #ifndef qdoc - QApplication(int &argc, char **argv, int = QT_VERSION); - QApplication(int &argc, char **argv, bool GUIenabled, int = QT_VERSION); - QApplication(int &argc, char **argv, Type, int = QT_VERSION); + QApplication(int &argc, char **argv, int = ApplicationFlags); + QApplication(int &argc, char **argv, bool GUIenabled, int = ApplicationFlags); + QApplication(int &argc, char **argv, Type, int = ApplicationFlags); #if defined(Q_WS_X11) - QApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0, int = QT_VERSION); - QApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0, int = QT_VERSION); + QApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0, int = ApplicationFlags); + QApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0, int = ApplicationFlags); #endif #if defined(Q_OS_SYMBIAN) - QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv, int = QT_VERSION); + QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv, int = ApplicationFlags); #endif #endif virtual ~QApplication(); @@ -196,6 +198,7 @@ public: static void alert(QWidget *widget, int duration = 0); static Qt::KeyboardModifiers keyboardModifiers(); + static Qt::KeyboardModifiers queryKeyboardModifiers(); static Qt::MouseButtons mouseButtons(); static void setDesktopSettingsAware(bool); @@ -254,6 +257,10 @@ public: #endif #endif +#if defined(Q_WS_QPA) + static QPlatformNativeInterface *platformNativeInterface(); +#endif + #if defined(Q_WS_WIN) void winFocus(QWidget *, bool); diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index 890d527..1f75f09 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -165,6 +165,7 @@ QT_BEGIN_NAMESPACE //for qt_mac.h QPaintDevice *qt_mac_safe_pdev = 0; QList<QMacWindowChangeEvent*> *QMacWindowChangeEvent::change_events = 0; +QPointer<QWidget> topLevelAt_cache = 0; /***************************************************************************** Internal variables and functions @@ -192,7 +193,6 @@ static bool qt_mac_previous_press_in_popup_mode = false; static bool qt_mac_no_click_through_mode = false; static int tablet_button_state = 0; #endif -QPointer<QWidget> qt_mouseover; #if defined(QT_DEBUG) static bool appNoGrab = false; // mouse/keyboard grabbing #endif @@ -216,11 +216,11 @@ extern bool qt_mac_can_clickThrough(const QWidget *); //qwidget_mac.cpp extern bool qt_mac_is_macdrawer(const QWidget *); //qwidget_mac.cpp extern OSWindowRef qt_mac_window_for(const QWidget*); //qwidget_mac.cpp extern QWidget *qt_mac_find_window(OSWindowRef); //qwidget_mac.cpp -extern void qt_mac_set_cursor(const QCursor *, const QPoint &); //qcursor_mac.cpp +extern void qt_mac_set_cursor(const QCursor *); //qcursor_mac.cpp extern bool qt_mac_is_macsheet(const QWidget *); //qwidget_mac.cpp -extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp extern void qt_mac_command_set_enabled(MenuRef, UInt32, bool); //qmenu_mac.cpp extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); // qapplication.cpp +extern void qt_mac_update_cursor(); // qcursor_mac.mm // Forward Decls void onApplicationWindowChangedActivation( QWidget*widget, bool activated ); @@ -549,7 +549,7 @@ void qt_mac_update_os_settings() FontMap("QTipLabel", kThemeSmallSystemFont), FontMap("QLabel", kThemeSystemFont), FontMap("QToolButton", kThemeSmallSystemFont), - FontMap("QMenuItem", kThemeMenuItemCmdKeyFont), // It doesn't exist, but its unique. + FontMap("QMenuItem", kThemeMenuItemFont), // It doesn't exist, but its unique. FontMap("QComboLineEdit", kThemeViewsFont), // It doesn't exist, but its unique. FontMap("QSmallFont", kThemeSmallSystemFont), // It doesn't exist, but its unique. FontMap("QMiniFont", kThemeMiniSystemFont), // It doesn't exist, but its unique. @@ -714,6 +714,7 @@ void qt_event_request_showsheet(QWidget *w) { Q_ASSERT(qt_mac_is_macsheet(w)); #ifdef QT_MAC_USE_COCOA + w->repaint(); [NSApp beginSheet:qt_mac_window_for(w) modalForWindow:qt_mac_window_for(w->parentWidget()) modalDelegate:nil didEndSelector:nil contextInfo:0]; #else @@ -1243,8 +1244,10 @@ void qt_init(QApplicationPrivate *priv, int) // Cocoa application delegate #ifdef QT_MAC_USE_COCOA NSApplication *cocoaApp = [QNSApplication sharedApplication]; + qt_redirectNSApplicationSendEvent(); + QMacCocoaAutoReleasePool pool; - NSObject *oldDelegate = [cocoaApp delegate]; + id oldDelegate = [cocoaApp delegate]; QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate]; Q_ASSERT(newDelegate); [newDelegate setQtPrivate:priv]; @@ -1362,43 +1365,16 @@ void QApplication::setMainWidget(QWidget *mainWidget) /***************************************************************************** QApplication cursor stack *****************************************************************************/ -#ifdef QT_MAC_USE_COCOA -void QApplicationPrivate::disableUsageOfCursorRects(bool disable) -{ - // In Cocoa there are two competing ways of setting the cursor; either - // by using cursor rects (see qcocoaview_mac.mm), or by pushing/popping - // the cursor manually. When we use override cursors, it makes most sense - // to use the latter. But then we need to tell cocoa to stop using the - // first approach so it doesn't change the cursor back when hovering over - // a cursor rect: - QWidgetList topLevels = qApp->topLevelWidgets(); - for (int i=0; i<topLevels.size(); ++i) { - if (NSWindow *window = qt_mac_window_for(topLevels.at(i))) - disable ? [window disableCursorRects] : [window enableCursorRects]; - } -} - -void QApplicationPrivate::updateOverrideCursor() -{ - // Sometimes Cocoa forgets that we have set a Cursor - // manually. In those cases, remind it again: - if (QCursor *override = qApp->overrideCursor()) - [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(*override)) set]; -} -#endif void QApplication::setOverrideCursor(const QCursor &cursor) { qApp->d_func()->cursor_list.prepend(cursor); #ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - if (qApp->d_func()->cursor_list.size() == 1) - qApp->d_func()->disableUsageOfCursorRects(true); - [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) push]; + qt_mac_update_cursor(); #else if (qApp && qApp->activeWindow()) - qt_mac_set_cursor(&qApp->d_func()->cursor_list.first(), QCursor::pos()); + qt_mac_set_cursor(&qApp->d_func()->cursor_list.first()); #endif } @@ -1409,19 +1385,21 @@ void QApplication::restoreOverrideCursor() qApp->d_func()->cursor_list.removeFirst(); #ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - [NSCursor pop]; - if (qApp->d_func()->cursor_list.isEmpty()) - qApp->d_func()->disableUsageOfCursorRects(false); + qt_mac_update_cursor(); #else if (qApp && qApp->activeWindow()) { const QCursor def(Qt::ArrowCursor); - qt_mac_set_cursor(qApp->d_func()->cursor_list.isEmpty() ? &def : &qApp->d_func()->cursor_list.first(), QCursor::pos()); + qt_mac_set_cursor(qApp->d_func()->cursor_list.isEmpty() ? &def : &qApp->d_func()->cursor_list.first()); } #endif } #endif // QT_NO_CURSOR +Qt::KeyboardModifiers QApplication::queryKeyboardModifiers() +{ + return qt_mac_get_modifiers(GetCurrentEventKeyModifiers()); +} + QWidget *QApplication::topLevelAt(const QPoint &p) { #ifndef QT_MAC_USE_COCOA @@ -1429,30 +1407,54 @@ QWidget *QApplication::topLevelAt(const QPoint &p) qt_mac_window_at(p.x(), p.y(), &widget); return widget; #else + // Use a cache to avoid iterate through the whole list of windows for all + // calls to to topLevelAt. We e.g. do this for each and every mouse + // move since we need to find the widget under mouse: + if (topLevelAt_cache && topLevelAt_cache->frameGeometry().contains(p)) + return topLevelAt_cache; + + // INVARIANT: Cache miss. Go through the list if windows instead: + QMacCocoaAutoReleasePool pool; + NSPoint cocoaPoint = flipPoint(p); NSInteger windowCount; NSCountWindows(&windowCount); if (windowCount <= 0) return 0; // There's no window to find! - QMacCocoaAutoReleasePool pool; - NSPoint cocoaPoint = flipPoint(p); + QVarLengthArray<NSInteger> windowList(windowCount); NSWindowList(windowCount, windowList.data()); + int firstQtWindowFound = -1; for (int i = 0; i < windowCount; ++i) { NSWindow *window = [NSApp windowWithWindowNumber:windowList[i]]; - if (window && NSPointInRect(cocoaPoint, [window frame])) { + if (window) { QWidget *candidateWindow = [window QT_MANGLE_NAMESPACE(qt_qwidget)]; - // Check to see if there's a hole in the window where the mask is. - // If there is, we should just continue to see if there is a window below. - if (candidateWindow && !candidateWindow->mask().isEmpty()) { - QPoint localPoint = candidateWindow->mapFromGlobal(p); - if (!candidateWindow->mask().contains(localPoint)) { - continue; + if (candidateWindow && firstQtWindowFound == -1) + firstQtWindowFound = i; + + if (NSPointInRect(cocoaPoint, [window frame])) { + // Check to see if there's a hole in the window where the mask is. + // If there is, we should just continue to see if there is a window below. + if (candidateWindow && !candidateWindow->mask().isEmpty()) { + QPoint localPoint = candidateWindow->mapFromGlobal(p); + if (!candidateWindow->mask().contains(localPoint)) + continue; + else + return candidateWindow; + } else { + if (i == firstQtWindowFound) { + // The cache will only work when the window under mouse is + // top most (that is, not partially obscured by other windows. + // And we only set it if no mask is present to optimize for the common case: + topLevelAt_cache = candidateWindow; + } + return candidateWindow; } } - return candidateWindow; } } - return 0; // Couldn't find a window at this point + + topLevelAt_cache = 0; + return 0; #endif } @@ -1478,8 +1480,8 @@ void QApplicationPrivate::enterModal_sys(QWidget *widget) if (!qt_modal_stack) qt_modal_stack = new QWidgetList; - dispatchEnterLeave(0, qt_mouseover); - qt_mouseover = 0; + dispatchEnterLeave(0, qt_last_mouse_receiver); + qt_last_mouse_receiver = 0; qt_modal_stack->insert(0, widget); if (!app_do_modal) @@ -1510,8 +1512,8 @@ void QApplicationPrivate::leaveModal_sys(QWidget *widget) w = grabber; else w = QApplication::widgetAt(p.x(), p.y()); - dispatchEnterLeave(w, qt_mouseover); // send synthetic enter event - qt_mouseover = w; + dispatchEnterLeave(w, qt_last_mouse_receiver); // send synthetic enter event + qt_last_mouse_receiver = w; } #ifdef QT_MAC_USE_COCOA if (!qt_mac_is_macsheet(widget)) @@ -1936,7 +1938,7 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event } } } - qt_mac_set_cursor(&cursor, QPoint(where.h, where.v)); + qt_mac_set_cursor(&cursor); } //This mouse button state stuff looks like this on purpose @@ -2130,20 +2132,20 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event QWidget * const enterLeaveWidget = (inPopupMode || ekind == kEventMouseUp) ? QApplication::widgetAt(where.h, where.v) : static_cast<QWidget*>(widget); - if ((QWidget *) qt_mouseover != enterLeaveWidget || inNonClientArea) { + if ((QWidget *) qt_last_mouse_receiver != enterLeaveWidget || inNonClientArea) { #ifdef DEBUG_MOUSE_MAPS qDebug("Entering: %p - %s (%s), Leaving %s (%s)", (QWidget*)enterLeaveWidget, enterLeaveWidget ? enterLeaveWidget->metaObject()->className() : "none", enterLeaveWidget ? enterLeaveWidget->objectName().toLocal8Bit().constData() : "", - qt_mouseover ? qt_mouseover->metaObject()->className() : "none", - qt_mouseover ? qt_mouseover->objectName().toLocal8Bit().constData() : ""); + qt_last_mouse_receiver ? qt_last_mouse_receiver->metaObject()->className() : "none", + qt_last_mouse_receiver ? qt_last_mouse_receiver->objectName().toLocal8Bit().constData() : ""); #endif QWidget * const mouseGrabber = QWidget::mouseGrabber(); if (inPopupMode) { QWidget *enter = enterLeaveWidget; - QWidget *leave = qt_mouseover; + QWidget *leave = qt_last_mouse_receiver; if (mouseGrabber) { QWidget * const popupWidget = qApp->activePopupWidget(); if (leave == popupWidget) @@ -2153,15 +2155,15 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event if ((enter == mouseGrabber && leave == popupWidget) || (leave == mouseGrabber && enter == popupWidget)) { QApplicationPrivate::dispatchEnterLeave(enter, leave); - qt_mouseover = enter; + qt_last_mouse_receiver = enter; } } else { QApplicationPrivate::dispatchEnterLeave(enter, leave); - qt_mouseover = enter; + qt_last_mouse_receiver = enter; } - } else if ((!qt_button_down || !qt_mouseover) && !mouseGrabber && !leaveAfterRelease) { - QApplicationPrivate::dispatchEnterLeave(enterLeaveWidget, qt_mouseover); - qt_mouseover = enterLeaveWidget; + } else if ((!qt_button_down || !qt_last_mouse_receiver) && !mouseGrabber && !leaveAfterRelease) { + QApplicationPrivate::dispatchEnterLeave(enterLeaveWidget, qt_last_mouse_receiver); + qt_last_mouse_receiver = enterLeaveWidget; } } break; } @@ -2238,7 +2240,7 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event if (leaveAfterRelease) { QWidget *enter = QApplication::widgetAt(where.h, where.v); QApplicationPrivate::dispatchEnterLeave(enter, leaveAfterRelease); - qt_mouseover = enter; + qt_last_mouse_receiver = enter; leaveAfterRelease = 0; } @@ -2501,7 +2503,7 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event void QApplicationPrivate::qt_initAfterNSAppStarted() { setupAppleEvents(); - updateOverrideCursor(); + qt_mac_update_cursor(); } void QApplicationPrivate::setupAppleEvents() @@ -2608,25 +2610,24 @@ OSStatus QApplicationPrivate::globalAppleEventProcessor(const AppleEvent *ae, Ap /*! \fn bool QApplication::macEventFilter(EventHandlerCallRef caller, EventRef event) - \warning This virtual function is only implemented under Mac OS X when against Carbon. + \warning This virtual function is only used under Mac OS X, and behaves different + depending on if Qt is based on Carbon or Cocoa. - If you create an application that inherits QApplication and reimplement + For the Carbon port, If you create an application that inherits QApplication and reimplement this function, you get direct access to all Carbon Events that Qt registers for from Mac OS X with this function being called with the \a caller and the \a event. + For the Cocoa port, If you create an application that inherits QApplication and reimplement + this function, you get direct access to all Cocoa Events that Qt receives + from Mac OS X with this function being called with the \a caller being 0 and + the \a event being an NSEvent pointer: + + NSEvent *e = reinterpret_cast<NSEvent *>(event); + Return true if you want to stop the event from being processed. Return false for normal event dispatching. The default implementation returns false. - - Cocoa uses a different event system which means this function is NOT CALLED - when building Qt against Cocoa. If you want similar functionality subclass - NSApplication and reimplement the sendEvent: message to handle all the - NSEvents. You also will need to to instantiate your custom NSApplication - before creating a QApplication. See \l - {http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/Reference/Reference.html}{Apple's - NSApplication Reference} for more information. - */ bool QApplication::macEventFilter(EventHandlerCallRef, EventRef) { @@ -3077,7 +3078,7 @@ void onApplicationWindowChangedActivation(QWidget *widget, bool activated) } QMenuBar::macUpdateMenuBar(); - QApplicationPrivate::updateOverrideCursor(); + qt_mac_update_cursor(); #else Q_UNUSED(widget); Q_UNUSED(activated); @@ -3107,15 +3108,12 @@ void onApplicationChangedActivation( bool activated ) } if (!app->activeWindow()) { -#if QT_MAC_USE_COCOA - OSWindowRef wp = [NSApp keyWindow]; -#else - OSWindowRef wp = ActiveNonFloatingWindow(); -#endif + OSWindowRef wp = [NSApp keyWindow]; if (QWidget *tmp_w = qt_mac_find_window(wp)) app->setActiveWindow(tmp_w); } QMenuBar::macUpdateMenuBar(); + qt_mac_update_cursor(); } else { // de-activated QApplicationPrivate *priv = [[QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate] qAppPrivate]; while (priv->inPopupMode()) diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 99822e2..e1252a9 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -65,8 +65,10 @@ #include "QtCore/qhash.h" #include "QtCore/qpointer.h" #include "private/qcoreapplication_p.h" -#include "private/qshortcutmap_p.h" +#include "QtGui/private/qshortcutmap_p.h" #include <private/qthread_p.h> +#include "QtCore/qpoint.h" +#include <QTime> #ifdef Q_WS_QWS #include "QtGui/qscreen_qws.h" #include <private/qgraphicssystem_qws_p.h> @@ -74,6 +76,11 @@ #ifdef Q_OS_SYMBIAN #include <w32std.h> #endif +#ifdef Q_WS_QPA +#include <QWindowSystemInterface> +#include "qwindowsysteminterface_qpa_p.h" +#include "QtGui/qplatformintegration_qpa.h" +#endif QT_BEGIN_NAMESPACE @@ -291,7 +298,7 @@ class Q_GUI_EXPORT QApplicationPrivate : public QCoreApplicationPrivate { Q_DECLARE_PUBLIC(QApplication) public: - QApplicationPrivate(int &argc, char **argv, QApplication::Type type); + QApplicationPrivate(int &argc, char **argv, QApplication::Type type, int flags); ~QApplicationPrivate(); #if defined(Q_WS_X11) @@ -312,10 +319,18 @@ public: static QString desktopStyleKey(); static QGraphicsSystem *graphicsSystem() -#if !defined(Q_WS_QWS) - { return graphics_system; } -#else +#if defined(Q_WS_QWS) { return QScreen::instance()->graphicsSystem(); } +#else + { return graphics_system; } +#endif + +#if defined(Q_WS_QPA) + static QPlatformIntegration *platformIntegration() + { return platform_integration; } + + static QAbstractEventDispatcher *qt_qpa_core_dispatcher() + { return QCoreApplication::instance()->d_func()->threadData->eventDispatcher; } #endif void createEventDispatcher(); @@ -418,6 +433,9 @@ public: static QGraphicsSystem *graphics_system; static QString graphics_system_name; static bool runtime_graphics_system; +#ifdef Q_WS_QPA + static QPlatformIntegration *platform_integration; +#endif private: static QFont *app_font; // private for a reason! Always use QApplication::font() instead! @@ -471,12 +489,36 @@ public: #ifdef QT_MAC_USE_COCOA static void qt_initAfterNSAppStarted(); static void setupAppleEvents(); - static void updateOverrideCursor(); - static void disableUsageOfCursorRects(bool disable); #endif static bool qt_mac_apply_settings(); #endif +#ifdef Q_WS_QPA + static void processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e); + static void processKeyEvent(QWindowSystemInterfacePrivate::KeyEvent *e); + static void processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e); + static void processTouchEvent(QWindowSystemInterfacePrivate::TouchEvent *e); + + static void processCloseEvent(QWindowSystemInterfacePrivate::CloseEvent *e); + + static void processGeometryChangeEvent(QWindowSystemInterfacePrivate::GeometryChangeEvent *e); + + static void processEnterEvent(QWindowSystemInterfacePrivate::EnterEvent *e); + static void processLeaveEvent(QWindowSystemInterfacePrivate::LeaveEvent *e); + + static void processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e); + + static void processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e); + +// static void reportScreenCount(int count); + static void reportScreenCount(QWindowSystemInterfacePrivate::ScreenCountEvent *e); +// static void reportGeometryChange(int screenIndex); + static void reportGeometryChange(QWindowSystemInterfacePrivate::ScreenGeometryEvent *e); +// static void reportAvailableGeometryChange(int screenIndex); + static void reportAvailableGeometryChange(QWindowSystemInterfacePrivate::ScreenAvailableGeometryEvent *e); + +#endif + #ifdef Q_WS_QWS QPointer<QWSManager> last_manager; QWSServerCleaner qwsServerCleaner; @@ -492,8 +534,6 @@ public: static QString styleOverride; - static int app_compile_version; - #ifdef QT_KEYPAD_NAVIGATION static QWidget *oldEditFocus; static Qt::NavigationMode navigationMode; @@ -524,7 +564,7 @@ public: void _q_aboutToQuit(); #endif -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) || defined(Q_WS_QPA) void sendSyntheticEnterLeave(QWidget *widget); #endif @@ -634,6 +674,8 @@ Q_GUI_EXPORT void qt_translateRawTouchEvent(QWidget *window, extern void qt_x11_enforce_cursor(QWidget *); #elif defined(Q_OS_SYMBIAN) extern void qt_symbian_set_cursor(QWidget *, bool); +#elif defined (Q_WS_QPA) + extern void qt_qpa_set_cursor(QWidget *, bool); #endif QT_END_NAMESPACE diff --git a/src/gui/kernel/qapplication_qpa.cpp b/src/gui/kernel/qapplication_qpa.cpp new file mode 100644 index 0000000..b23c15c --- /dev/null +++ b/src/gui/kernel/qapplication_qpa.cpp @@ -0,0 +1,984 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qapplication_p.h" +#include "qcolormap.h" +#include "qpixmapcache.h" +#if !defined(QT_NO_GLIB) +#include "qeventdispatcher_glib_qpa_p.h" +#endif +#include "qeventdispatcher_qpa_p.h" +#ifndef QT_NO_CURSOR +#include "private/qcursor_p.h" +#endif + +#include "private/qwidget_p.h" +#include "private/qevent_p.h" + +#include "qgenericpluginfactory_qpa.h" +#include "qplatformintegrationfactory_qpa_p.h" +#include <qdesktopwidget.h> + +#include <qinputcontext.h> +#include <QPlatformCursor> +#include <qdebug.h> +#include <QWindowSystemInterface> +#include "qwindowsysteminterface_qpa_p.h" +#include <QPlatformIntegration> + +#include "qdesktopwidget_qpa_p.h" + +QT_BEGIN_NAMESPACE + +static QString appName; +static QString appFont; + +QWidget *qt_button_down = 0; // widget got last button-down + +static bool app_do_modal = false; +extern QWidgetList *qt_modal_stack; // stack of modal widgets + +int qt_last_x = 0; +int qt_last_y = 0; +QPointer<QWidget> qt_last_mouse_receiver = 0; + +static Qt::MouseButtons buttons = Qt::NoButton; +static ulong mousePressTime; +static Qt::MouseButton mousePressButton = Qt::NoButton; +static int mousePressX; +static int mousePressY; +static int mouse_double_click_distance = 5; + +void QApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *e) +{ + switch(e->type) { + case QWindowSystemInterfacePrivate::Mouse: + QApplicationPrivate::processMouseEvent(static_cast<QWindowSystemInterfacePrivate::MouseEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::Wheel: + QApplicationPrivate::processWheelEvent(static_cast<QWindowSystemInterfacePrivate::WheelEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::Key: + QApplicationPrivate::processKeyEvent(static_cast<QWindowSystemInterfacePrivate::KeyEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::Touch: + QApplicationPrivate::processTouchEvent(static_cast<QWindowSystemInterfacePrivate::TouchEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::GeometryChange: + QApplicationPrivate::processGeometryChangeEvent(static_cast<QWindowSystemInterfacePrivate::GeometryChangeEvent*>(e)); + break; + case QWindowSystemInterfacePrivate::Enter: + QApplicationPrivate::processEnterEvent(static_cast<QWindowSystemInterfacePrivate::EnterEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::Leave: + QApplicationPrivate::processLeaveEvent(static_cast<QWindowSystemInterfacePrivate::LeaveEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::ActivatedWindow: + QApplicationPrivate::processActivatedEvent(static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::Close: + QApplicationPrivate::processCloseEvent( + static_cast<QWindowSystemInterfacePrivate::CloseEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::ScreenCountChange: + QApplicationPrivate::reportScreenCount( + static_cast<QWindowSystemInterfacePrivate::ScreenCountEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::ScreenGeometry: + QApplicationPrivate::reportGeometryChange( + static_cast<QWindowSystemInterfacePrivate::ScreenGeometryEvent *>(e)); + break; + case QWindowSystemInterfacePrivate::ScreenAvailableGeometry: + QApplicationPrivate::reportAvailableGeometryChange( + static_cast<QWindowSystemInterfacePrivate::ScreenAvailableGeometryEvent *>(e)); + break; + default: + qWarning() << "Unknown user input event type:" << e->type; + break; + } +} + +QString QApplicationPrivate::appName() const +{ + return QT_PREPEND_NAMESPACE(appName); +} + +void QApplicationPrivate::createEventDispatcher() +{ + Q_Q(QApplication); +#if !defined(QT_NO_GLIB) + if (qgetenv("QT_NO_GLIB").isEmpty() && QEventDispatcherGlib::versionSupported()) + eventDispatcher = new QPAEventDispatcherGlib(q); + else +#endif + eventDispatcher = new QEventDispatcherQPA(q); +} + +static bool qt_try_modal(QWidget *widget, QEvent::Type type) +{ + QWidget * top = 0; + + if (QApplicationPrivate::tryModalHelper(widget, &top)) + return true; + + bool block_event = false; + bool paint_event = false; + + switch (type) { +#if 0 + case QEvent::Focus: + if (!static_cast<QWSFocusEvent*>(event)->simpleData.get_focus) + break; + // drop through +#endif + case QEvent::MouseButtonPress: // disallow mouse/key events + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + case QEvent::KeyPress: + case QEvent::KeyRelease: + block_event = true; + break; + default: + break; + } + + if ((block_event || paint_event) && top->parentWidget() == 0) + top->raise(); + + return !block_event; +} + + + +void QApplicationPrivate::enterModal_sys(QWidget *widget) +{ + if (!qt_modal_stack) + qt_modal_stack = new QWidgetList; + qt_modal_stack->insert(0, widget); + app_do_modal = true; +} + +void QApplicationPrivate::leaveModal_sys(QWidget *widget ) +{ + if (qt_modal_stack && qt_modal_stack->removeAll(widget)) { + if (qt_modal_stack->isEmpty()) { + delete qt_modal_stack; + qt_modal_stack = 0; + } + } + app_do_modal = qt_modal_stack != 0; +} + +bool QApplicationPrivate::modalState() +{ + return app_do_modal; +} + +void QApplicationPrivate::closePopup(QWidget *popup) +{ + Q_Q(QApplication); + if (!popupWidgets) + return; + popupWidgets->removeAll(popup); + +//### +// if (popup == qt_popup_down) { +// qt_button_down = 0; +// qt_popup_down = 0; +// } + + if (QApplicationPrivate::popupWidgets->count() == 0) { // this was the last popup + delete QApplicationPrivate::popupWidgets; + QApplicationPrivate::popupWidgets = 0; + + //### replay mouse event? + + //### transfer/release mouse grab + + //### transfer/release keyboard grab + + //give back focus + + if (active_window) { + if (QWidget *fw = active_window->focusWidget()) { + if (fw != QApplication::focusWidget()) { + fw->setFocus(Qt::PopupFocusReason); + } else { + QFocusEvent e(QEvent::FocusIn, Qt::PopupFocusReason); + q->sendEvent(fw, &e); + } + } + } + + } else { + // A popup was closed, so the previous popup gets the focus. + + QWidget* aw = QApplicationPrivate::popupWidgets->last(); + if (QWidget *fw = aw->focusWidget()) + fw->setFocus(Qt::PopupFocusReason); + + //### regrab the keyboard and mouse in case 'popup' lost the grab + + + } + +} + +static int openPopupCount = 0; +void QApplicationPrivate::openPopup(QWidget *popup) +{ + openPopupCount++; + if (!popupWidgets) { // create list + popupWidgets = new QWidgetList; + + /* only grab if you are the first/parent popup */ + //#### ->grabMouse(popup,true); + //#### ->grabKeyboard(popup,true); + //### popupGrabOk = true; + } + popupWidgets->append(popup); // add to end of list + + // popups are not focus-handled by the window system (the first + // popup grabbed the keyboard), so we have to do that manually: A + // new popup gets the focus + if (popup->focusWidget()) { + popup->focusWidget()->setFocus(Qt::PopupFocusReason); + } else if (popupWidgets->count() == 1) { // this was the first popup + if (QWidget *fw = QApplication::focusWidget()) { + QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason); + QApplication::sendEvent(fw, &e); + } + } +} + +void QApplicationPrivate::initializeMultitouch_sys() +{ +} + +void QApplicationPrivate::cleanupMultitouch_sys() +{ +} + +void QApplicationPrivate::initializeWidgetPaletteHash() +{ +} + +void QApplication::setCursorFlashTime(int msecs) +{ + QApplicationPrivate::cursor_flash_time = msecs; +} + +int QApplication::cursorFlashTime() +{ + return QApplicationPrivate::cursor_flash_time; +} + +void QApplication::setDoubleClickInterval(int ms) +{ + QApplicationPrivate::mouse_double_click_time = ms; +} + +int QApplication::doubleClickInterval() +{ + return QApplicationPrivate::mouse_double_click_time; +} + +void QApplication::setKeyboardInputInterval(int ms) +{ + QApplicationPrivate::keyboard_input_time = ms; +} + +int QApplication::keyboardInputInterval() +{ + return QApplicationPrivate::keyboard_input_time; +} + +#ifndef QT_NO_WHEELEVENT +void QApplication::setWheelScrollLines(int lines) +{ + QApplicationPrivate::wheel_scroll_lines = lines; +} + +int QApplication::wheelScrollLines() +{ + return QApplicationPrivate::wheel_scroll_lines; +} +#endif + +void QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable) +{ + switch (effect) { + case Qt::UI_AnimateMenu: + QApplicationPrivate::animate_menu = enable; + break; + case Qt::UI_FadeMenu: + if (enable) + QApplicationPrivate::animate_menu = true; + QApplicationPrivate::fade_menu = enable; + break; + case Qt::UI_AnimateCombo: + QApplicationPrivate::animate_combo = enable; + break; + case Qt::UI_AnimateTooltip: + QApplicationPrivate::animate_tooltip = enable; + break; + case Qt::UI_FadeTooltip: + if (enable) + QApplicationPrivate::animate_tooltip = true; + QApplicationPrivate::fade_tooltip = enable; + break; + case Qt::UI_AnimateToolBox: + QApplicationPrivate::animate_toolbox = enable; + break; + default: + QApplicationPrivate::animate_ui = enable; + break; + } +} + +bool QApplication::isEffectEnabled(Qt::UIEffect effect) +{ + if (QColormap::instance().depth() < 16 || !QApplicationPrivate::animate_ui) + return false; + + switch(effect) { + case Qt::UI_AnimateMenu: + return QApplicationPrivate::animate_menu; + case Qt::UI_FadeMenu: + return QApplicationPrivate::fade_menu; + case Qt::UI_AnimateCombo: + return QApplicationPrivate::animate_combo; + case Qt::UI_AnimateTooltip: + return QApplicationPrivate::animate_tooltip; + case Qt::UI_FadeTooltip: + return QApplicationPrivate::fade_tooltip; + case Qt::UI_AnimateToolBox: + return QApplicationPrivate::animate_toolbox; + default: + return QApplicationPrivate::animate_ui; + } +} + +#ifndef QT_NO_CURSOR +void QApplication::setOverrideCursor(const QCursor &cursor) +{ + qApp->d_func()->cursor_list.prepend(cursor); + qt_qpa_set_cursor(0, false); +} + +void QApplication::restoreOverrideCursor() +{ + if (qApp->d_func()->cursor_list.isEmpty()) + return; + qApp->d_func()->cursor_list.removeFirst(); + qt_qpa_set_cursor(0, false); +} + +#endif// QT_NO_CURSOR + +QWidget *QApplication::topLevelAt(const QPoint &pos) +{ + QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); + + QList<QPlatformScreen *> screens = pi->screens(); + QList<QPlatformScreen *>::const_iterator screen = screens.constBegin(); + QList<QPlatformScreen *>::const_iterator end = screens.constEnd(); + + // The first screen in a virtual environment should know about all top levels + if (pi->isVirtualDesktop()) { + QWidget *w = (*screen)->topLevelAt(pos); + return w; + } + + while (screen != end) { + if ((*screen)->geometry().contains(pos)) + return (*screen)->topLevelAt(pos); + ++screen; + } + return 0; +} + +void QApplication::beep() +{ +} + +void QApplication::alert(QWidget *, int) +{ +} + +/*! + \internal +*/ +QPlatformNativeInterface *QApplication::platformNativeInterface() +{ + QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); + return pi->nativeInterface(); +} + +static void init_platform(const QString &name, const QString &platformPluginPath) +{ + QApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, platformPluginPath); + if (!QApplicationPrivate::platform_integration) { + QStringList keys = QPlatformIntegrationFactory::keys(platformPluginPath); + QString fatalMessage = + QString::fromLatin1("Failed to load platform plugin \"%1\". Available platforms are: \n").arg(name); + foreach(QString key, keys) { + fatalMessage.append(key + QString::fromLatin1("\n")); + } + qFatal("%s", fatalMessage.toLocal8Bit().constData()); + + } + +} + + +static void cleanup_platform() +{ + delete QApplicationPrivate::platform_integration; + QApplicationPrivate::platform_integration = 0; +} + +static void init_plugins(const QList<QByteArray> pluginList) +{ + for (int i = 0; i < pluginList.count(); ++i) { + QByteArray pluginSpec = pluginList.at(i); + qDebug() << "init_plugins" << i << pluginSpec; + int colonPos = pluginSpec.indexOf(':'); + QObject *plugin; + if (colonPos < 0) + plugin = QGenericPluginFactory::create(QLatin1String(pluginSpec), QString()); + else + plugin = QGenericPluginFactory::create(QLatin1String(pluginSpec.mid(0, colonPos)), + QLatin1String(pluginSpec.mid(colonPos+1))); + qDebug() << " created" << plugin; + } +} + +#ifndef QT_NO_QWS_INPUTMETHODS +class QDummyInputContext : public QInputContext +{ +public: + explicit QDummyInputContext(QObject* parent = 0) : QInputContext(parent) {} + ~QDummyInputContext() {} + QString identifierName() { return QString(); } + QString language() { return QString(); } + + void reset() {} + bool isComposing() const { return false; } + +}; +#endif // QT_NO_QWS_INPUTMETHODS + +void qt_init(QApplicationPrivate *priv, int type) +{ + Q_UNUSED(type); + + qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + char *p; + char **argv = priv->argv; + int argc = priv->argc; + + if (argv && *argv) { //apparently, we allow people to pass 0 on the other platforms + p = strrchr(argv[0], '/'); + appName = QString::fromLocal8Bit(p ? p + 1 : argv[0]); + } + + QList<QByteArray> pluginList; + QString platformPluginPath = QLatin1String(qgetenv("QT_QPA_PLATFORM_PLUGIN_PATH")); + QByteArray platformName; +#ifdef QT_QPA_DEFAULT_PLATFORM_NAME + platformName = QT_QPA_DEFAULT_PLATFORM_NAME; +#endif + QByteArray platformNameEnv = qgetenv("QT_QPA_PLATFORM"); + if (!platformNameEnv.isEmpty()) { + platformName = platformNameEnv; + } + + // Get command line params + + int j = argc ? 1 : 0; + for (int i=1; i<argc; i++) { + if (argv[i] && *argv[i] != '-') { + argv[j++] = argv[i]; + continue; + } + QByteArray arg = argv[i]; + if (arg == "-fn" || arg == "-font") { + if (++i < argc) + appFont = QString::fromLocal8Bit(argv[i]); + } else if (arg == "-platformpluginpath") { + if (++i < argc) + platformPluginPath = QLatin1String(argv[i]); + } else if (arg == "-platform") { + if (++i < argc) + platformName = argv[i]; + } else if (arg == "-plugin") { + if (++i < argc) + pluginList << argv[i]; + } else { + argv[j++] = argv[i]; + } + } + + if (j < priv->argc) { + priv->argv[j] = 0; + priv->argc = j; + } + +#if 0 + QByteArray pluginEnv = qgetenv("QT_QPA_PLUGINS"); + if (!pluginEnv.isEmpty()) { + pluginList.append(pluginEnv.split(';')); + } +#endif + + init_platform(QLatin1String(platformName), platformPluginPath); + init_plugins(pluginList); + + QColormap::initialize(); + QFont::initialize(); +#ifndef QT_NO_CURSOR +// QCursorData::initialize(); +#endif + + qApp->setObjectName(appName); + +#ifndef QT_NO_QWS_INPUTMETHODS + qApp->setInputContext(new QDummyInputContext(qApp)); +#endif +} + +void qt_cleanup() +{ + cleanup_platform(); + + QPixmapCache::clear(); +#ifndef QT_NO_CURSOR + QCursorData::cleanup(); +#endif + QFont::cleanup(); + QColormap::cleanup(); + delete QApplicationPrivate::inputContext; + QApplicationPrivate::inputContext = 0; + + QApplicationPrivate::active_window = 0; //### this should not be necessary +} + + +#ifdef QT3_SUPPORT +void QApplication::setMainWidget(QWidget *mainWidget) +{ + QApplicationPrivate::main_widget = mainWidget; + if (QApplicationPrivate::main_widget && windowIcon().isNull() + && QApplicationPrivate::main_widget->testAttribute(Qt::WA_SetWindowIcon)) + setWindowIcon(QApplicationPrivate::main_widget->windowIcon()); +} +#endif + +void QApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e) +{ + if (!e->widget) + return; + + // qDebug() << "handleMouseEvent" << tlw << ev.pos() << ev.globalPos() << hex << ev.buttons(); + static QWeakPointer<QWidget> implicit_mouse_grabber; + + QEvent::Type type; + // move first + Qt::MouseButtons stateChange = e->buttons ^ buttons; + if (e->globalPos != QPoint(qt_last_x, qt_last_y) && (stateChange != Qt::NoButton)) { + QWindowSystemInterfacePrivate::MouseEvent * newMouseEvent = + new QWindowSystemInterfacePrivate::MouseEvent(e->widget.data(), e->timestamp, e->localPos, e->globalPos, e->buttons); + QWindowSystemInterfacePrivate::windowSystemEventQueue.prepend(newMouseEvent); // just in case the move triggers a new event loop + stateChange = Qt::NoButton; + } + + QWidget * tlw = e->widget.data(); + + QPoint localPoint = e->localPos; + QPoint globalPoint = e->globalPos; + QWidget *mouseWindow = tlw; + + Qt::MouseButton button = Qt::NoButton; + + + if (qt_last_x != globalPoint.x() || qt_last_y != globalPoint.y()) { + type = QEvent::MouseMove; + qt_last_x = globalPoint.x(); + qt_last_y = globalPoint.y(); + if (qAbs(globalPoint.x() - mousePressX) > mouse_double_click_distance|| + qAbs(globalPoint.y() - mousePressY) > mouse_double_click_distance) + mousePressButton = Qt::NoButton; + } + else { // check to see if a new button has been pressed/released + for (int check = Qt::LeftButton; + check <= Qt::XButton2; + check = check << 1) { + if (check & stateChange) { + button = Qt::MouseButton(check); + break; + } + } + if (button == Qt::NoButton) { + // Ignore mouse events that don't change the current state + return; + } + buttons = e->buttons; + if (button & e->buttons) { + if ((e->timestamp - mousePressTime) < static_cast<ulong>(QApplication::doubleClickInterval()) && button == mousePressButton) { + type = QEvent::MouseButtonDblClick; + mousePressButton = Qt::NoButton; + } + else { + type = QEvent::MouseButtonPress; + mousePressTime = e->timestamp; + mousePressButton = button; + mousePressX = qt_last_x; + mousePressY = qt_last_y; + } + } + else + type = QEvent::MouseButtonRelease; + } + + if (self->inPopupMode()) { + //popup mouse handling is magical... + mouseWindow = qApp->activePopupWidget(); + + implicit_mouse_grabber.clear(); + //### how should popup mode and implicit mouse grab interact? + + } else if (tlw && app_do_modal && !qt_try_modal(tlw, QEvent::MouseButtonRelease) ) { + //even if we're blocked by modality, we should deliver the mouse release event.. + //### this code is not completely correct: multiple buttons can be pressed simultaneously + if (!(implicit_mouse_grabber && buttons == Qt::NoButton)) { + //qDebug() << "modal blocked mouse event to" << tlw; + return; + } + } + + // find the tlw if we didn't get it from the plugin + if (!mouseWindow) { + mouseWindow = QApplication::topLevelAt(globalPoint); + } + + if (!mouseWindow && !implicit_mouse_grabber) + mouseWindow = QApplication::desktop(); + + if (mouseWindow && mouseWindow != tlw) { + //we did not get a sensible localPoint from the window system, so let's calculate it + localPoint = mouseWindow->mapFromGlobal(globalPoint); + } + + // which child should have it? + QWidget *mouseWidget = mouseWindow; + if (mouseWindow) { + QWidget *w = mouseWindow->childAt(localPoint); + if (w) { + mouseWidget = w; + } + } + + //handle implicit mouse grab + if (type == QEvent::MouseButtonPress && !implicit_mouse_grabber) { + implicit_mouse_grabber = mouseWidget; + + Q_ASSERT(mouseWindow); + mouseWindow->activateWindow(); //focus + } else if (implicit_mouse_grabber) { + mouseWidget = implicit_mouse_grabber.data(); + mouseWindow = mouseWidget->window(); + if (mouseWindow != tlw) + localPoint = mouseWindow->mapFromGlobal(globalPoint); + } + + Q_ASSERT(mouseWidget); + + //localPoint is local to mouseWindow, but it needs to be local to mouseWidget + localPoint = mouseWidget->mapFrom(mouseWindow, localPoint); + + if (buttons == Qt::NoButton) { + //qDebug() << "resetting mouse grabber"; + implicit_mouse_grabber.clear(); + } + + if (mouseWidget != qt_last_mouse_receiver) { + dispatchEnterLeave(mouseWidget, qt_last_mouse_receiver); + qt_last_mouse_receiver = mouseWidget; + } + + // Remember, we might enter a modal event loop when sending the event, + // so think carefully before adding code below this point. + + // qDebug() << "sending mouse ev." << ev.type() << localPoint << globalPoint << ev.button() << ev.buttons() << mouseWidget << "mouse grabber" << implicit_mouse_grabber; + + QMouseEvent ev(type, localPoint, globalPoint, button, buttons, QApplication::keyboardModifiers()); + + QList<QWeakPointer<QPlatformCursor> > cursors = QPlatformCursorPrivate::getInstances(); + foreach (QWeakPointer<QPlatformCursor> cursor, cursors) { + if (cursor) + cursor.data()->pointerEvent(ev); + } + + int oldOpenPopupCount = openPopupCount; + QApplication::sendSpontaneousEvent(mouseWidget, &ev); + +#ifndef QT_NO_CONTEXTMENU + if (type == QEvent::MouseButtonPress && button == Qt::RightButton && (openPopupCount == oldOpenPopupCount)) { + QContextMenuEvent e(QContextMenuEvent::Mouse, localPoint, globalPoint, QApplication::keyboardModifiers()); + QApplication::sendSpontaneousEvent(mouseWidget, &e); + } +#endif // QT_NO_CONTEXTMENU +} + + +//### there's a lot of duplicated logic here -- refactoring required! + +void QApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e) +{ + + if (!e->widget) + return; + +// QPoint localPoint = ev.pos(); + QPoint globalPoint = e->globalPos; +// bool trustLocalPoint = !!tlw; //is there something the local point can be local to? + QWidget *mouseWidget; + + qt_last_x = globalPoint.x(); + qt_last_y = globalPoint.y(); + + QWidget *mouseWindow = e->widget.data(); + + // find the tlw if we didn't get it from the plugin + if (!mouseWindow) { + mouseWindow = QApplication::topLevelAt(globalPoint); + } + + if (!mouseWindow) + return; + + mouseWidget = mouseWindow; + + if (app_do_modal && !qt_try_modal(mouseWindow, QEvent::Wheel) ) { + qDebug() << "modal blocked wheel event" << mouseWindow; + return; + } + QPoint p = mouseWindow->mapFromGlobal(globalPoint); + QWidget *w = mouseWindow->childAt(p); + if (w) { + mouseWidget = w; + p = mouseWidget->mapFromGlobal(globalPoint); + } + + QWheelEvent ev(p, globalPoint, e->delta, buttons, QApplication::keyboardModifiers(), + e->orient); + QApplication::sendSpontaneousEvent(mouseWidget, &ev); +} + + + +// Remember, Qt convention is: keyboard state is state *before* + +void QApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyEvent *e) +{ + QWidget *focusW = 0; + if (self->inPopupMode()) { + QWidget *popupW = qApp->activePopupWidget(); + focusW = popupW->focusWidget() ? popupW->focusWidget() : popupW; + } + if (!focusW) + focusW = QApplication::focusWidget(); + if (!focusW) { + focusW = e->widget.data(); + } + if (!focusW) + focusW = QApplication::activeWindow(); + + //qDebug() << "handleKeyEvent" << hex << e->key() << e->modifiers() << e->text() << "widget" << focusW; + + if (!focusW) + return; + if (app_do_modal && !qt_try_modal(focusW, e->keyType)) + return; + + if (e->nativeScanCode || e->nativeVirtualKey || e->nativeModifiers) { + QKeyEventEx ev(e->keyType, e->key, e->modifiers, e->unicode, e->repeat, e->repeatCount, + e->nativeScanCode, e->nativeVirtualKey, e->nativeModifiers); + QApplication::sendSpontaneousEvent(focusW, &ev); + } else { + QKeyEvent ev(e->keyType, e->key, e->modifiers, e->unicode, e->repeat, e->repeatCount); + QApplication::sendSpontaneousEvent(focusW, &ev); + } +} + +void QApplicationPrivate::processEnterEvent(QWindowSystemInterfacePrivate::EnterEvent *e) +{ + if (!e->enter) + return; + + QApplicationPrivate::dispatchEnterLeave(e->enter.data(),0); + qt_last_mouse_receiver = e->enter.data(); +} + +void QApplicationPrivate::processLeaveEvent(QWindowSystemInterfacePrivate::LeaveEvent *e) +{ + if (!e->leave) + return; + + QApplicationPrivate::dispatchEnterLeave(0,qt_last_mouse_receiver); + + if (e->leave.data() && !e->leave.data()->isAncestorOf(qt_last_mouse_receiver)) //(???) this should not happen + QApplicationPrivate::dispatchEnterLeave(0, e->leave.data()); + qt_last_mouse_receiver = 0; + +} + +void QApplicationPrivate::processActivatedEvent(QWindowSystemInterfacePrivate::ActivatedWindowEvent *e) +{ + if (!e->activated) + return; + + QApplication::setActiveWindow(e->activated.data()); +} + +void QApplicationPrivate::processGeometryChangeEvent(QWindowSystemInterfacePrivate::GeometryChangeEvent *e) +{ + if (e->tlw.isNull()) + return; + QWidget *tlw = e->tlw.data(); + if (!tlw->isWindow()) + return; //geo of native child widgets is controlled by lighthouse + //so we already have sent the events; besides this new rect + //is not mapped to parent + + QRect newRect = e->newGeometry; + QRect cr(tlw->geometry()); + bool isResize = cr.size() != newRect.size(); + bool isMove = cr.topLeft() != newRect.topLeft(); + tlw->data->crect = newRect; + if (isResize) { + QResizeEvent e(tlw->data->crect.size(), cr.size()); + QApplication::sendSpontaneousEvent(tlw, &e); + tlw->update(); + } + + if (isMove) { + //### frame geometry + QMoveEvent e(tlw->data->crect.topLeft(), cr.topLeft()); + QApplication::sendSpontaneousEvent(tlw, &e); + } +} + +void QApplicationPrivate::processCloseEvent(QWindowSystemInterfacePrivate::CloseEvent *e) +{ + if (e->topLevel.isNull()) { + //qDebug() << "QApplicationPrivate::processCloseEvent NULL"; + return; + } + e->topLevel.data()->d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent); +} + +void QApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::TouchEvent *e) +{ + translateRawTouchEvent(e->widget.data(), e->devType, e->points); +} + +void QApplicationPrivate::reportScreenCount(QWindowSystemInterfacePrivate::ScreenCountEvent *e) +{ + // This operation only makes sense after the QApplication constructor runs + if (QCoreApplication::startingUp()) + return; + + QApplication::desktop()->d_func()->updateScreenList(); + // signal anything listening for creation or deletion of screens + QDesktopWidget *desktop = QApplication::desktop(); + emit desktop->screenCountChanged(e->count); +} + +void QApplicationPrivate::reportGeometryChange(QWindowSystemInterfacePrivate::ScreenGeometryEvent *e) +{ + // This operation only makes sense after the QApplication constructor runs + if (QCoreApplication::startingUp()) + return; + + QApplication::desktop()->d_func()->updateScreenList(); + + // signal anything listening for screen geometry changes + QDesktopWidget *desktop = QApplication::desktop(); + emit desktop->resized(e->index); + + // make sure maximized and fullscreen windows are updated + QWidgetList list = QApplication::topLevelWidgets(); + for (int i = list.size() - 1; i >= 0; --i) { + QWidget *w = list.at(i); + if (w->isFullScreen()) + w->d_func()->setFullScreenSize_helper(); + else if (w->isMaximized()) + w->d_func()->setMaxWindowState_helper(); + } +} + +void QApplicationPrivate::reportAvailableGeometryChange( + QWindowSystemInterfacePrivate::ScreenAvailableGeometryEvent *e) +{ + // This operation only makes sense after the QApplication constructor runs + if (QCoreApplication::startingUp()) + return; + + QApplication::desktop()->d_func()->updateScreenList(); + + // signal anything listening for screen geometry changes + QDesktopWidget *desktop = QApplication::desktop(); + emit desktop->workAreaResized(e->index); + + // make sure maximized and fullscreen windows are updated + QWidgetList list = QApplication::topLevelWidgets(); + for (int i = list.size() - 1; i >= 0; --i) { + QWidget *w = list.at(i); + if (w->isFullScreen()) + w->d_func()->setFullScreenSize_helper(); + else if (w->isMaximized()) + w->d_func()->setMaxWindowState_helper(); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qapplication_qws.cpp b/src/gui/kernel/qapplication_qws.cpp index 2d49ea6..193dfcd 100644 --- a/src/gui/kernel/qapplication_qws.cpp +++ b/src/gui/kernel/qapplication_qws.cpp @@ -112,19 +112,6 @@ #include <qvfbhdr.h> -#ifndef QT_NO_QWS_MULTIPROCESS -#ifdef QT_NO_QSHM -#include <sys/ipc.h> -#include <sys/shm.h> -#ifndef Q_OS_DARWIN -# include <sys/sem.h> -#endif -#include <sys/socket.h> -#else -#include "private/qwssharedmemory_p.h" -#endif -#endif - QT_BEGIN_NAMESPACE #ifndef QT_NO_DIRECTPAINTER @@ -204,6 +191,11 @@ QString qws_dataDir() result = QT_VFB_DATADIR(qws_display_id); QByteArray dataDir = result.toLocal8Bit(); +#if defined(Q_OS_INTEGRITY) + /* ensure filesystem is ready before starting requests */ + WaitForFileSystemInitialization(); +#endif + if (QT_MKDIR(dataDir, 0700)) { if (errno != EEXIST) { qFatal("Cannot create Qt for Embedded Linux data directory: %s", dataDir.constData()); @@ -217,7 +209,7 @@ QString qws_dataDir() if (!S_ISDIR(buf.st_mode)) qFatal("%s is not a directory", dataDir.constData()); -#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS) +#if !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS) && !defined(Q_OS_QNX) if (buf.st_uid != getuid()) qFatal("Qt for Embedded Linux data directory is not owned by user %d", getuid()); @@ -225,7 +217,7 @@ QString qws_dataDir() qFatal("Qt for Embedded Linux data directory has incorrect permissions: %s", dataDir.constData()); #endif - result.append("/"); + result.append(QLatin1Char('/')); return result; } @@ -2192,6 +2184,11 @@ void qt_init(QApplicationPrivate *priv, int type) qws_screen_is_interlaced = read_bool_env_var("QWS_INTERLACE",false); const char *display = ::getenv("QWS_DISPLAY"); + +#ifdef QT_QWS_DEFAULT_DRIVER_NAME + if (!display) display = QT_QWS_DEFAULT_DRIVER_NAME; +#endif + if (display) qws_display_spec = display; // since we setenv later! @@ -2702,6 +2699,11 @@ void QApplication::alert(QWidget *, int) { } +Qt::KeyboardModifiers QApplication::queryKeyboardModifiers() +{ + return keyboardModifiers(); // TODO proper implementation +} + int QApplication::qwsProcessEvent(QWSEvent* event) { Q_D(QApplication); @@ -3560,13 +3562,8 @@ bool QETWidget::translateKeyEvent(const QWSKeyEvent *event, bool grab) /* grab i QEvent::KeyPress : QEvent::KeyRelease; bool autor = event->simpleData.is_auto_repeat; QString text; - char ascii = 0; - if (event->simpleData.unicode) { - QChar ch(event->simpleData.unicode); - if (ch.unicode() != 0xffff) - text += ch; - ascii = ch.toLatin1(); - } + if (event->simpleData.unicode && event->simpleData.unicode != 0xffff) + text += QChar(event->simpleData.unicode); code = event->simpleData.keycode; #if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT) diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index a53d273..80bcdf0 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -115,6 +115,8 @@ QWidget *qt_button_down = 0; // widget got last button-down QSymbianControl *QSymbianControl::lastFocusedControl = 0; +static Qt::KeyboardModifiers app_keyboardModifiers = Qt::NoModifier; + QS60Data* qGlobalS60Data() { return qt_s60Data(); @@ -580,13 +582,20 @@ QPoint QSymbianControl::translatePointForFixedNativeOrientation(const TPoint &po { QPoint pos(pointerEventPos.iX, pointerEventPos.iY); if (qwidget->d_func()->fixNativeOrientationCalled) { - QSize wsize = qwidget->size(); - TSize size = Size(); + QSize wsize = qwidget->size(); // always same as the size in the native orientation + TSize size = Size(); // depends on the current orientation if (size.iWidth == wsize.height() && size.iHeight == wsize.width()) { qreal x = pos.x(); qreal y = pos.y(); - pos.setX(size.iHeight - y); - pos.setY(x); + if (S60->screenRotation == QS60Data::ScreenRotation90) { + // DisplayRightUp + pos.setX(size.iHeight - y); + pos.setY(x); + } else if (S60->screenRotation == QS60Data::ScreenRotation270) { + // DisplayLeftUp + pos.setX(y); + pos.setY(size.iWidth - x); + } } } return pos; @@ -735,6 +744,7 @@ void QSymbianControl::HandlePointerEvent(const TPointerEvent& pEvent) Qt::MouseButton button; mapS60MouseEventTypeToQt(&type, &button, &pEvent); Qt::KeyboardModifiers modifiers = mapToQtModifiers(pEvent.iModifiers); + app_keyboardModifiers = modifiers; QPoint widgetPos = translatePointForFixedNativeOrientation(pEvent.iPosition); TPoint controlScreenPos = PositionRelativeToScreen(); @@ -1392,6 +1402,23 @@ void QSymbianControl::PositionChanged() } } +// Search recursively if there is a child widget that is both visible and focused. +bool QSymbianControl::hasFocusedAndVisibleChild(QWidget *parentWidget) +{ + for (int i = 0; i < parentWidget->children().size(); ++i) { + QObject *object = parentWidget->children().at(i); + if (object && object->isWidgetType()) { + QWidget *w = static_cast<QWidget *>(object); + WId winId = w->internalWinId(); + if (winId && winId->IsFocused() && winId->IsVisible()) + return true; + if (hasFocusedAndVisibleChild(w)) + return true; + } + } + return false; +} + void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */) { if (m_ignoreFocusChanged || (qwidget->windowType() & Qt::WindowType_Mask) == Qt::Desktop) @@ -1424,17 +1451,9 @@ void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */) if (qwidget->isWindow()) S60->setRecursiveDecorationsVisibility(qwidget, qwidget->windowState()); #endif - } else if (QApplication::activeWindow() == qwidget->window()) { - bool focusedControlFound = false; - WId winId = 0; - for (QWidget *w = qwidget->parentWidget(); w && (winId = w->internalWinId()); w = w->parentWidget()) { - if (winId->IsFocused() && winId->IsVisible()) { - focusedControlFound = true; - break; - } else if (w->isWindow()) - break; - } - if (!focusedControlFound) { + } else { + QWidget *parentWindow = qwidget->window(); + if (QApplication::activeWindow() == parentWindow && !hasFocusedAndVisibleChild(parentWindow)) { if (CCoeEnv::Static()->AppUi()->IsDisplayingMenuOrDialog() || S60->menuBeingConstructed) { QWidget *fw = QApplication::focusWidget(); if (fw) { @@ -1547,6 +1566,10 @@ void QSymbianControl::HandleResourceChange(int resourceType) #ifdef Q_WS_S60 case KEikDynamicLayoutVariantSwitch: { +#ifdef QT_SOFTKEYS_ENABLED + // Update needed just in case softkeys contain icons + QSoftKeyManager::updateSoftKeys(); +#endif handleClientAreaChange(); // Send resize event to trigger desktopwidget workAreaResized signal if (qt_desktopWidget) { @@ -1688,7 +1711,7 @@ void QSymbianControl::ensureFixNativeOrientation() This function is only available on S60. */ QApplication::QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv) - : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient)) + : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, 0x040000)) { Q_D(QApplication); S60->s60ApplicationFactory = factory; @@ -1696,7 +1719,7 @@ QApplication::QApplication(QApplication::QS60MainApplicationFactory factory, int } QApplication::QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv, int _internal) - : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient)) + : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, _internal)) { Q_D(QApplication); S60->s60ApplicationFactory = factory; @@ -1722,7 +1745,7 @@ void qt_init(QApplicationPrivate * /* priv */, int) if (commandLine) { // After this construction, CEikonEnv will be available from CEikonEnv::Static(). // (much like our qApp). - QtEikonEnv* coe = new QtEikonEnv; + CEikonEnv* coe = new CEikonEnv; //not using QT_TRAP_THROWING, because coe owns the cleanupstack so it can't be pushed there. TRAPD(err, coe->ConstructAppFromCommandLineL(factory, *commandLine)); if(err != KErrNone) { @@ -1832,6 +1855,8 @@ void qt_init(QApplicationPrivate * /* priv */, int) repository = 0; #endif + qt_keymapper_private()->updateInputLanguage(); + #ifdef QT_KEYPAD_NAVIGATION if (touch) { QApplicationPrivate::navigationMode = Qt::NavigationModeNone; @@ -1915,13 +1940,18 @@ void qt_init(QApplicationPrivate * /* priv */, int) qRegisterMetaType<WId>("WId"); } +#ifdef QT_NO_FREETYPE extern void qt_cleanup_symbianFontDatabase(); // qfontdatabase_s60.cpp +#endif /***************************************************************************** qt_cleanup() - cleans up when the application is finished *****************************************************************************/ void qt_cleanup() { +#ifdef Q_WS_S60 + S60->setButtonGroupContainer(0); +#endif if(qt_S60Beep) { delete qt_S60Beep; qt_S60Beep = 0; @@ -1929,7 +1959,9 @@ void qt_cleanup() QFontCache::cleanup(); // Has to happen now, since QFontEngineS60 has FBS handles QPixmapCache::clear(); // Has to happen now, since QSymbianRasterPixmapData has FBS handles +#ifdef QT_NO_FREETYPE qt_cleanup_symbianFontDatabase(); +#endif // S60 structure and window server session are freed in eventdispatcher destructor as they are needed there // It's important that this happens here, before the event dispatcher gets @@ -2398,6 +2430,13 @@ int QApplicationPrivate::symbianProcessWsEvent(const QSymbianEvent *symbianEvent } break; #endif + +#ifdef Q_WS_S60 + case KEikInputLanguageChange: + qt_keymapper_private()->updateInputLanguage(); + break; +#endif + default: break; } @@ -2561,6 +2600,11 @@ void QApplication::setEffectEnabled(Qt::UIEffect /* effect */, bool /* enable */ // TODO: Implement QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable) } +Qt::KeyboardModifiers QApplication::queryKeyboardModifiers() +{ + return app_keyboardModifiers; +} + TUint QApplicationPrivate::resolveS60ScanCode(TInt scanCode, TUint keysym) { if (!scanCode) diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index 61b01ab..756cb56 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -115,12 +115,25 @@ extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.c # if defined(Q_WS_WINCE) # include <bldver.h> # endif -# include <winable.h> +# if !defined(Q_WS_WINCE) +# include <winable.h> +# endif +#endif + +#ifndef QT_NO_GESTURES +# ifndef GID_ZOOM +# define GID_ZOOM 3 +# define GID_TWOFINGERTAP 6 +# define GID_PRESSANDTAP 7 +# define GID_ROLLOVER GID_PRESSANDTAP +# endif #endif #ifndef WM_TOUCH # define WM_TOUCH 0x0240 +#endif +#ifndef TOUCHEVENTF_MOVE # define TOUCHEVENTF_MOVE 0x0001 # define TOUCHEVENTF_DOWN 0x0002 # define TOUCHEVENTF_UP 0x0004 @@ -207,8 +220,6 @@ static void resolveAygLibs() if (!aygResolved) { aygResolved = true; QSystemLibrary ayglib(QLatin1String("aygshell")); - if (!ayglib.load()) - return; ptrRecognizeGesture = (AygRecognizeGesture) ayglib.resolve("SHRecognizeGesture"); } } @@ -226,6 +237,7 @@ static void resolveAygLibs() # define FE_FONTSMOOTHINGCLEARTYPE 0x0002 #endif +Q_GUI_EXPORT qreal qt_fontsmoothing_gamma; Q_GUI_EXPORT bool qt_cleartype_enabled; Q_GUI_EXPORT bool qt_win_owndc_required; // CS_OWNDC is required if we use the GL graphicssystem as default @@ -642,8 +654,18 @@ static void qt_win_read_cleartype_settings() if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0)) qt_cleartype_enabled = (result == FE_FONTSMOOTHINGCLEARTYPE); #endif -} + int winSmooth; + if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0)) { + qt_fontsmoothing_gamma = winSmooth / qreal(1000.0); + } else { + qt_fontsmoothing_gamma = 1.0; + } + + // Safeguard ourselves against corrupt registry values... + if (qt_fontsmoothing_gamma > 5 || qt_fontsmoothing_gamma < 1) + qt_fontsmoothing_gamma = qreal(1.4); +} static void qt_set_windows_resources() { @@ -944,29 +966,36 @@ bool qt_nograb() // application no-grab option typedef QHash<QString, int> WinClassNameHash; Q_GLOBAL_STATIC(WinClassNameHash, winclassNames) +// +// If 0 is passed as the widget pointer, register a window class +// for QWidget as default. This is used in QGLTemporaryContext +// during GL initialization, where we don't want to use temporary +// QWidgets or QGLWidgets, neither do we want to have separate code +// to register window classes. +// const QString qt_reg_winclass(QWidget *w) // register window class { - int flags = w->windowFlags(); - int type = flags & Qt::WindowType_Mask; + Qt::WindowFlags flags = w ? w->windowFlags() : (Qt::WindowFlags)0; + Qt::WindowFlags type = flags & Qt::WindowType_Mask; uint style; bool icon; QString cname; - if (qt_widget_private(w)->isGLWidget) { + if (w && qt_widget_private(w)->isGLWidget) { cname = QLatin1String("QGLWidget"); style = CS_DBLCLKS; #ifndef Q_WS_WINCE style |= CS_OWNDC; #endif icon = true; - } else if (flags & Qt::MSWindowsOwnDC) { + } else if (w && (flags & Qt::MSWindowsOwnDC)) { cname = QLatin1String("QWidgetOwnDC"); style = CS_DBLCLKS; #ifndef Q_WS_WINCE style |= CS_OWNDC; #endif icon = true; - } else if (type == Qt::Tool || type == Qt::ToolTip){ + } else if (w && (type == Qt::Tool || type == Qt::ToolTip)) { style = CS_DBLCLKS; if (w->inherits("QTipLabel") || w->inherits("QAlphaWidget")) { if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP @@ -981,7 +1010,7 @@ const QString qt_reg_winclass(QWidget *w) // register window class style |= CS_SAVEBITS; #endif icon = false; - } else if (type == Qt::Popup) { + } else if (w && (type == Qt::Popup)) { cname = QLatin1String("QPopup"); style = CS_DBLCLKS; #ifndef Q_WS_WINCE @@ -1035,7 +1064,12 @@ const QString qt_reg_winclass(QWidget *w) // register window class if (winclassNames()->contains(cname)) // already registered in our list return cname; +#ifndef Q_WS_WINCE + WNDCLASSEX wc; + wc.cbSize = sizeof(WNDCLASSEX); +#else WNDCLASS wc; +#endif wc.style = style; wc.lpfnWndProc = (WNDPROC)QtWndProc; wc.cbClsExtra = 0; @@ -1044,22 +1078,38 @@ const QString qt_reg_winclass(QWidget *w) // register window class if (icon) { wc.hIcon = (HICON)LoadImage(qWinAppInst(), L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); #ifndef Q_WS_WINCE - if (!wc.hIcon) + if (wc.hIcon) { + int sw = GetSystemMetrics(SM_CXSMICON); + int sh = GetSystemMetrics(SM_CYSMICON); + wc.hIconSm = (HICON)LoadImage(qWinAppInst(), L"IDI_ICON1", IMAGE_ICON, sw, sh, 0); + } else { wc.hIcon = (HICON)LoadImage(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + wc.hIconSm = 0; + } #endif } else { wc.hIcon = 0; +#ifndef Q_WS_WINCE + wc.hIconSm = 0; +#endif } wc.hCursor = 0; #ifndef Q_WS_WINCE - wc.hbrBackground = qt_widget_private(w)->isGLWidget ? 0 : (HBRUSH)GetSysColorBrush(COLOR_WINDOW); + HBRUSH brush = 0; + if (w && !qt_widget_private(w)->isGLWidget) + brush = (HBRUSH)GetSysColorBrush(COLOR_WINDOW); + wc.hbrBackground = brush; #else wc.hbrBackground = 0; #endif wc.lpszMenuName = 0; wc.lpszClassName = (wchar_t*)cname.utf16(); +#ifndef Q_WS_WINCE + ATOM atom = RegisterClassEx(&wc); +#else ATOM atom = RegisterClass(&wc); +#endif #ifndef QT_NO_DEBUG if (!atom) @@ -1074,8 +1124,7 @@ const QString qt_reg_winclass(QWidget *w) // register window class Q_GUI_EXPORT const QString qt_getRegisteredWndClass() { - QWidget w; - return qt_reg_winclass(&w); + return qt_reg_winclass(0); } static void unregWinClasses() @@ -1270,6 +1319,11 @@ Qt::KeyboardModifiers qt_win_getKeyboardModifiers() return modifiers; } +Qt::KeyboardModifiers QApplication::queryKeyboardModifiers() +{ + return qt_win_getKeyboardModifiers(); +} + /***************************************************************************** Routines to find a Qt widget from a screen position *****************************************************************************/ @@ -2323,8 +2377,13 @@ extern "C" LRESULT QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wPa #ifndef QT_NO_ACCESSIBILITY case WM_GETOBJECT: { + /* On Win64, lParam can be 0x00000000fffffffc or 0xfffffffffffffffc (!), + but MSDN says that lParam should be converted to a DWORD + before its compared against OBJID_CLIENT + */ + const DWORD dwObjId = (DWORD)lParam; // Ignoring all requests while starting up - if (QApplication::startingUp() || QApplication::closingDown() || (LONG)lParam != OBJID_CLIENT) { + if (QApplication::startingUp() || QApplication::closingDown() || dwObjId != OBJID_CLIENT) { result = false; break; } @@ -3676,13 +3735,11 @@ static void initWinTabFunctions() return; QSystemLibrary library(QLatin1String("wintab32")); - if (library.load()) { - ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoW"); - ptrWTGet = (PtrWTGet)library.resolve("WTGetW"); - ptrWTEnable = (PtrWTEnable)library.resolve("WTEnable"); - ptrWTOverlap = (PtrWTEnable)library.resolve("WTOverlap"); - ptrWTPacketsGet = (PtrWTPacketsGet)library.resolve("WTPacketsGet"); - } + ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoW"); + ptrWTGet = (PtrWTGet)library.resolve("WTGetW"); + ptrWTEnable = (PtrWTEnable)library.resolve("WTEnable"); + ptrWTOverlap = (PtrWTEnable)library.resolve("WTOverlap"); + ptrWTPacketsGet = (PtrWTPacketsGet)library.resolve("WTPacketsGet"); #endif // Q_OS_WINCE } #endif // QT_NO_TABLETEVENT diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp index 6d13628..ef8e2b8 100644 --- a/src/gui/kernel/qapplication_x11.cpp +++ b/src/gui/kernel/qapplication_x11.cpp @@ -483,11 +483,9 @@ static void* qt_load_library_runtime(const char *library, int vernum, Q_FOREACH(int version, versions) { QLatin1String libName(library); QLibrary xfixesLib(libName, version); - if (xfixesLib.load()) { - void *ptr = xfixesLib.resolve(symbol); - if (ptr) - return ptr; - } + void *ptr = xfixesLib.resolve(symbol); + if (ptr) + return ptr; } return 0; } @@ -1589,6 +1587,9 @@ static void getXDefault(const char *group, const char *key, int *val) int v = strtol(str, &end, 0); if (str != end) *val = v; + // otherwise use fontconfig to convert the string to integer + else + FcNameConstant((FcChar8 *) str, val); } } @@ -1739,6 +1740,9 @@ void qt_init(QApplicationPrivate *priv, int, } else { // Qt controls everything (default) + if (QApplication::testAttribute(Qt::AA_X11InitThreads)) + XInitThreads(); + // Set application name and class char *app_class = 0; if (argv && argv[0]) { @@ -2233,6 +2237,7 @@ void qt_init(QApplicationPrivate *priv, int, } getXDefault("Xft", FC_ANTIALIAS, &X11->fc_antialias); #ifdef FC_HINT_STYLE + X11->fc_hint_style = -1; getXDefault("Xft", FC_HINT_STYLE, &X11->fc_hint_style); #endif #if 0 @@ -2273,6 +2278,13 @@ void qt_init(QApplicationPrivate *priv, int, // Attempt to determine the current running X11 Desktop Enviornment // Use dbus if/when we can, but fall back to using windowManagerName() for now +#ifndef QT_NO_XFIXES + if (X11->ptrXFixesSelectSelectionInput) + X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(), ATOM(_NET_WM_CM_S0), + XFixesSetSelectionOwnerNotifyMask + | XFixesSelectionWindowDestroyNotifyMask + | XFixesSelectionClientCloseNotifyMask); +#endif // QT_NO_XFIXES X11->compositingManagerRunning = XGetSelectionOwner(X11->display, ATOM(_NET_WM_CM_S0)); X11->desktopEnvironment = DE_UNKNOWN; @@ -2608,22 +2620,20 @@ void qt_init(QApplicationPrivate *priv, int, #if !defined (Q_OS_IRIX) && !defined (QT_NO_TABLET) QLibrary wacom(QString::fromLatin1("wacomcfg"), 0); // version 0 is the latest release at time of writing this. - if (wacom.load()) { - // NOTE: C casts instead of reinterpret_cast for GCC 3.3.x - ptrWacomConfigInit = (PtrWacomConfigInit)wacom.resolve("WacomConfigInit"); - ptrWacomConfigOpenDevice = (PtrWacomConfigOpenDevice)wacom.resolve("WacomConfigOpenDevice"); - ptrWacomConfigGetRawParam = (PtrWacomConfigGetRawParam)wacom.resolve("WacomConfigGetRawParam"); - ptrWacomConfigCloseDevice = (PtrWacomConfigCloseDevice)wacom.resolve("WacomConfigCloseDevice"); - ptrWacomConfigTerm = (PtrWacomConfigTerm)wacom.resolve("WacomConfigTerm"); - - if (ptrWacomConfigInit == 0 || ptrWacomConfigOpenDevice == 0 || ptrWacomConfigGetRawParam == 0 - || ptrWacomConfigCloseDevice == 0 || ptrWacomConfigTerm == 0) { // either we have all, or we have none. + // NOTE: C casts instead of reinterpret_cast for GCC 3.3.x + ptrWacomConfigInit = (PtrWacomConfigInit)wacom.resolve("WacomConfigInit"); + ptrWacomConfigOpenDevice = (PtrWacomConfigOpenDevice)wacom.resolve("WacomConfigOpenDevice"); + ptrWacomConfigGetRawParam = (PtrWacomConfigGetRawParam)wacom.resolve("WacomConfigGetRawParam"); + ptrWacomConfigCloseDevice = (PtrWacomConfigCloseDevice)wacom.resolve("WacomConfigCloseDevice"); + ptrWacomConfigTerm = (PtrWacomConfigTerm)wacom.resolve("WacomConfigTerm"); + + if (ptrWacomConfigInit == 0 || ptrWacomConfigOpenDevice == 0 || ptrWacomConfigGetRawParam == 0 + || ptrWacomConfigCloseDevice == 0 || ptrWacomConfigTerm == 0) { // either we have all, or we have none. ptrWacomConfigInit = 0; ptrWacomConfigOpenDevice = 0; ptrWacomConfigGetRawParam = 0; ptrWacomConfigCloseDevice = 0; ptrWacomConfigTerm = 0; - } } #endif } @@ -3048,6 +3058,21 @@ void QApplicationPrivate::_q_alertTimeOut() } } +Qt::KeyboardModifiers QApplication::queryKeyboardModifiers() +{ + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint keybstate; + for (int i = 0; i < ScreenCount(X11->display); ++i) { + if (XQueryPointer(X11->display, QX11Info::appRootWindow(i), &root, &child, + &root_x, &root_y, &win_x, &win_y, &keybstate)) + return X11->translateModifiers(keybstate & 0x00ff); + } + return 0; + +} + /***************************************************************************** Special lookup functions for windows that have been reparented recently *****************************************************************************/ @@ -3210,6 +3235,8 @@ int QApplication::x11ProcessEvent(XEvent* event) XFixesSelectionNotifyEvent *req = reinterpret_cast<XFixesSelectionNotifyEvent *>(event); X11->time = req->selection_timestamp; + if (req->selection == ATOM(_NET_WM_CM_S0)) + X11->compositingManagerRunning = req->owner; } #endif @@ -5225,14 +5252,15 @@ bool QETWidget::translateConfigEvent(const XEvent *event) bool trust = isVisible() && (d->topData()->parentWinId == XNone || d->topData()->parentWinId == QX11Info::appRootWindow()); + bool isCPos = false; if (event->xconfigure.send_event || trust) { // if a ConfigureNotify comes from a real sendevent request, we can // trust its values. newCPos.rx() = event->xconfigure.x + event->xconfigure.border_width; newCPos.ry() = event->xconfigure.y + event->xconfigure.border_width; + isCPos = true; } - if (isVisible()) QApplication::syncX(); @@ -5258,6 +5286,7 @@ bool QETWidget::translateConfigEvent(const XEvent *event) otherEvent.xconfigure.border_width; newCPos.ry() = otherEvent.xconfigure.y + otherEvent.xconfigure.border_width; + isCPos = true; } } #ifndef QT_NO_XSYNC @@ -5270,6 +5299,19 @@ bool QETWidget::translateConfigEvent(const XEvent *event) #endif // QT_NO_XSYNC } + if (!isCPos) { + // we didn't get an updated position of the toplevel. + // either we haven't moved or there is a bug in the window manager. + // anyway, let's query the position to be certain. + int x, y; + Window child; + XTranslateCoordinates(X11->display, internalWinId(), + QApplication::desktop()->screen(d->xinfo.screen())->internalWinId(), + 0, 0, &x, &y, &child); + newCPos.rx() = x; + newCPos.ry() = y; + } + QRect cr (geometry()); if (newCPos != cr.topLeft()) { // compare with cpos (exluding frame) QPoint oldPos = geometry().topLeft(); @@ -5312,18 +5354,6 @@ bool QETWidget::translateConfigEvent(const XEvent *event) } if (wasResize) { - static bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt(); - if (d->extra->compress_events && !slowResize && !data->in_show && isVisible()) { - QApplication::syncX(); - XEvent otherEvent; - while (XCheckTypedWindowEvent(X11->display, internalWinId(), ConfigureNotify, &otherEvent) - && !qt_x11EventFilter(&otherEvent) && !x11Event(&otherEvent) - && otherEvent.xconfigure.event == otherEvent.xconfigure.window) { - data->crect.setWidth(otherEvent.xconfigure.width); - data->crect.setHeight(otherEvent.xconfigure.height); - } - } - if (isVisible() && data->crect.size() != oldSize) { Q_ASSERT(d->extra->topextra); QWidgetBackingStore *bs = d->extra->topextra->backingStore.data(); @@ -5332,7 +5362,7 @@ bool QETWidget::translateConfigEvent(const XEvent *event) // resize optimization in order to get invalidated regions for resized widgets. // The optimization discards all invalidateBuffer() calls since we're going to // repaint everything anyways, but that's not the case with static contents. - if (!slowResize && !hasStaticContents) + if (!hasStaticContents) d->extra->topextra->inTopLevelResize = true; QResizeEvent e(data->crect.size(), oldSize); QApplication::sendSpontaneousEvent(this, &e); diff --git a/src/gui/kernel/qclipboard.cpp b/src/gui/kernel/qclipboard.cpp index e68a8c1..ef995eb 100644 --- a/src/gui/kernel/qclipboard.cpp +++ b/src/gui/kernel/qclipboard.cpp @@ -631,7 +631,7 @@ QByteArray QMimeDataWrapper::encodedData(const char *format) const return data->data(QLatin1String(format)); } else{ QVariant variant = data->imageData(); - QImage img = qVariantValue<QImage>(variant); + QImage img = qvariant_cast<QImage>(variant); QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); diff --git a/src/gui/kernel/qclipboard.h b/src/gui/kernel/qclipboard.h index 91f1303..d42f0c7 100644 --- a/src/gui/kernel/qclipboard.h +++ b/src/gui/kernel/qclipboard.h @@ -112,6 +112,7 @@ protected: friend class QBaseApplication; friend class QDragManager; friend class QMimeSource; + friend class QPlatformClipboard; private: Q_DISABLE_COPY(QClipboard) diff --git a/src/gui/kernel/qclipboard_qpa.cpp b/src/gui/kernel/qclipboard_qpa.cpp new file mode 100644 index 0000000..6a88129 --- /dev/null +++ b/src/gui/kernel/qclipboard_qpa.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qclipboard.h" + +#ifndef QT_NO_CLIPBOARD + +#include "qmimedata.h" +#include "private/qapplication_p.h" +#include "qplatformclipboard_qpa.h" + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +void QClipboard::clear(Mode mode) +{ + setMimeData(0,mode); +} + + +bool QClipboard::event(QEvent *e) +{ + return QObject::event(e); +} + +const QMimeData* QClipboard::mimeData(Mode mode) const +{ + QPlatformClipboard *clipboard = QApplicationPrivate::platformIntegration()->clipboard(); + if (!clipboard->supportsMode(mode)) return 0; + return clipboard->mimeData(mode); +} + +void QClipboard::setMimeData(QMimeData* src, Mode mode) +{ + QPlatformClipboard *clipboard = QApplicationPrivate::platformIntegration()->clipboard(); + if (!clipboard->supportsMode(mode)) return; + + clipboard->setMimeData(src,mode); + + emitChanged(mode); +} + +bool QClipboard::supportsMode(Mode mode) const +{ + QPlatformClipboard *clipboard = QApplicationPrivate::platformIntegration()->clipboard(); + return clipboard->supportsMode(mode); +} + +bool QClipboard::ownsMode(Mode mode) const +{ + if (mode == Clipboard) + qWarning("QClipboard::ownsClipboard: UNIMPLEMENTED!"); + return false; +} + +void QClipboard::connectNotify( const char * ) +{ +} + +void QClipboard::ownerDestroyed() +{ +} + +#endif // QT_NO_CLIPBOARD + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qcocoaapplication_mac.mm b/src/gui/kernel/qcocoaapplication_mac.mm index c46d9f4..6c47a9e 100644 --- a/src/gui/kernel/qcocoaapplication_mac.mm +++ b/src/gui/kernel/qcocoaapplication_mac.mm @@ -78,6 +78,7 @@ #include <private/qcocoaapplication_mac_p.h> #include <private/qcocoaapplicationdelegate_mac_p.h> #include <private/qt_cocoa_helpers_mac_p.h> +#include <private/qcocoaintrospection_p.h> QT_USE_NAMESPACE @@ -116,12 +117,30 @@ QT_USE_NAMESPACE quint64 lower = [event data1]; quint64 upper = [event data2]; QCocoaPostMessageArgs *args = reinterpret_cast<QCocoaPostMessageArgs *>(lower | (upper << 32)); - [args->target performSelector:args->selector]; + // Special case for convenience: if the argument is an NSNumber, we unbox it directly. + // Use NSValue instead if this behaviour is unwanted. + id a1 = ([args->arg1 isKindOfClass:[NSNumber class]]) ? (id)[args->arg1 intValue] : args->arg1; + id a2 = ([args->arg2 isKindOfClass:[NSNumber class]]) ? (id)[args->arg2 intValue] : args->arg2; + switch (args->argCount) { + case 0: + [args->target performSelector:args->selector]; + break; + case 1: + [args->target performSelector:args->selector withObject:a1]; + break; + case 3: + [args->target performSelector:args->selector withObject:a1 withObject:a2]; + break; + } + delete args; } -- (BOOL)qt_sendEvent:(NSEvent *)event +- (BOOL)qt_filterEvent:(NSEvent *)event { + if (qApp->macEventFilter(0, reinterpret_cast<EventRef>(event))) + return true; + if ([event type] == NSApplicationDefined) { switch ([event subtype]) { case QtCocoaEventSubTypePostMessage: @@ -138,20 +157,66 @@ QT_USE_NAMESPACE @implementation QNSApplication -// WARNING: If Qt did not create NSApplication (this can e.g. -// happend if Qt is used as a plugin from a 3rd-party cocoa -// application), QNSApplication::sendEvent will never be called. -// SO DO NOT RELY ON THIS FUNCTION BEING AVAILABLE. -// Plugin developers that _do_ control the NSApplication sub-class -// implementation of the 3rd-party application can call qt_sendEvent -// from the sub-class event handler (like we do here) to work around -// any issues. +- (void)qt_sendEvent_original:(NSEvent *)event +{ + Q_UNUSED(event); + // This method will only be used as a signature + // template for the method we add into NSApplication + // containing the original [NSApplication sendEvent:] implementation +} + +- (void)qt_sendEvent_replacement:(NSEvent *)event +{ + // This method (or its implementation to be precise) will + // be called instead of sendEvent if redirection occurs. + // 'self' will then be an instance of NSApplication + // (and not QNSApplication) + if (![NSApp qt_filterEvent:event]) + [self qt_sendEvent_original:event]; +} + - (void)sendEvent:(NSEvent *)event { - if (![self qt_sendEvent:event]) + // This method will be called if + // no redirection occurs + if (![NSApp qt_filterEvent:event]) [super sendEvent:event]; } +- (void)qtDispatcherToQAction:(id)sender +{ + // Forward actions sendt from the menu bar (e.g. quit) to the menu loader. + // Having this method here means that we are the last stop in the responder + // chain, and that we are able to handle menu actions even when no window is + // visible on screen. Note: If Qt is used as a plugin, Qt will not use a + // native menu bar. Hence, we will also not need to do any redirection etc. as + // we do with sendEvent. + [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender]; +} + @end +QT_BEGIN_NAMESPACE + +void qt_redirectNSApplicationSendEvent() +{ + if ([NSApp isMemberOfClass:[QNSApplication class]]) { + // No need to change implementation since Qt + // already controls a subclass of NSApplication + return; + } + + // Change the implementation of [NSApplication sendEvent] to the + // implementation of qt_sendEvent_replacement found in QNSApplication. + // And keep the old implementation that gets overwritten inside a new + // method 'qt_sendEvent_original' that we add to NSApplication + qt_cocoa_change_implementation( + [NSApplication class], + @selector(sendEvent:), + [QNSApplication class], + @selector(qt_sendEvent_replacement:), + @selector(qt_sendEvent_original:)); + } + +QT_END_NAMESPACE #endif diff --git a/src/gui/kernel/qcocoaapplication_mac_p.h b/src/gui/kernel/qcocoaapplication_mac_p.h index 29d1ecf..a8a16f3 100644 --- a/src/gui/kernel/qcocoaapplication_mac_p.h +++ b/src/gui/kernel/qcocoaapplication_mac_p.h @@ -101,11 +101,17 @@ QT_FORWARD_DECLARE_CLASS(QApplicationPrivate) - (int)QT_MANGLE_NAMESPACE(qt_validModesForFontPanel):(NSFontPanel *)fontPanel; - (void)qt_sendPostedMessage:(NSEvent *)event; -- (BOOL)qt_sendEvent:(NSEvent *)event; +- (BOOL)qt_filterEvent:(NSEvent *)event; @end @interface QNSApplication : NSApplication { } @end +QT_BEGIN_NAMESPACE + +void qt_redirectNSApplicationSendEvent(); + +QT_END_NAMESPACE + #endif diff --git a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm index 47b451b..cd9ad22 100644 --- a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm +++ b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm @@ -78,6 +78,7 @@ #import <private/qcocoaapplicationdelegate_mac_p.h> #import <private/qcocoamenuloader_mac_p.h> +#import <private/qcocoaapplication_mac_p.h> #include <private/qapplication_p.h> #include <private/qt_mac_p.h> #include <private/qt_cocoa_helpers_mac_p.h> @@ -89,6 +90,10 @@ QT_BEGIN_NAMESPACE extern void onApplicationChangedActivation(bool); // qapplication_mac.mm extern void qt_release_apple_event_handler(); //qapplication_mac.mm +extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp +extern QPointer<QWidget> qt_last_native_mouse_receiver; // qt_cocoa_helpers_mac.mm +extern QPointer<QWidget> qt_button_down; // qapplication_mac.cpp + QT_END_NAMESPACE QT_FORWARD_DECLARE_CLASS(QDesktopWidgetImplementation) @@ -173,7 +178,7 @@ static void cleanupCocoaApplicationDelegate() qtMenuLoader = menuLoader; } -- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader; +- (QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *)menuLoader { return [[qtMenuLoader retain] autorelease]; } @@ -253,15 +258,35 @@ static void cleanupCocoaApplicationDelegate() if (reflectionDelegate && [reflectionDelegate respondsToSelector:@selector(applicationDidBecomeActive:)]) [reflectionDelegate applicationDidBecomeActive:notification]; + onApplicationChangedActivation(true); + + if (!QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, 0); + qt_last_mouse_receiver = widgetUnderMouse; + qt_last_native_mouse_receiver = widgetUnderMouse ? + (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0; + } } -- (void)applicationDidResignActive:(NSNotification *)notification; +- (void)applicationDidResignActive:(NSNotification *)notification { if (reflectionDelegate && [reflectionDelegate respondsToSelector:@selector(applicationDidResignActive:)]) [reflectionDelegate applicationDidResignActive:notification]; + onApplicationChangedActivation(false); + + if (!QWidget::mouseGrabber()) + QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); + qt_last_mouse_receiver = 0; + qt_last_native_mouse_receiver = 0; + qt_button_down = 0; } - (void)applicationDidChangeScreenParameters:(NSNotification *)notification diff --git a/src/gui/kernel/qcocoaintrospection_mac.mm b/src/gui/kernel/qcocoaintrospection_mac.mm new file mode 100644 index 0000000..ed2fbea --- /dev/null +++ b/src/gui/kernel/qcocoaintrospection_mac.mm @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (c) 2007-2008, Apple, Inc. +** +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** +** * Redistributions of source code must retain the above copyright notice, +** this list of conditions and the following disclaimer. +** +** * Redistributions in binary form must reproduce the above copyright notice, +** this list of conditions and the following disclaimer in the documentation +** and/or other materials provided with the distribution. +** +** * Neither the name of Apple, Inc. nor the names of its contributors +** may be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +****************************************************************************/ + +#include <private/qcocoaintrospection_p.h> + +QT_BEGIN_NAMESPACE + +void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class proxyClass, SEL replacementSel, SEL backupSel) +{ +#ifndef QT_MAC_USE_COCOA + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) +#endif + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + // The following code replaces the _implementation_ for the selector we want to hack + // (originalSel) with the implementation found in proxyClass. Then it creates + // a new 'backup' method inside baseClass containing the old, original, + // implementation (fakeSel). You can let the proxy implementation of originalSel + // call fakeSel if needed (similar approach to calling a super class implementation). + // fakeSel must also be implemented in proxyClass, as the signature is used + // as template for the method one we add into baseClass. + // NB: You will typically never create any instances of proxyClass; we use it + // only for stealing its contents and put it into baseClass. + if (!replacementSel) + replacementSel = originalSel; + + Method originalMethod = class_getInstanceMethod(baseClass, originalSel); + Method replacementMethod = class_getInstanceMethod(proxyClass, replacementSel); + IMP originalImp = method_setImplementation(originalMethod, method_getImplementation(replacementMethod)); + + if (backupSel) { + Method backupMethod = class_getInstanceMethod(proxyClass, backupSel); + class_addMethod(baseClass, backupSel, originalImp, method_getTypeEncoding(backupMethod)); + } +#endif + } +} + +void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL backupSel) +{ +#ifndef QT_MAC_USE_COCOA + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) +#endif + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + Method originalMethod = class_getInstanceMethod(baseClass, originalSel); + Method backupMethodInBaseClass = class_getInstanceMethod(baseClass, backupSel); + method_setImplementation(originalMethod, method_getImplementation(backupMethodInBaseClass)); +#endif + } +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qcocoaintrospection_p.h b/src/gui/kernel/qcocoaintrospection_p.h new file mode 100644 index 0000000..e18646d --- /dev/null +++ b/src/gui/kernel/qcocoaintrospection_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (c) 2007-2008, Apple, Inc. +** +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** +** * Redistributions of source code must retain the above copyright notice, +** this list of conditions and the following disclaimer. +** +** * Redistributions in binary form must reproduce the above copyright notice, +** this list of conditions and the following disclaimer in the documentation +** and/or other materials provided with the distribution. +** +** * Neither the name of Apple, Inc. nor the names of its contributors +** may be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +** +****************************************************************************/ + +#include <qglobal.h> +#import <objc/objc-class.h> + +QT_BEGIN_NAMESPACE + +void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class proxyClass, SEL replacementSel = 0, SEL backupSel = 0); +void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL backupSel); + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qcocoamenuloader_mac.mm b/src/gui/kernel/qcocoamenuloader_mac.mm index 0d4d349..da11c3a 100644 --- a/src/gui/kernel/qcocoamenuloader_mac.mm +++ b/src/gui/kernel/qcocoamenuloader_mac.mm @@ -70,13 +70,13 @@ QT_USE_NAMESPACE showAllItem = [[appMenu itemWithTitle:@"Show All"] retain]; // Get the names in the nib to match the app name set by Qt. - NSString *appName = reinterpret_cast<const NSString*>(QCFString::toCFStringRef(qAppName())); + const NSString *appName = reinterpret_cast<const NSString*>(QCFString::toCFStringRef(qAppName())); [quitItem setTitle:[[quitItem title] stringByReplacingOccurrencesOfString:@"NewApplication" - withString:appName]]; + withString:const_cast<NSString *>(appName)]]; [hideItem setTitle:[[hideItem title] stringByReplacingOccurrencesOfString:@"NewApplication" - withString:appName]]; + withString:const_cast<NSString *>(appName)]]; [aboutItem setTitle:[[aboutItem title] stringByReplacingOccurrencesOfString:@"NewApplication" - withString:appName]]; + withString:const_cast<NSString *>(appName)]]; [appName release]; // Disable the items that don't do anything. If someone associates a QAction with them // They should get synced back in. @@ -170,12 +170,12 @@ QT_USE_NAMESPACE return [[aboutQtItem retain] autorelease]; } -- (NSMenuItem *)hideMenuItem; +- (NSMenuItem *)hideMenuItem { return [[hideItem retain] autorelease]; } -- (NSMenuItem *)appSpecificMenuItem; +- (NSMenuItem *)appSpecificMenuItem { // Create an App-Specific menu item, insert it into the menu and return // it as an autorelease item. diff --git a/src/gui/kernel/qcocoapanel_mac.mm b/src/gui/kernel/qcocoapanel_mac.mm index ef66be7..5b490cf 100644 --- a/src/gui/kernel/qcocoapanel_mac.mm +++ b/src/gui/kernel/qcocoapanel_mac.mm @@ -47,9 +47,10 @@ #import <private/qcocoaview_mac_p.h> #import <private/qcocoawindowcustomthemeframe_mac_p.h> #import <private/qcocoaapplication_mac_p.h> -#include <private/qapplication_p.h> -#include <private/qbackingstore_p.h> - +#import <private/qmultitouch_mac_p.h> +#import <private/qapplication_p.h> +#import <private/qbackingstore_p.h> +#import <private/qdnd_p.h> #include <QtGui/QWidget> diff --git a/src/gui/kernel/qcocoapanel_mac_p.h b/src/gui/kernel/qcocoapanel_mac_p.h index a9d6d68..ad02b16 100644 --- a/src/gui/kernel/qcocoapanel_mac_p.h +++ b/src/gui/kernel/qcocoapanel_mac_p.h @@ -50,20 +50,34 @@ // We mean it. // +#ifndef QCOCOAPANEL_MAC_P +#define QCOCOAPANEL_MAC_P + #include "qmacdefines_mac.h" #ifdef QT_MAC_USE_COCOA #import <Cocoa/Cocoa.h> QT_FORWARD_DECLARE_CLASS(QStringList); +QT_FORWARD_DECLARE_CLASS(QCocoaDropData); + +@interface NSPanel (QtIntegration) +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender; +- (void)draggingExited:(id <NSDraggingInfo>)sender; +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; +@end @interface QT_MANGLE_NAMESPACE(QCocoaPanel) : NSPanel { - bool leftButtonIsRightButton; QStringList *currentCustomDragTypes; + QCocoaDropData *dropData; + NSInteger dragEnterSequence; } + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; - (void)registerDragTypes; +- (void)drawRectOriginal:(NSRect)rect; @end #endif +#endif diff --git a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h b/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h index a2eb484..8c194cc 100644 --- a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h +++ b/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h @@ -62,7 +62,6 @@ extern void qt_event_request_window_change(QWidget *); // qapplication_mac.mm extern void qt_mac_send_posted_gl_updates(QWidget *widget); // qapplication_mac.mm Q_GLOBAL_STATIC(QPointer<QWidget>, currentDragTarget); - QT_END_NAMESPACE - (id)initWithContentRect:(NSRect)contentRect @@ -140,16 +139,6 @@ QT_END_NAMESPACE qt_dispatchTabletProximityEvent(tabletEvent); } -- (void)qtDispatcherToQAction:(id)sender -{ - // If this window is modal, the menu bar will be modally shaddowed. - // In that case, since the window will be in the first responder chain, - // we can still catch the trigger here and forward it to the menu bar. - // This is needed as a single modal dialog on Qt should be able to access - // the application menu (e.g. quit). - [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender]; -} - - (void)terminate:(id)sender { // This function is called from the quit item in the menubar when this window @@ -173,77 +162,27 @@ QT_END_NAMESPACE - (void)sendEvent:(NSEvent *)event { - if ([event type] == NSApplicationDefined) { - switch ([event subtype]) { - case QtCocoaEventSubTypePostMessage: - [NSApp qt_sendPostedMessage:event]; - return; - default: - break; - } - return; - } - - QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self]; - // Cocoa can hold onto the window after we've disavowed its knowledge. So, - // if we get sent an event afterwards just have it go through the super's - // version and don't do any stuff with Qt. - if (!widget) { - [super sendEvent:event]; - return; - } - [self retain]; - QT_MANGLE_NAMESPACE(QCocoaView) *view = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(qt_mac_nativeview_for(widget)); - Qt::MouseButton mouseButton = cocoaButton2QtButton([event buttonNumber]); bool handled = false; - // sometimes need to redirect mouse events to the popup. - QWidget *popup = qAppInstance()->activePopupWidget(); - if (popup && popup != widget) { - switch([event type]) - { - case NSLeftMouseDown: - if (!qt_button_down) - qt_button_down = widget; - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton); - // Don't call super here. This prevents us from getting the mouseUp event, - // which we need to send even if the mouseDown event was not accepted. - // (this is standard Qt behavior.) - break; - case NSRightMouseDown: - case NSOtherMouseDown: - if (!qt_button_down) - qt_button_down = widget; - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonPress, mouseButton); - break; - case NSLeftMouseUp: - case NSRightMouseUp: - case NSOtherMouseUp: - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseButtonRelease, mouseButton); - qt_button_down = 0; - break; - case NSMouseMoved: - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseMove, Qt::NoButton); - break; - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->view = view; - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->theEvent = event; - handled = qt_mac_handleMouseEvent(view, event, QEvent::MouseMove, mouseButton); - break; - default: - [super sendEvent:event]; - break; - } - } else { - [super sendEvent:event]; + switch([event type]) { + case NSMouseMoved: + // Cocoa sends move events to a parent and all its children under the mouse, much + // like Qt handles hover events. But we only want to handle the move event once, so + // to optimize a bit (since we subscribe for move event for all views), we handle it + // here before this logic happends. Note: it might be tempting to do this shortcut for + // all mouse events. The problem is that Cocoa does more than just find the correct view + // when sending the event, like raising windows etc. So avoid it as much as possible: + handled = qt_mac_handleMouseEvent(event, QEvent::MouseMove, Qt::NoButton, 0); + break; + default: + break; } - if (!handled) - qt_mac_dispatchNCMouseMessage(self, event, [self QT_MANGLE_NAMESPACE(qt_qwidget)], leftButtonIsRightButton); - + if (!handled) { + [super sendEvent:event]; + qt_mac_handleNonClientAreaMouseEvent(self, event); + } [self release]; } @@ -276,6 +215,56 @@ QT_END_NAMESPACE return [super frameViewClassForStyleMask:styleMask]; } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +- (void)touchesBeganWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesMovedWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesEndedWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesCancelledWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + -(void)registerDragTypes { // Calling registerForDraggedTypes below is slow, so only do @@ -298,21 +287,47 @@ QT_END_NAMESPACE NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil]; // Add custom types supported by the application. for (int i = 0; i < customTypes.size(); i++) { - [supportedTypes addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(customTypes[i]))]; + [supportedTypes addObject:qt_mac_QStringToNSString(customTypes[i])]; } [self registerForDraggedTypes:supportedTypes]; } } -- (QWidget *)dragTargetHitTest:(id <NSDraggingInfo>)sender +- (void)removeDropData +{ + if (dropData) { + delete dropData; + dropData = 0; + } +} + +- (void)addDropData:(id <NSDraggingInfo>)sender { - // Do a hittest to find the NSView under the - // mouse, and return the corresponding QWidget: - NSPoint windowPoint = [sender draggingLocation]; - NSView *candidateView = [[self contentView] hitTest:windowPoint]; - if (![candidateView isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) - return 0; - return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(candidateView) qt_qwidget]; + [self removeDropData]; + CFStringRef dropPasteboard = (CFStringRef) [[sender draggingPasteboard] name]; + dropData = new QCocoaDropData(dropPasteboard); +} + +- (void)changeDraggingCursor:(NSDragOperation)newOperation +{ + static SEL action = nil; + static bool operationSupported = false; + if (action == nil) { + action = NSSelectorFromString(@"operationNotAllowedCursor"); + if ([NSCursor respondsToSelector:action]) { + operationSupported = true; + } + } + if (operationSupported) { + NSCursor *notAllowedCursor = [NSCursor performSelector:action]; + bool isNotAllowedCursor = ([NSCursor currentCursor] == notAllowedCursor); + if (newOperation == NSDragOperationNone && !isNotAllowedCursor) { + [notAllowedCursor push]; + } else if (newOperation != NSDragOperationNone && isNotAllowedCursor) { + [notAllowedCursor pop]; + } + + } } - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender @@ -325,80 +340,211 @@ QT_END_NAMESPACE // registerForDraggedTypes on the views will severly degrade initialization time // for an application that uses a lot of drag subscribing widgets. - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = QApplication::widgetAt(globalPoint); + *currentDragTarget() = qwidget; + if (!qwidget) return NSDragOperationNone; - if (target->testAttribute(Qt::WA_DropSiteRegistered) == false) + if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) return NSDragOperationNone; - *currentDragTarget() = target; - return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender]; + [self addDropData:sender]; + + QMimeData *mimeData = dropData; + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); + QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + if ([sender draggingSource] != nil) { + // modifier flags might have changed, update it here since we don't send any input events. + QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); + modifiers = QApplication::keyboardModifiers(); + } else { + // when the source is from another application the above technique will not work. + modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); + } + + // send the drag enter event to the widget. + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + QDragEnterEvent qDEEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + QApplication::sendEvent(qwidget, &qDEEvent); + + if (!qDEEvent.isAccepted()) { + // The enter event was not accepted. We mark this by removing + // the drop data so we don't send subsequent drag move events: + [self removeDropData]; + [self changeDraggingCursor:NSDragOperationNone]; + return NSDragOperationNone; + } else { + // Send a drag move event immediately after a drag enter event (as per documentation). + QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + qDMEvent.setDropAction(qDEEvent.dropAction()); + qDMEvent.accept(); // accept by default, since enter event was accepted. + QApplication::sendEvent(qwidget, &qDMEvent); + + if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { + // Since we accepted the drag enter event, the widget expects + // future drage move events. + nsActions = NSDragOperationNone; + // Save as ignored in the answer rect. + qDMEvent.setDropAction(Qt::IgnoreAction); + } else { + nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction()); + } + + QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent); + [self changeDraggingCursor:nsActions]; + return nsActions; + } } -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender { - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = QApplication::widgetAt(globalPoint); + if (!qwidget) return NSDragOperationNone; - if (target == *currentDragTarget()) { - // The drag continues to move over the widget that we have sendt - // a draggingEntered message to. So just update the view: - return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingUpdated:sender]; - } else { - // The widget under the mouse has changed. - // So we need to fake enter/leave events: - if (*currentDragTarget()) - [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender]; - if (target->testAttribute(Qt::WA_DropSiteRegistered) == false) { - *currentDragTarget() = 0; - return NSDragOperationNone; + // First, check if the widget under the mouse has changed since the + // last drag move events. If so, we need to change target, and dispatch + // syntetic drag enter/leave events: + if (qwidget != *currentDragTarget()) { + if (*currentDragTarget() && dropData) { + QDragLeaveEvent de; + QApplication::sendEvent(*currentDragTarget(), &de); + [self removeDropData]; } - *currentDragTarget() = target; - return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender]; + return [self draggingEntered:sender]; + } + + if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) + return NSDragOperationNone; + + // If we have no drop data (which will be assigned inside draggingEntered), it means + // that the current drag target did not accept the enter event. If so, we ignore + // subsequent move events as well: + if (dropData == 0) { + [self changeDraggingCursor:NSDragOperationNone]; + return NSDragOperationNone; + } + + // If the mouse is still within the accepted rect (provided by + // the application on a previous event), we follow the optimization + // and just return the answer given at that point: + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + if (qt_mac_mouse_inside_answer_rect(localPoint) + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) == nsActions) { + NSDragOperation operation = QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction)); + [self changeDraggingCursor:operation]; + return operation; } + + QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; + Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions); + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + // Update modifiers: + if ([sender draggingSource] != nil) { + QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); + modifiers = QApplication::keyboardModifiers(); + } else { + modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); + } + + QMimeData *mimeData = dropData; + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + + // Insert the same drop action on the event according to + // what the application told us it should be on the previous event: + QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + if (QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction != Qt::IgnoreAction + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).buttons == qDMEvent.mouseButtons() + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).modifiers == qDMEvent.keyboardModifiers()) + qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction); + + // Now, end the drag move event to the widget: + qDMEvent.accept(); + QApplication::sendEvent(qwidget, &qDMEvent); + + NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction()); + if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { + // Ignore this event (we will still receive further + // notifications), save as ignored in the answer rect: + operation = NSDragOperationNone; + qDMEvent.setDropAction(Qt::IgnoreAction); + } + + qt_mac_copy_answer_rect(qDMEvent); + [self changeDraggingCursor:operation]; + + return operation; } -- (void)draggingExited:(id < NSDraggingInfo >)sender +- (void)draggingExited:(id <NSDraggingInfo>)sender { - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = *currentDragTarget(); + if (!qwidget) return; - if (*currentDragTarget()) { - [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender]; - *currentDragTarget() = 0; + if (dropData) { + QDragLeaveEvent de; + QApplication::sendEvent(qwidget, &de); + [self removeDropData]; } + + // Clean-up: + [self removeDropData]; + *currentDragTarget() = 0; + [self changeDraggingCursor:NSDragOperationEvery]; } -- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender { - QWidget *target = [self dragTargetHitTest:sender]; - if (!target) + QWidget *qwidget = *currentDragTarget(); + if (!qwidget) return NO; - BOOL dropResult = NO; - if (*currentDragTarget()) { - dropResult = [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) performDragOperation:sender]; - *currentDragTarget() = 0; - } - return dropResult; -} + *currentDragTarget() = 0; + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); -- (void)displayIfNeeded -{ + [self addDropData:sender]; - QWidget *qwidget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self]; - if (qwidget == 0) { - [super displayIfNeeded]; - return; - } + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); + QMimeData *mimeData = dropData; - if (QApplicationPrivate::graphicsSystem() != 0) { - if (QWidgetBackingStore *bs = qt_widget_private(qwidget)->maybeBackingStore()) - bs->sync(qwidget, qwidget->rect()); - } - [super displayIfNeeded]; + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + if (QDragManager::self()->object) + QDragManager::self()->dragPrivate()->target = qwidget; + + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + QDropEvent de(localPoint, qtAllowed, mimeData, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + QApplication::sendEvent(qwidget, &de); + + if (QDragManager::self()->object) + QDragManager::self()->dragPrivate()->executed_action = de.dropAction(); + + return de.isAccepted(); } // This is a hack and it should be removed once we find the real cause for @@ -433,8 +579,8 @@ static bool firstDrawingInvocation = true; - (void)drawRectSpecial:(NSRect)rect { // Call the original drawing method. - [self drawRectOriginal:rect]; - NSWindow *window = [self window]; + [id(self) drawRectOriginal:rect]; + NSWindow *window = [id(self) window]; NSToolbar *toolbar = [window toolbar]; if(!toolbar) { // There is no toolbar, we have to draw a line on top of the line drawn by Cocoa. @@ -454,3 +600,11 @@ static bool firstDrawingInvocation = true; } } } + +- (void)drawRectOriginal:(NSRect)rect +{ + Q_UNUSED(rect) + // This method implementation is here to silenct the compiler. + // See drawRectSpecial for information. +} + diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 61012d5..0fbae59 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -49,9 +49,10 @@ #include <private/qt_cocoa_helpers_mac_p.h> #include <private/qdnd_p.h> #include <private/qmacinputcontext_p.h> -#include <private/qmultitouch_mac_p.h> #include <private/qevent_p.h> #include <private/qbackingstore_p.h> +#include <private/qwindowsurface_raster_p.h> +#include <private/qunifiedtoolbarsurface_mac_p.h> #include <qscrollarea.h> #include <qhash.h> @@ -65,9 +66,14 @@ #include <qdebug.h> @interface NSEvent (Qt_Compile_Leopard_DeviceDelta) + // SnowLeopard: - (CGFloat)deviceDeltaX; - (CGFloat)deviceDeltaY; - (CGFloat)deviceDeltaZ; + // Lion: + - (CGFloat)scrollingDeltaX; + - (CGFloat)scrollingDeltaY; + - (CGFloat)scrollingDeltaZ; @end @interface NSEvent (Qt_Compile_Leopard_Gestures) @@ -76,70 +82,16 @@ QT_BEGIN_NAMESPACE -Q_GLOBAL_STATIC(DnDParams, qMacDnDParams); - -extern void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos); // qcursor_mac.mm +extern void qt_mac_update_cursor(); // qcursor_mac.mm extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp +extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp +extern QPointer<QWidget> qt_last_native_mouse_receiver; // qt_cocoa_helpers_mac.mm extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm -extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm +extern OSViewRef qt_mac_effectiveview_for(const QWidget *w); // qwidget_mac.mm extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); - -struct dndenum_mapper -{ - NSDragOperation mac_code; - Qt::DropAction qt_code; - bool Qt2Mac; -}; - -static dndenum_mapper dnd_enums[] = { - { NSDragOperationLink, Qt::LinkAction, true }, - { NSDragOperationMove, Qt::MoveAction, true }, - { NSDragOperationCopy, Qt::CopyAction, true }, - { NSDragOperationGeneric, Qt::CopyAction, false }, - { NSDragOperationEvery, Qt::ActionMask, false }, - { NSDragOperationNone, Qt::IgnoreAction, false } -}; - -static NSDragOperation qt_mac_mapDropAction(Qt::DropAction action) -{ - for (int i=0; dnd_enums[i].qt_code; i++) { - if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) { - return dnd_enums[i].mac_code; - } - } - return NSDragOperationNone; -} - -static NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions) -{ - NSDragOperation nsActions = NSDragOperationNone; - for (int i=0; dnd_enums[i].qt_code; i++) { - if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code)) - nsActions |= dnd_enums[i].mac_code; - } - return nsActions; -} - -static Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions) -{ - Qt::DropAction action = Qt::IgnoreAction; - for (int i=0; dnd_enums[i].mac_code; i++) { - if (nsActions & dnd_enums[i].mac_code) - return dnd_enums[i].qt_code; - } - return action; -} - -static Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions) -{ - Qt::DropActions actions = Qt::IgnoreAction; - for (int i=0; dnd_enums[i].mac_code; i++) { - if (nsActions & dnd_enums[i].mac_code) - actions |= dnd_enums[i].qt_code; - } - return actions; -} +extern QWidget *mac_mouse_grabber; +extern bool qt_mac_clearDirtyOnWidgetInsideDrawWidget; // qwidget.cpp static QColor colorFrom(NSColor *color) { @@ -185,6 +137,7 @@ extern "C" { extern NSString *NSTextInputReplacementRangeAttributeName; } +//#define ALIEN_DEBUG 1 #ifdef ALIEN_DEBUG static int qCocoaViewCount = 0; #endif @@ -202,11 +155,14 @@ static int qCocoaViewCount = 0; #ifdef ALIEN_DEBUG ++qCocoaViewCount; - qDebug() << "init: qCocoaViewCount is" << qCocoaViewCount; + qDebug() << "Alien: create native view for" << widget << ". qCocoaViewCount is:" << qCocoaViewCount; #endif composing = false; sendKeyEvents = true; + fromKeyDownEvent = false; + alienTouchCount = 0; + [self setHidden:YES]; return self; } @@ -221,262 +177,28 @@ static int qCocoaViewCount = 0; object:self]; } -- (void)resetCursorRects -{ - // [NSView addCursorRect] is slow, so bail out early if we can: - if (NSIsEmptyRect([self visibleRect])) - return; - - QWidget *cursorWidget = qwidget; - - if (cursorWidget->testAttribute(Qt::WA_TransparentForMouseEvents)) - cursorWidget = QApplication::widgetAt(qwidget->mapToGlobal(qwidget->rect().center())); - - if (cursorWidget == 0) - return; - - if (!cursorWidget->testAttribute(Qt::WA_SetCursor)) { - [super resetCursorRects]; - return; - } - - QRegion mask = qt_widget_private(cursorWidget)->extra->mask; - NSCursor *nscursor = static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursorWidget->cursor())); - // The mask could have the WA_MouseNoMask attribute set and that means that we have to ignore the mask. - if (mask.isEmpty() || cursorWidget->testAttribute(Qt::WA_MouseNoMask)) { - [self addCursorRect:[qt_mac_nativeview_for(cursorWidget) visibleRect] cursor:nscursor]; - } else { - const QVector<QRect> &rects = mask.rects(); - for (int i = 0; i < rects.size(); ++i) { - const QRect &rect = rects.at(i); - [self addCursorRect:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()) cursor:nscursor]; - } - } -} - -- (void)removeDropData -{ - if (dropData) { - delete dropData; - dropData = 0; - } -} - -- (void)addDropData:(id <NSDraggingInfo>)sender -{ - [self removeDropData]; - CFStringRef dropPasteboard = (CFStringRef) [[sender draggingPasteboard] name]; - dropData = new QCocoaDropData(dropPasteboard); -} - -- (void)changeDraggingCursor:(NSDragOperation)newOperation -{ - static SEL action = nil; - static bool operationSupported = false; - if (action == nil) { - action = NSSelectorFromString(@"operationNotAllowedCursor"); - if ([NSCursor respondsToSelector:action]) { - operationSupported = true; - } - } - if (operationSupported) { - NSCursor *notAllowedCursor = [NSCursor performSelector:action]; - bool isNotAllowedCursor = ([NSCursor currentCursor] == notAllowedCursor); - if (newOperation == NSDragOperationNone && !isNotAllowedCursor) { - [notAllowedCursor push]; - } else if (newOperation != NSDragOperationNone && isNotAllowedCursor) { - [notAllowedCursor pop]; - } - - } -} - -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - NSPoint windowPoint = [sender draggingLocation]; - dragEnterSequence = [sender draggingSequenceNumber]; - [self addDropData:sender]; - QMimeData *mimeData = dropData; - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint]; - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - QPoint posDrag(localPoint.x, localPoint.y); - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); - QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - if ([sender draggingSource] != nil) { - // modifier flags might have changed, update it here since we don't send any input events. - QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); - modifiers = QApplication::keyboardModifiers(); - } else { - // when the source is from another application the above technique will not work. - modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); - } - // send the drag enter event to the widget. - QDragEnterEvent qDEEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - QApplication::sendEvent(qwidget, &qDEEvent); - if (!qDEEvent.isAccepted()) { - // widget is not interested in this drag, so ignore this drop data. - [self removeDropData]; - [self changeDraggingCursor:NSDragOperationNone]; - return NSDragOperationNone; - } else { - // save the mouse position, used by draggingExited handler. - DnDParams *dndParams = [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]; - dndParams->activeDragEnterPos = windowPoint; - // send a drag move event immediately after a drag enter event (as per documentation). - QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - qDMEvent.setDropAction(qDEEvent.dropAction()); - qDMEvent.accept(); // accept by default, since enter event was accepted. - QApplication::sendEvent(qwidget, &qDMEvent); - if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { - // since we accepted the drag enter event, the widget expects - // future drage move events. - // ### check if we need to treat this like the drag enter event. - nsActions = NSDragOperationNone; - // Save as ignored in the answer rect. - qDMEvent.setDropAction(Qt::IgnoreAction); - } else { - nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction()); - } - QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent); - [self changeDraggingCursor:nsActions]; - return nsActions; - } - } -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - NSPoint windowPoint = [sender draggingLocation]; - // in cases like QFocusFrame, the view under the mouse might - // not have received the drag enter. Generate a synthetic - // drag enter event for that view. - if (dragEnterSequence != [sender draggingSequenceNumber]) - [self draggingEntered:sender]; - // drag enter event was rejected, so ignore the move event. - if (dropData == 0) { - [self changeDraggingCursor:NSDragOperationNone]; - return NSDragOperationNone; - } - // return last value, if we are still in the answerRect. - NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint]; - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - QPoint posDrag(localPoint.x, localPoint.y); - if (qt_mac_mouse_inside_answer_rect(posDrag) - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) == nsActions) { - NSDragOperation operation = QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction)); - [self changeDraggingCursor:operation]; - return operation; - } - // send drag move event to the widget - QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; - Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions); - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - if ([sender draggingSource] != nil) { - // modifier flags might have changed, update it here since we don't send any input events. - QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); - modifiers = QApplication::keyboardModifiers(); - } else { - // when the source is from another application the above technique will not work. - modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); - } - QMimeData *mimeData = dropData; - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - if (QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction != Qt::IgnoreAction - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).buttons == qDMEvent.mouseButtons() - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).modifiers == qDMEvent.keyboardModifiers()) - qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction); - qDMEvent.accept(); - QApplication::sendEvent(qwidget, &qDMEvent); - - NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction()); - if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { - // ignore this event (we will still receive further notifications) - operation = NSDragOperationNone; - // Save as ignored in the answer rect. - qDMEvent.setDropAction(Qt::IgnoreAction); - } - qt_mac_copy_answer_rect(qDMEvent); - [self changeDraggingCursor:operation]; - return operation; -} - -- (void)draggingExited:(id < NSDraggingInfo >)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - Q_UNUSED(sender); - dragEnterSequence = -1; - // drag enter event was rejected, so ignore the move event. - if (dropData) { - QDragLeaveEvent de; - QApplication::sendEvent(qwidget, &de); - [self removeDropData]; - } - [self changeDraggingCursor:NSDragOperationEvery]; - -} - -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender -{ - // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly - // from Cocoa. They modify the drag target, and might fake enter/leave events. - NSPoint windowPoint = [sender draggingLocation]; - dragEnterSequence = -1; - [self addDropData:sender]; - - NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint]; - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - QPoint posDrop(localPoint.x, localPoint.y); - - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); - QMimeData *mimeData = dropData; - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - // send the drop event to the widget. - QDropEvent de(posDrop, qtAllowed, mimeData, - QApplication::mouseButtons(), QApplication::keyboardModifiers()); - if (QDragManager::self()->object) - QDragManager::self()->dragPrivate()->target = qwidget; - QApplication::sendEvent(qwidget, &de); - if (QDragManager::self()->object) - QDragManager::self()->dragPrivate()->executed_action = de.dropAction(); - if (!de.isAccepted()) - return NO; - else - return YES; -} - - (void)dealloc { + QMacCocoaAutoReleasePool pool; delete composingText; [[NSNotificationCenter defaultCenter] removeObserver:self]; #ifdef ALIEN_DEBUG --qCocoaViewCount; - qDebug() << "qCocoaViewCount is" << qCocoaViewCount; + qDebug() << "Alien: widget deallocated. qCocoaViewCount is:" << qCocoaViewCount; #endif [super dealloc]; } -- (BOOL)isOpaque; +- (BOOL)isOpaque { if (!qwidgetprivate) return [super isOpaque]; return qwidgetprivate->isOpaque; } -- (BOOL)isFlipped; +- (BOOL)isFlipped { return YES; } @@ -508,7 +230,7 @@ static int qCocoaViewCount = 0; } // Make sure the opengl context is updated on resize. - if (qwidgetprivate && qwidgetprivate->isGLWidget) { + if (qwidgetprivate && qwidgetprivate->isGLWidget && [self window]) { qwidgetprivate->needWindowChange = true; QEvent event(QEvent::MacGLWindowChange); qApp->sendEvent(qwidget, &event); @@ -532,17 +254,98 @@ static int qCocoaViewCount = 0; if (!qwidget) return; - if (QApplicationPrivate::graphicsSystem() != 0) { - if (qwidgetprivate->maybeBackingStore()) { - // Drawing is handled on the window level - // See qcocoasharedwindowmethods_mac_p.h - if (!qwidget->testAttribute(Qt::WA_PaintOnScreen)) - return; + // Getting context. + CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + qt_mac_retain_graphics_context(context); + + // We use a different graphics system. + // + // Widgets that are set to paint on screen, specifically QGLWidget, + // requires the native engine to execute in order to be drawn. + if (QApplicationPrivate::graphicsSystem() != 0 && !qwidget->testAttribute(Qt::WA_PaintOnScreen)) { + + // Raster engine. + if (QApplicationPrivate::graphics_system_name == QLatin1String("raster")) { + + if (!qwidgetprivate->isInUnifiedToolbar) { + + // Qt handles the painting occuring inside the window. + // Cocoa also keeps track of all widgets as NSView and therefore might + // ask for a repainting of a widget even if Qt is already taking care of it. + // + // The only valid reason for Cocoa to call drawRect: is for window manipulation + // (ie. resize, ...). + // + // Qt will then forward the update to the children. + if (!qwidget->isWindow()) { + qt_mac_release_graphics_context(context); + return; + } + + QRasterWindowSurface *winSurface = dynamic_cast<QRasterWindowSurface *>(qwidget->windowSurface()); + if (!winSurface || !winSurface->needsFlush) { + qt_mac_release_graphics_context(context); + return; + } + + // Clip to region. + const QVector<QRect> &rects = winSurface->regionToFlush.rects(); + for (int i = 0; i < rects.size(); ++i) { + const QRect &rect = rects.at(i); + CGContextAddRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height())); + } + CGContextClip(context); + + QRect r = winSurface->regionToFlush.boundingRect(); + const CGRect area = CGRectMake(r.x(), r.y(), r.width(), r.height()); + + qt_mac_draw_image(context, winSurface->imageContext(), area, area); + + winSurface->needsFlush = false; + winSurface->regionToFlush = QRegion(); + + } else { + + QUnifiedToolbarSurface *unifiedSurface = qwidgetprivate->unifiedSurface; + if (!unifiedSurface) { + qt_mac_release_graphics_context(context); + return; + } + + int areaX = qwidgetprivate->toolbar_offset.x(); + int areaY = qwidgetprivate->toolbar_offset.y(); + int areaWidth = qwidget->geometry().width(); + int areaHeight = qwidget->geometry().height(); + const CGRect area = CGRectMake(areaX, areaY, areaWidth, areaHeight); + const CGRect drawingArea = CGRectMake(0, 0, areaWidth, areaHeight); + + qt_mac_draw_image(context, unifiedSurface->imageContext(), area, drawingArea); + + qwidgetprivate->flushRequested = false; + + } + + CGContextSynchronize(context); + qt_mac_release_graphics_context(context); + return; + } + + // Qt handles the painting occuring inside the window. + // Cocoa also keeps track of all widgets as NSView and therefore might + // ask for a repainting of a widget even if Qt is already taking care of it. + // + // The only valid reason for Cocoa to call drawRect: is for window manipulation + // (ie. resize, ...). + // + // Qt will then forward the update to the children. + if (qwidget->isWindow()) { + qwidget->update(qwidget->rect()); + qwidgetprivate->syncBackingStore(qwidget->rect()); } } - CGContextRef cg = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - qwidgetprivate->hd = cg; - CGContextSaveGState(cg); + + // Native engine. + qwidgetprivate->hd = context; if (qwidget->isVisible() && qwidget->updatesEnabled()) { //process the actual paint event. if (qwidget->testAttribute(Qt::WA_WState_InPaintEvent)) @@ -551,13 +354,13 @@ static int qCocoaViewCount = 0; const QRect qrect = QRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height); QRegion qrgn; - const NSRect *rects; - NSInteger count; - [self getRectsBeingDrawn:&rects count:&count]; - for (int i = 0; i < count; ++i) { - QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height); - qrgn += tmpRect; - } + const NSRect *rects; + NSInteger count; + [self getRectsBeingDrawn:&rects count:&count]; + for (int i = 0; i < count; ++i) { + QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height); + qrgn += tmpRect; + } if (!qwidget->isWindow() && !qobject_cast<QAbstractScrollArea *>(qwidget->parent())) { const QRegion &parentMask = qwidget->window()->mask(); @@ -577,48 +380,37 @@ static int qCocoaViewCount = 0; engine->setSystemClip(qrgn); if (qwidgetprivate->extra && qwidgetprivate->extra->hasMask) { CGRect widgetRect = CGRectMake(0, 0, qwidget->width(), qwidget->height()); - CGContextTranslateCTM (cg, 0, widgetRect.size.height); - CGContextScaleCTM(cg, 1, -1); + CGContextTranslateCTM (context, 0, widgetRect.size.height); + CGContextScaleCTM(context, 1, -1); if (qwidget->isWindow()) - CGContextClearRect(cg, widgetRect); - CGContextClipToMask(cg, widgetRect, qwidgetprivate->extra->imageMask); - CGContextScaleCTM(cg, 1, -1); - CGContextTranslateCTM (cg, 0, -widgetRect.size.height); + CGContextClearRect(context, widgetRect); + CGContextClipToMask(context, widgetRect, qwidgetprivate->extra->imageMask); + CGContextScaleCTM(context, 1, -1); + CGContextTranslateCTM (context, 0, -widgetRect.size.height); } if (qwidget->isWindow() && !qwidgetprivate->isOpaque - && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) { - CGContextClearRect(cg, NSRectToCGRect(aRect)); + && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) { + CGContextClearRect(context, NSRectToCGRect(aRect)); } - // Check for alien widgets, use qwidgetPrivate->drawWidget() to draw the widget if this - // is the case. This makes sure child widgets are drawn as well, Cocoa does not know about - // those and wont send them drawRect calls. - if (qwidget->testAttribute(Qt::WA_NativeWindow) && qt_widget_private(qwidget)->hasAlienChildren == false) { - if (engine && !qwidget->testAttribute(Qt::WA_NoSystemBackground) - && (qwidget->isWindow() || qwidget->autoFillBackground()) - || qwidget->testAttribute(Qt::WA_TintedBackground) - || qwidget->testAttribute(Qt::WA_StyledBackground)) { -#ifdef DEBUG_WIDGET_PAINT - if(doDebug) - qDebug(" Handling erase for [%s::%s]", qwidget->metaObject()->className(), - qwidget->objectName().local8Bit().data()); -#endif - QPainter p(qwidget); - qwidgetprivate->paintBackground(&p, qrgn, - qwidget->isWindow() ? QWidgetPrivate::DrawAsRoot : 0); - p.end(); - } - QPaintEvent e(qrgn); -#ifdef QT3_SUPPORT - e.setErased(true); -#endif - qt_sendSpontaneousEvent(qwidget, &e); - } else { - qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); // QWidgetPrivate::drawWidget sets this - QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget); - qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen | QWidgetPrivate::DrawRecursive, 0); - } + qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); + QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget); + + // We specify that we want to draw the widget itself, and + // all its children recursive. But we skip native children, because + // they will receive drawRect calls by themselves as needed: + int flags = QWidgetPrivate::DrawPaintOnScreen + | QWidgetPrivate::DrawRecursive + | QWidgetPrivate::DontDrawNativeChildren; + + if (qwidget->isWindow()) + flags |= QWidgetPrivate::DrawAsRoot; + + // Start to draw: + qt_mac_clearDirtyOnWidgetInsideDrawWidget = true; + qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), flags, 0); + qt_mac_clearDirtyOnWidgetInsideDrawWidget = false; if (!redirectionOffset.isNull()) QPainter::restoreRedirected(qwidget); @@ -627,19 +419,21 @@ static int qCocoaViewCount = 0; qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); if(!qwidget->testAttribute(Qt::WA_PaintOutsidePaintEvent) && qwidget->paintingActive()) qWarning("QWidget: It is dangerous to leave painters active on a" - " widget outside of the PaintEvent"); + " widget outside of the PaintEvent"); } qwidgetprivate->hd = 0; - CGContextRestoreGState(cg); + qt_mac_release_graphics_context(context); } - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { - if (!qwidget) + // Find the widget that should receive the event: + QPoint qlocal, qglobal; + QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::MouseButtonPress, qlocal, qglobal, qwidget, 0); + if (!widgetToGetMouse) return NO; - Q_UNUSED(theEvent); - return !qwidget->testAttribute(Qt::WA_MacNoClickThrough); + return !widgetToGetMouse->testAttribute(Qt::WA_MacNoClickThrough); } - (NSView *)hitTest:(NSPoint)aPoint @@ -690,161 +484,120 @@ static int qCocoaViewCount = 0; - (void)mouseEntered:(NSEvent *)event { - if (!qwidget) - return; - if (qwidgetprivate->data.in_destructor) - return; - - if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) { - QEvent enterEvent(QEvent::Enter); - NSPoint windowPoint = [event locationInWindow]; - NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint]; - NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil]; - QApplication::sendEvent(qwidget, &enterEvent); - qt_mouseover = qwidget; - - // Update cursor icon: - qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint()); - - // Send mouse move and hover events as well: - if (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window()) { - // This mouse move event should be sendt, even when mouse - // tracking is switched off (to trigger tooltips): - NSEvent *mouseEvent = [NSEvent mouseEventWithType:NSMouseMoved - location:windowPoint modifierFlags:[event modifierFlags] timestamp:[event timestamp] - windowNumber:[event windowNumber] context:[event context] eventNumber:[event eventNumber] - clickCount:0 pressure:0]; - qt_mac_handleMouseEvent(self, mouseEvent, QEvent::MouseMove, Qt::NoButton); - - if (qwidget->testAttribute(Qt::WA_Hover)) { - QHoverEvent he(QEvent::HoverEnter, QPoint(viewPoint.x, viewPoint.y), QPoint(-1, -1)); - QApplicationPrivate::instance()->notify_helper(qwidget, &he); - } - } - } + // Cocoa will not send a move event on mouseEnter. But since + // Qt expect this, we fake one now. See also mouseExited below + // for info about enter/leave event handling + NSEvent *nsmoveEvent = [NSEvent + mouseEventWithType:NSMouseMoved + location:[[self window] mouseLocationOutsideOfEventStream] + modifierFlags: [event modifierFlags] + timestamp: [event timestamp] + windowNumber: [event windowNumber] + context: [event context] + eventNumber: [event eventNumber] + clickCount: 0 + pressure: 0]; + + // Important: Cocoa sends us mouseEnter on all views under the mouse + // and not just the one on top. Therefore, to we cannot use qwidget + // as native widget for this case. Instead, we let qt_mac_handleMouseEvent + // resolve it (last argument set to 0): + qt_mac_handleMouseEvent(nsmoveEvent, QEvent::MouseMove, Qt::NoButton, 0); } - (void)mouseExited:(NSEvent *)event { - if (!qwidget) - return; - - QEvent leaveEvent(QEvent::Leave); - NSPoint globalPoint = [[event window] convertBaseToScreen:[event locationInWindow]]; - if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) { - QApplication::sendEvent(qwidget, &leaveEvent); - - // ### Think about if it is necessary to update the cursor, should only be for a few cases. - qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint()); - if (qwidget->testAttribute(Qt::WA_Hover) - && (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window())) { - QHoverEvent he(QEvent::HoverLeave, QPoint(-1, -1), - qwidget->mapFromGlobal(QApplicationPrivate::instance()->hoverGlobalPos)); - QApplicationPrivate::instance()->notify_helper(qwidget, &he); + // Note: normal enter/leave handling is done from within mouseMove. This handler + // catches the case when the mouse moves out of the window (which mouseMove do not). + // Updating the mouse cursor follows the same logic as enter/leave. And we update + // neither if a grab exists (even if the grab points to this widget, it seems, ref X11) + Q_UNUSED(event); + if (self == [[self window] contentView] && !qt_button_down && !QWidget::mouseGrabber()) { + qt_mac_update_cursor(); + // If the mouse exits the content view, but qt_mac_getTargetForMouseEvent still + // reports a target, it means that either there is a grab involved, or the mouse + // hovered over another window in the application. In both cases, move events will + // cause qt_mac_handleMouseEvent to be called, which will handle enter/leave. + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Leave, qlocal, qglobal, qwidget, &widgetUnderMouse); + + if (widgetUnderMouse == 0) { + QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); + qt_last_mouse_receiver = 0; + qt_last_native_mouse_receiver = 0; } } } - (void)flagsChanged:(NSEvent *)theEvent { - if (!qwidget) + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) return; - QWidget *widgetToGetKey = qwidget; - - QWidget *popup = qAppInstance()->activePopupWidget(); - if (popup && popup != qwidget->window()) - widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup; qt_dispatchModifiersChanged(theEvent, widgetToGetKey); [super flagsChanged:theEvent]; } - (void)mouseMoved:(NSEvent *)theEvent { - if (!qwidget) - return; - - // We always enable mouse tracking for all QCocoaView-s. In cases where we have - // child views, we will receive mouseMoved for both parent & the child (if - // mouse is over the child). We need to ignore the parent mouseMoved in such - // cases. - NSPoint windowPoint = [theEvent locationInWindow]; - NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; - if (candidateView && candidateView == self) { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); - } + // Important: this method will only be called when the view's window is _not_ inside + // QCocoaWindow/QCocoaPanel. Otherwise, [QCocoaWindow sendEvent] will handle the event + // before it ends up here. So, this method is added for supporting QMacNativeWidget. + // TODO: Cocoa send move events to all views under the mouse. So make sure we only + // handle the event for the widget on top when using QMacNativeWidget. + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)mouseDown:(NSEvent *)theEvent { - if (!qt_button_down) - qt_button_down = qwidget; - - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::LeftButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::LeftButton, qwidget); // Don't call super here. This prevents us from getting the mouseUp event, // which we need to send even if the mouseDown event was not accepted. // (this is standard Qt behavior.) } - - (void)mouseUp:(NSEvent *)theEvent { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::LeftButton); - - qt_button_down = 0; + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::LeftButton, qwidget); } - (void)rightMouseDown:(NSEvent *)theEvent { - if (!qt_button_down) - qt_button_down = qwidget; - - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::RightButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::RightButton, qwidget); } - (void)rightMouseUp:(NSEvent *)theEvent { - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::RightButton); - - qt_button_down = 0; + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::RightButton, qwidget); } - (void)otherMouseDown:(NSEvent *)theEvent { - if (!qt_button_down) - qt_button_down = qwidget; - Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]); - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, mouseButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, mouseButton, qwidget); } - (void)otherMouseUp:(NSEvent *)theEvent { Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]); - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, mouseButton); - - qt_button_down = 0; + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, mouseButton, qwidget); } - (void)mouseDragged:(NSEvent *)theEvent { - qMacDnDParams()->view = self; - qMacDnDParams()->theEvent = theEvent; - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)rightMouseDragged:(NSEvent *)theEvent { - qMacDnDParams()->view = self; - qMacDnDParams()->theEvent = theEvent; - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)otherMouseDragged:(NSEvent *)theEvent { - qMacDnDParams()->view = self; - qMacDnDParams()->theEvent = theEvent; - qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); } - (void)scrollWheel:(NSEvent *)theEvent @@ -855,25 +608,17 @@ static int qCocoaViewCount = 0; [currentIManager handleMouseEvent:theEvent]; } - NSPoint windowPoint = [theEvent locationInWindow]; - NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint]; - NSPoint localPoint = [self convertPoint:windowPoint fromView:nil]; - QPoint qlocal = QPoint(localPoint.x, localPoint.y); - QPoint qglobal = QPoint(globalPoint.x, flipYCoordinate(globalPoint.y)); Qt::MouseButtons buttons = QApplication::mouseButtons(); - bool wheelOK = false; Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]); - QWidget *widgetToGetMouse = qwidget; - // if popup is open it should get wheel events if the cursor is over the popup, - // otherwise the event should be ignored. - if (QWidget *popup = qAppInstance()->activePopupWidget()) { - if (!popup->geometry().contains(qglobal)) - return; - } + + // Find the widget that should receive the event: + QPoint qlocal, qglobal; + QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::Wheel, qlocal, qglobal, qwidget, 0); + if (!widgetToGetMouse) + return; int deltaX = 0; int deltaY = 0; - int deltaZ = 0; const EventRef carbonEvent = (EventRef)[theEvent eventRef]; const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0; @@ -886,58 +631,40 @@ static int qCocoaViewCount = 0; // It looks like 1/4 degrees per pixel behaves most native. // (NB: Qt expects the unit for delta to be 8 per degree): const int pixelsToDegrees = 2; // 8 * 1/4 - deltaX = [theEvent deviceDeltaX] * pixelsToDegrees; - deltaY = [theEvent deviceDeltaY] * pixelsToDegrees; - deltaZ = [theEvent deviceDeltaZ] * pixelsToDegrees; + if (QSysInfo::MacintoshVersion <= QSysInfo::MV_10_6) { + // Mac OS 10.6 + deltaX = [theEvent deviceDeltaX] * pixelsToDegrees; + deltaY = [theEvent deviceDeltaY] * pixelsToDegrees; + } else { + // Mac OS 10.7+ + deltaX = [theEvent scrollingDeltaX] * pixelsToDegrees; + deltaY = [theEvent scrollingDeltaY] * pixelsToDegrees; + } } else { // carbonEventKind == kEventMouseWheelMoved // Remove acceleration, and use either -120 or 120 as delta: deltaX = qBound(-120, int([theEvent deltaX] * 10000), 120); deltaY = qBound(-120, int([theEvent deltaY] * 10000), 120); - deltaZ = qBound(-120, int([theEvent deltaZ] * 10000), 120); } #ifndef QT_NO_WHEELEVENT + // ### Qt 5: Send one QWheelEvent with dx, dy and dz + + if (deltaX != 0 && deltaY != 0) + QMacScrollOptimization::initDelayedScroll(); + if (deltaX != 0) { QWheelEvent qwe(qlocal, qglobal, deltaX, buttons, keyMods, Qt::Horizontal); qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); - wheelOK = qwe.isAccepted(); - if (!wheelOK && QApplicationPrivate::focus_widget - && QApplicationPrivate::focus_widget != widgetToGetMouse) { - QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal, - deltaX, buttons, keyMods, Qt::Horizontal); - qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); - wheelOK = qwe2.isAccepted(); - } } - if (deltaY) { + if (deltaY != 0) { QWheelEvent qwe(qlocal, qglobal, deltaY, buttons, keyMods, Qt::Vertical); qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); - wheelOK = qwe.isAccepted(); - if (!wheelOK && QApplicationPrivate::focus_widget - && QApplicationPrivate::focus_widget != widgetToGetMouse) { - QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal, - deltaY, buttons, keyMods, Qt::Vertical); - qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); - wheelOK = qwe2.isAccepted(); - } } - if (deltaZ) { - // Qt doesn't explicitly support wheels with a Z component. In a misguided attempt to - // try to be ahead of the pack, I'm adding this extra value. - QWheelEvent qwe(qlocal, qglobal, deltaZ, buttons, keyMods, (Qt::Orientation)3); - qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); - wheelOK = qwe.isAccepted(); - if (!wheelOK && QApplicationPrivate::focus_widget - && QApplicationPrivate::focus_widget != widgetToGetMouse) { - QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal, - deltaZ, buttons, keyMods, (Qt::Orientation)3); - qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); - wheelOK = qwe2.isAccepted(); - } - } + if (deltaX != 0 && deltaY != 0) + QMacScrollOptimization::performDelayedScroll(); #endif //QT_NO_WHEELEVENT } @@ -952,35 +679,14 @@ static int qCocoaViewCount = 0; [super tabletPoint:tabletEvent]; } -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 -- (void)touchesBeganWithEvent:(NSEvent *)event; -{ - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesMovedWithEvent:(NSEvent *)event; -{ - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesEndedWithEvent:(NSEvent *)event; -{ - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesCancelledWithEvent:(NSEvent *)event; +- (void)magnifyWithEvent:(NSEvent *)event { - bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - -- (void)magnifyWithEvent:(NSEvent *)event; -{ - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -989,13 +695,18 @@ static int qCocoaViewCount = 0; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); qNGEvent.percentage = [event magnification]; - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } -- (void)rotateWithEvent:(NSEvent *)event; +- (void)rotateWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1004,13 +715,18 @@ static int qCocoaViewCount = 0; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); qNGEvent.percentage = -[event rotation]; - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } -- (void)swipeWithEvent:(NSEvent *)event; +- (void)swipeWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1026,13 +742,18 @@ static int qCocoaViewCount = 0; qNGEvent.angle = 90.0f; else if ([event deltaY] == -1) qNGEvent.angle = 270.0f; - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } -- (void)beginGestureWithEvent:(NSEvent *)event; +- (void)beginGestureWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1040,13 +761,18 @@ static int qCocoaViewCount = 0; qNGEvent.gestureType = QNativeGestureEvent::GestureBegin; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); - qt_sendSpontaneousEvent(qwidget, &qNGEvent); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); #endif // QT_NO_GESTURES } -- (void)endGestureWithEvent:(NSEvent *)event; +- (void)endGestureWithEvent:(NSEvent *)event { - if (!QApplicationPrivate::tryModalHelper(qwidget, 0)) + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) return; #ifndef QT_NO_GESTURES @@ -1054,9 +780,9 @@ static int qCocoaViewCount = 0; qNGEvent.gestureType = QNativeGestureEvent::GestureEnd; NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; qNGEvent.position = flipPoint(p).toPoint(); - qt_sendSpontaneousEvent(qwidget, &qNGEvent); -#endif // QT_NO_GESTURES + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); } +#endif // QT_NO_GESTURES - (void)frameDidChange:(NSNotification *)note { @@ -1099,16 +825,32 @@ static int qCocoaViewCount = 0; { if (!qwidget) return NO; - // disabled widget shouldn't get focus even if it's a window. + + // Disabled widget shouldn't get focus even if it's a window. // hence disabled windows will not get any key or mouse events. if (!qwidget->isEnabled()) return NO; - // Before accepting the focus for a window, we check that - // the focusWidget (if any) is not contained in the same window. - if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded - && (!qApp->focusWidget() || qApp->focusWidget()->window() != qwidget)) { - return YES; // Always do it, so that windows can accept key press events. + + if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded) { + QWidget *focusWidget = qApp->focusWidget(); + if (!focusWidget) { + // There is no focus widget, but we still want to receive key events + // for shortcut handling etc. So we accept first responer for the + // content view as a last resort: + return YES; + } + if (!focusWidget->internalWinId() && focusWidget->nativeParentWidget() == qwidget) { + // The current focus widget is alien, and hence, cannot get acceptsFirstResponder + // calls. Since the focus widget is a child of qwidget, we let this view say YES: + return YES; + } + if (focusWidget->window() != qwidget) { + // The current focus widget is in another window. Since cocoa + // suggest that this window should be key now, we accept: + return YES; + } } + return qwidget->focusPolicy() != Qt::NoFocus; } @@ -1116,10 +858,13 @@ static int qCocoaViewCount = 0; { if (!qwidget) return YES; + // Seems like the following test only triggers if this // view is inside a QMacNativeWidget: - if (qwidget == QApplication::focusWidget()) - qwidget->clearFocus(); +// if (QWidget *fw = QApplication::focusWidget()) { +// if (qwidget == fw || qwidget == fw->nativeParentWidget()) +// fw->clearFocus(); +// } return YES; } @@ -1148,11 +893,11 @@ static int qCocoaViewCount = 0; { Q_UNUSED(anImage); Q_UNUSED(aPoint); - qMacDnDParams()->performedAction = operation; + macCurrentDnDParameters()->performedAction = operation; if (QDragManager::self()->object && QDragManager::self()->dragPrivate()->executed_action != Qt::ActionMask) { - qMacDnDParams()->performedAction = - qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action); + macCurrentDnDParameters()->performedAction = + qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action); } } @@ -1167,49 +912,37 @@ static int qCocoaViewCount = 0; qwidgetprivate = 0; } -- (BOOL)qt_leftButtonIsRightButton -{ - return leftButtonIsRightButton; -} - -- (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped -{ - leftButtonIsRightButton = isSwapped; -} - -+ (DnDParams*)currentMouseEvent -{ - return qMacDnDParams(); -} - - (void)keyDown:(NSEvent *)theEvent { - sendKeyEvents = true; - - QWidget *widgetToGetKey = qwidget; + if (!qwidget) + return; + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return; - QWidget *popup = qAppInstance()->activePopupWidget(); - bool sendToPopup = false; - if (popup && popup != qwidget->window()) { - widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup; - sendToPopup = true; - } + sendKeyEvents = true; if (widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled) && !(widgetToGetKey->inputMethodHints() & Qt::ImhDigitsOnly || widgetToGetKey->inputMethodHints() & Qt::ImhFormattedNumbersOnly || widgetToGetKey->inputMethodHints() & Qt::ImhHiddenText)) { - [qt_mac_nativeview_for(widgetToGetKey) interpretKeyEvents:[NSArray arrayWithObject: theEvent]]; + fromKeyDownEvent = true; + [qt_mac_nativeview_for(qwidget) interpretKeyEvents:[NSArray arrayWithObject: theEvent]]; + fromKeyDownEvent = false; } + if (sendKeyEvents && !composing) { - bool keyOK = qt_dispatchKeyEvent(theEvent, widgetToGetKey); - if (!keyOK && !sendToPopup) { - // find the first responder that is not created by Qt and forward - // the event to it (for example if Qt widget is embedded into native). + bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey); + if (!keyEventEaten && qwidget) { + // The event is not yet eaten, and if Qt is embedded inside a native + // cocoa application, send it to first responder not owned by Qt. + // The exception is if widgetToGetKey was redirected to a popup. QWidget *toplevel = qwidget->window(); - if (toplevel && qt_widget_private(toplevel)->topData()->embedded) { - if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) - [w keyDown:theEvent]; + if (toplevel == widgetToGetKey->window()) { + if (qt_widget_private(toplevel)->topData()->embedded) { + if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) + [w keyDown:theEvent]; + } } } } @@ -1219,12 +952,21 @@ static int qCocoaViewCount = 0; - (void)keyUp:(NSEvent *)theEvent { if (sendKeyEvents) { - bool keyOK = qt_dispatchKeyEvent(theEvent, qwidget); - if (!keyOK) { + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return; + + bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey); + if (!keyEventEaten && qwidget) { + // The event is not yet eaten, and if Qt is embedded inside a native + // cocoa application, send it to first responder not owned by Qt. + // The exception is if widgetToGetKey was redirected to a popup. QWidget *toplevel = qwidget->window(); - if (toplevel && qt_widget_private(toplevel)->topData()->embedded) { - if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) - [w keyUp:theEvent]; + if (toplevel == widgetToGetKey->window()) { + if (qt_widget_private(toplevel)->topData()->embedded) { + if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) + [w keyUp:theEvent]; + } } } } @@ -1269,13 +1011,17 @@ static int qCocoaViewCount = 0; }; } - if ([aString length] && composing) { + // When entering characters through Character Viewer or Keyboard Viewer, the text is passed + // through this insertText method. Since we dont receive a keyDown Event in such cases, the + // composing flag will be false. + if (([aString length] && composing) || !fromKeyDownEvent) { // Send the commit string to the widget. composing = false; sendKeyEvents = false; QInputMethodEvent e; e.setCommitString(commitText); - qt_sendSpontaneousEvent(qwidget, &e); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); } else { // The key sequence "`q" on a French Keyboard will generate two calls to insertText before // it returns from interpretKeyEvents. The first call will turn off 'composing' and accept @@ -1297,7 +1043,7 @@ static int qCocoaViewCount = 0; QString qtText; // Cursor position is retrived from the range. QList<QInputMethodEvent::Attribute> attrs; - attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selRange.location, 1, QVariant()); + attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selRange.location + selRange.length, 1, QVariant()); if ([aString isKindOfClass:[NSAttributedString class]]) { qtText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string])); composingLength = qtText.length(); @@ -1339,8 +1085,11 @@ static int qCocoaViewCount = 0; 0, composingLength, format); } *composingText = qtText; + QInputMethodEvent e(qtText, attrs); - qt_sendSpontaneousEvent(qwidget, &e); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); + if (!composingLength) composing = false; } @@ -1350,7 +1099,8 @@ static int qCocoaViewCount = 0; if (composing) { QInputMethodEvent e; e.setCommitString(*composingText); - qt_sendSpontaneousEvent(qwidget, &e); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); } composingText->clear(); composing = false; @@ -1383,7 +1133,7 @@ static int qCocoaViewCount = 0; if (!selectedText.isEmpty()) { QCFString string(selectedText.mid(theRange.location, theRange.length)); const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string); - return [[[NSAttributedString alloc] initWithString:tmpString] autorelease]; + return [[[NSAttributedString alloc] initWithString:const_cast<NSString *>(tmpString)] autorelease]; } else { return nil; } @@ -1423,8 +1173,12 @@ static int qCocoaViewCount = 0; { Q_UNUSED(theRange); // The returned rect is always based on the internal cursor. - QRect mr(qwidget->inputMethodQuery(Qt::ImMicroFocus).toRect()); - QPoint mp(qwidget->mapToGlobal(QPoint(mr.bottomLeft()))); + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return NSZeroRect; + + QRect mr(widgetToGetKey->inputMethodQuery(Qt::ImMicroFocus).toRect()); + QPoint mp(widgetToGetKey->mapToGlobal(QPoint(mr.bottomLeft()))); NSRect rect ; rect.origin.x = mp.x(); rect.origin.y = flipYCoordinate(mp.y()); @@ -1442,10 +1196,11 @@ static int qCocoaViewCount = 0; - (NSArray*) validAttributesForMarkedText { - if (qwidget == 0) + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) return nil; - if (!qwidget->testAttribute(Qt::WA_InputMethodEnabled)) + if (!widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled)) return nil; // Not sure if that's correct, but it's saves a malloc. // Support only underline color/style. @@ -1459,7 +1214,7 @@ void QMacInputContext::reset() { QWidget *w = QInputContext::focusWidget(); if (w) { - NSView *view = qt_mac_nativeview_for(w); + NSView *view = qt_mac_effectiveview_for(w); if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) { QMacCocoaAutoReleasePool pool; QT_MANGLE_NAMESPACE(QCocoaView) *qc = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view); @@ -1476,7 +1231,7 @@ bool QMacInputContext::isComposing() const { QWidget *w = QInputContext::focusWidget(); if (w) { - NSView *view = qt_mac_nativeview_for(w); + NSView *view = qt_mac_effectiveview_for(w); if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) { return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view) isComposing]; } @@ -1569,39 +1324,45 @@ Qt::DropAction QDragManager::drag(QDrag *o) } else { hotspot = dragPrivate()->hotspot; } - // convert the image to NSImage. + + // Convert the image to NSImage: NSImage *image = (NSImage *)qt_mac_create_nsimage(pix); [image retain]; - DnDParams dndParams = *[QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]; - // save supported actions - [dndParams.view setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)]; - NSPoint imageLoc = {dndParams.localPoint.x - hotspot.x(), - dndParams.localPoint.y + pix.height() - hotspot.y()}; + + DnDParams *dndParams = macCurrentDnDParameters(); + QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(dndParams->view); + + // Save supported actions: + [theView setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)]; + QPoint pointInView = [theView qt_qwidget]->mapFromGlobal(dndParams->globalPoint); + NSPoint imageLoc = {pointInView.x() - hotspot.x(), pointInView.y() + pix.height() - hotspot.y()}; NSSize mouseOffset = {0.0, 0.0}; NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - NSPoint windowPoint = [dndParams.theEvent locationInWindow]; dragPrivate()->executed_action = Qt::ActionMask; - // do the drag - [dndParams.view retain]; - [dndParams.view dragImage:image - at:imageLoc - offset:mouseOffset - event:dndParams.theEvent - pasteboard:pboard - source:dndParams.view - slideBack:YES]; - // reset the implicit grab widget when drag ends because we will not - // receive the mouse release event when DND is active. + + // Execute the drag: + [theView retain]; + [theView dragImage:image + at:imageLoc + offset:mouseOffset + event:dndParams->theEvent + pasteboard:pboard + source:theView + slideBack:YES]; + + // Reset the implicit grab widget when drag ends because we will not + // receive the mouse release event when DND is active: qt_button_down = 0; - [dndParams.view release]; + [theView release]; [image release]; if (dragPrivate()) dragPrivate()->executed_action = Qt::IgnoreAction; object = 0; - Qt::DropAction performedAction(qt_mac_mapNSDragOperation(qMacDnDParams()->performedAction)); - // do post drag processing, if required. - if(performedAction != Qt::IgnoreAction) { - // check if the receiver points us to a file location. + Qt::DropAction performedAction(qt_mac_mapNSDragOperation(dndParams->performedAction)); + + // Do post drag processing, if required. + if (performedAction != Qt::IgnoreAction) { + // Check if the receiver points us to a file location. // if so, we need to do the file copy/move ourselves. QCFType<CFURLRef> pasteLocation = 0; PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation); @@ -1618,6 +1379,8 @@ Qt::DropAction QDragManager::drag(QDrag *o) } } } + + // Clean-up: o->setMimeData(0); o->deleteLater(); return performedAction; diff --git a/src/gui/kernel/qcocoaview_mac_p.h b/src/gui/kernel/qcocoaview_mac_p.h index d0a238d..e5e977b 100644 --- a/src/gui/kernel/qcocoaview_mac_p.h +++ b/src/gui/kernel/qcocoaview_mac_p.h @@ -58,56 +58,30 @@ QT_FORWARD_DECLARE_CLASS(QWidgetPrivate); QT_FORWARD_DECLARE_CLASS(QWidget); QT_FORWARD_DECLARE_CLASS(QEvent); -QT_FORWARD_DECLARE_CLASS(QCocoaDropData); QT_FORWARD_DECLARE_CLASS(QString); QT_FORWARD_DECLARE_CLASS(QStringList); -QT_BEGIN_NAMESPACE -struct DnDParams -{ - QT_MANGLE_NAMESPACE(QCocoaView) *view; - NSEvent *theEvent; - NSPoint localPoint; - NSDragOperation performedAction; - NSPoint activeDragEnterPos; -}; - -QT_END_NAMESPACE - -QT_FORWARD_DECLARE_STRUCT(DnDParams); - Q_GUI_EXPORT @interface QT_MANGLE_NAMESPACE(QCocoaView) : NSControl <NSTextInput> { QWidget *qwidget; QWidgetPrivate *qwidgetprivate; - bool leftButtonIsRightButton; - QCocoaDropData *dropData; NSDragOperation supportedActions; bool composing; int composingLength; bool sendKeyEvents; + bool fromKeyDownEvent; QString *composingText; - NSInteger dragEnterSequence; + @public int alienTouchCount; } - (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; - (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; - (void)frameDidChange:(NSNotification *)note; -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; -- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender; -- (void)draggingExited:(id < NSDraggingInfo >)sender; -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; -- (void)removeDropData; -- (void)addDropData:(id <NSDraggingInfo>)sender; - (void)setSupportedActions:(NSDragOperation)actions; - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal; - (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation; - (BOOL)isComposing; - (QWidget *)qt_qwidget; - (void) qt_clearQWidget; -- (BOOL)qt_leftButtonIsRightButton; -- (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped; -- (void)changeDraggingCursor:(NSDragOperation)newOperation; -+ (DnDParams*)currentMouseEvent; @end #endif diff --git a/src/gui/kernel/qcocoawindow_mac.mm b/src/gui/kernel/qcocoawindow_mac.mm index f081df7..fba69a3 100644 --- a/src/gui/kernel/qcocoawindow_mac.mm +++ b/src/gui/kernel/qcocoawindow_mac.mm @@ -47,6 +47,8 @@ #import <private/qt_cocoa_helpers_mac_p.h> #import <private/qcocoawindowcustomthemeframe_mac_p.h> #import <private/qcocoaapplication_mac_p.h> +#import <private/qdnd_p.h> +#import <private/qmultitouch_mac_p.h> #include <QtGui/QWidget> @@ -55,7 +57,7 @@ QT_USE_NAMESPACE @implementation NSWindow (QT_MANGLE_NAMESPACE(QWidgetIntegration)) -- (id)QT_MANGLE_NAMESPACE(qt_initWithQWidget):(QWidget*)widget contentRect:(NSRect)rect styleMask:(NSUInteger)mask; +- (id)QT_MANGLE_NAMESPACE(qt_initWithQWidget):(QWidget*)widget contentRect:(NSRect)rect styleMask:(NSUInteger)mask { self = [self initWithContentRect:rect styleMask:mask backing:NSBackingStoreBuffered defer:YES]; if (self) { diff --git a/src/gui/kernel/qcocoawindow_mac_p.h b/src/gui/kernel/qcocoawindow_mac_p.h index 7257853..9013f66 100644 --- a/src/gui/kernel/qcocoawindow_mac_p.h +++ b/src/gui/kernel/qcocoawindow_mac_p.h @@ -50,17 +50,20 @@ // We mean it. // +#ifndef QCOCOAWINDOW_MAC_P +#define QCOCOAWINDOW_MAC_P + #ifdef QT_MAC_USE_COCOA #include "qmacdefines_mac.h" #import <Cocoa/Cocoa.h> #include <private/qapplication_p.h> #include <private/qbackingstore_p.h> - enum { QtMacCustomizeWindow = 1 << 21 }; // This will one day be run over by QT_FORWARD_DECLARE_CLASS(QWidget); QT_FORWARD_DECLARE_CLASS(QStringList); +QT_FORWARD_DECLARE_CLASS(QCocoaDropData); @interface NSWindow (QtCoverForHackWithCategory) + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; @@ -71,14 +74,24 @@ QT_FORWARD_DECLARE_CLASS(QStringList); - (QWidget *)QT_MANGLE_NAMESPACE(qt_qwidget); @end +@interface NSWindow (QtIntegration) +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender; +- (void)draggingExited:(id <NSDraggingInfo>)sender; +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; +@end + @interface QT_MANGLE_NAMESPACE(QCocoaWindow) : NSWindow { - bool leftButtonIsRightButton; QStringList *currentCustomDragTypes; + QCocoaDropData *dropData; + NSInteger dragEnterSequence; } + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; - (void)registerDragTypes; +- (void)drawRectOriginal:(NSRect)rect; @end #endif +#endif diff --git a/src/gui/kernel/qcocoawindowdelegate_mac.mm b/src/gui/kernel/qcocoawindowdelegate_mac.mm index 8536f46..772fd2b 100644 --- a/src/gui/kernel/qcocoawindowdelegate_mac.mm +++ b/src/gui/kernel/qcocoawindowdelegate_mac.mm @@ -48,6 +48,9 @@ #include <qlayout.h> #include <qcoreapplication.h> #include <qmenubar.h> +#include <QMainWindow> +#include <QToolBar> +#include <private/qmainwindowlayout_p.h> QT_BEGIN_NAMESPACE extern QWidgetData *qt_qwidget_data(QWidget *); // qwidget.cpp @@ -138,7 +141,7 @@ static void cleanupCocoaWindowDelegate() } } -- (void)dumpMaximizedStateforWidget:(QWidget*)qwidget window:(NSWindow *)window; +- (void)dumpMaximizedStateforWidget:(QWidget*)qwidget window:(NSWindow *)window { if (!window) return; // Nothing to do. @@ -215,6 +218,24 @@ static void cleanupCocoaWindowDelegate() QWidgetPrivate::qt_mac_update_sizer(qwidget); [self syncSizeForWidget:qwidget toSize:newSize fromSize:oldSize]; } + + // We force the repaint to be synchronized with the resize of the window. + // Otherwise, the resize looks sluggish because we paint one event loop later. + if ([[window contentView] inLiveResize]) { + qwidget->repaint(); + + // We need to repaint the toolbar as well. + QMainWindow* mWindow = qobject_cast<QMainWindow*>(qwidget->window()); + if (mWindow) { + QMainWindowLayout *mLayout = qobject_cast<QMainWindowLayout*>(mWindow->layout()); + QList<QToolBar *> toolbarList = mLayout->qtoolbarsInUnifiedToolbarList; + + for (int i = 0; i < toolbarList.size(); ++i) { + QToolBar* toolbar = toolbarList.at(i); + toolbar->repaint(); + } + } + } } - (void)windowDidMove:(NSNotification *)notification diff --git a/src/gui/kernel/qcursor.h b/src/gui/kernel/qcursor.h index 4b589b4..d857eda 100644 --- a/src/gui/kernel/qcursor.h +++ b/src/gui/kernel/qcursor.h @@ -77,7 +77,7 @@ class QBitmap; class QPixmap; #if defined(Q_WS_MAC) -void qt_mac_set_cursor(const QCursor *c, const QPoint &p); +void qt_mac_set_cursor(const QCursor *c); #endif #if defined(Q_OS_SYMBIAN) extern void qt_symbian_show_pointer_sprite(); @@ -96,6 +96,10 @@ public: QCursor(const QCursor &cursor); ~QCursor(); QCursor &operator=(const QCursor &cursor); +#ifdef Q_COMPILER_RVALUE_REFS + inline QCursor &operator=(QCursor &&other) + { qSwap(d, other.d); return *this; } +#endif operator QVariant() const; Qt::CursorShape shape() const; @@ -126,7 +130,7 @@ public: static int x11Screen(); #elif defined(Q_WS_MAC) Qt::HANDLE handle() const; -#elif defined(Q_WS_QWS) +#elif defined(Q_WS_QWS) || defined(Q_WS_QPA) int handle() const; #elif defined(Q_OS_SYMBIAN) Qt::HANDLE handle() const; @@ -137,7 +141,8 @@ private: QCursorData *d; #if defined(Q_WS_MAC) friend void *qt_mac_nsCursorForQCursor(const QCursor &c); - friend void qt_mac_set_cursor(const QCursor *c, const QPoint &p); + friend void qt_mac_set_cursor(const QCursor *c); + friend void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse); #endif #if defined(Q_OS_SYMBIAN) friend void qt_symbian_show_pointer_sprite(); diff --git a/src/gui/kernel/qcursor_mac.mm b/src/gui/kernel/qcursor_mac.mm index 0b93cb1..0b0abaa 100644 --- a/src/gui/kernel/qcursor_mac.mm +++ b/src/gui/kernel/qcursor_mac.mm @@ -50,6 +50,7 @@ #include <AppKit/NSCursor.h> #include <qpainter.h> #include <private/qt_cocoa_helpers_mac_p.h> +#include <private/qapplication_p.h> QT_BEGIN_NAMESPACE @@ -60,6 +61,7 @@ extern QCursorData *qt_cursorTable[Qt::LastCursor + 1]; extern OSWindowRef qt_mac_window_for(const QWidget *); //qwidget_mac.cpp extern GrafPtr qt_mac_qd_context(const QPaintDevice *); //qpaintdevice_mac.cpp extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); //qapplication_mac.cpp +extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp /***************************************************************************** Internal QCursorData class @@ -95,18 +97,19 @@ protected: } }; -void *qt_mac_nsCursorForQCursor(const QCursor &c) +inline void *qt_mac_nsCursorForQCursor(const QCursor &c) { c.d->update(); return [[static_cast<NSCursor *>(c.d->curs.cp.nscursor) retain] autorelease]; } static QCursorData *currentCursor = 0; //current cursor -void qt_mac_set_cursor(const QCursor *c, const QPoint &) + +void qt_mac_set_cursor(const QCursor *c) { #ifdef QT_MAC_USE_COCOA - Q_UNUSED(c); - return; + QMacCocoaAutoReleasePool pool; + [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(*c)) set]; #else if (!c) { currentCursor = 0; @@ -128,35 +131,122 @@ void qt_mac_set_cursor(const QCursor *c, const QPoint &) c->d->curs.tc.anim->start(c->d->curs.tc.curs); } } + currentCursor = c->d; #endif } -void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos) +static QPointer<QWidget> lastWidgetUnderMouse = 0; +static QPointer<QWidget> lastMouseCursorWidget = 0; +static bool qt_button_down_on_prev_call = false; +static QCursor *grabCursor = 0; + +void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse) { -#ifdef QT_MAC_USE_COCOA - Q_UNUSED(globalPos); - return; -#else QCursor cursor(Qt::ArrowCursor); + if (qt_button_down) { + // The widget that is currently pressed + // grabs the mouse cursor: + widgetUnderMouse = qt_button_down; + qt_button_down_on_prev_call = true; + } else if (qt_button_down_on_prev_call) { + // Grab has been released, so do + // a full check: + qt_button_down_on_prev_call = false; + lastWidgetUnderMouse = 0; + lastMouseCursorWidget = 0; + } + if (QApplication::overrideCursor()) { cursor = *QApplication::overrideCursor(); - } else { - for(QWidget *w = QApplication::widgetAt(globalPos); w; w = w->parentWidget()) { - if(w->testAttribute(Qt::WA_SetCursor)) { - cursor = w->cursor(); - break; + } else if (grabCursor) { + cursor = *grabCursor; + } else if (widgetUnderMouse) { + if (widgetUnderMouse == lastWidgetUnderMouse) { + // Optimization that should hit when the widget under + // the mouse does not change as the mouse moves: + if (lastMouseCursorWidget) + cursor = lastMouseCursorWidget->cursor(); + } else { + QWidget *w = widgetUnderMouse; + for (; w; w = w->parentWidget()) { + if (w->testAttribute(Qt::WA_SetCursor)) { + cursor = w->cursor(); + break; + } + if (w->isWindow()) + break; } + // One final check in case we ran out of parents in the loop: + if (w && !w->testAttribute(Qt::WA_SetCursor)) + w = 0; + + lastWidgetUnderMouse = widgetUnderMouse; + lastMouseCursorWidget = w; } } - qt_mac_set_cursor(&cursor, globalPos); + +#ifdef QT_MAC_USE_COCOA + cursor.d->update(); + NSCursor *nsCursor = static_cast<NSCursor *>(cursor.d->curs.cp.nscursor); + if ([NSCursor currentCursor] != nsCursor) { + QMacCocoaAutoReleasePool pool; + [nsCursor set]; + } +#else + qt_mac_set_cursor(&cursor); #endif } void qt_mac_update_cursor() { - qt_mac_update_cursor_at_global_pos(QCursor::pos()); + // This function is similar to qt_mac_updateCursorWithWidgetUnderMouse + // except that is clears the optimization cache, and finds the widget + // under mouse itself. Clearing the cache is useful in cases where the + // application has been deactivated/activated etc. + // NB: since we dont have any true native widget, the call to + // qt_mac_getTargetForMouseEvent will fail when the mouse is over QMacNativeWidgets. +#ifdef QT_MAC_USE_COCOA + lastWidgetUnderMouse = 0; + lastMouseCursorWidget = 0; + QWidget *widgetUnderMouse = 0; + + if (qt_button_down) { + widgetUnderMouse = qt_button_down; + } else { + QPoint localPoint; + QPoint globalPoint; + qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, 0, &widgetUnderMouse); + } + qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse); +#else + qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(QCursor::pos())); +#endif +} + +void qt_mac_setMouseGrabCursor(bool set, QCursor *const cursor = 0) +{ + if (grabCursor) { + delete grabCursor; + grabCursor = 0; + } + if (set) { + if (cursor) + grabCursor = new QCursor(*cursor); + else if (lastMouseCursorWidget) + grabCursor = new QCursor(lastMouseCursorWidget->cursor()); + else + grabCursor = new QCursor(Qt::ArrowCursor); + } + qt_mac_update_cursor(); +} + +#ifndef QT_MAC_USE_COCOA +void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos) +{ + qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(globalPos)); } +#endif static int nextCursorId = Qt::BitmapCursor; @@ -427,7 +517,8 @@ void QCursorData::update() break; case Qt::DragCopyCursor: type = QCursorData::TYPE_ThemeCursor; - curs.cp.nscursor = [NSCursor dragCopyCursor]; + if ([NSCursor respondsToSelector:@selector(dragCopyCursor)]) + curs.cp.nscursor = [NSCursor performSelector:@selector(dragCopyCursor)]; break; case Qt::DragMoveCursor: type = QCursorData::TYPE_ThemeCursor; @@ -435,7 +526,8 @@ void QCursorData::update() break; case Qt::DragLinkCursor: type = QCursorData::TYPE_ThemeCursor; - curs.cp.nscursor = [NSCursor dragLinkCursor]; + if ([NSCursor respondsToSelector:@selector(dragLinkCursor)]) + curs.cp.nscursor = [NSCursor performSelector:@selector(dragLinkCursor)]; break; #define QT_USE_APPROXIMATE_CURSORS #ifdef QT_USE_APPROXIMATE_CURSORS diff --git a/src/gui/kernel/qcursor_p.h b/src/gui/kernel/qcursor_p.h index 320add6..775d46d 100644 --- a/src/gui/kernel/qcursor_p.h +++ b/src/gui/kernel/qcursor_p.h @@ -91,7 +91,7 @@ public: short hx, hy; #if defined (Q_WS_MAC) int mId; -#elif defined(Q_WS_QWS) +#elif defined(Q_WS_QWS) || defined(Q_WS_QPA) int id; #endif #if defined (Q_WS_WIN) diff --git a/src/gui/kernel/qcursor_qpa.cpp b/src/gui/kernel/qcursor_qpa.cpp new file mode 100644 index 0000000..c0b27be --- /dev/null +++ b/src/gui/kernel/qcursor_qpa.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qcursor.h> +#include <private/qcursor_p.h> +#include <qbitmap.h> + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +#ifndef QT_NO_CURSOR + +static int nextCursorId = Qt::BitmapCursor; + +/***************************************************************************** + Internal QCursorData class + *****************************************************************************/ + +QCursorData::QCursorData(Qt::CursorShape s) + : cshape(s), bm(0), bmm(0), hx(0), hy(0), id(s) +{ + ref = 1; +} + +QCursorData::~QCursorData() +{ + delete bm; + delete bmm; +} + + +/***************************************************************************** + Global cursors + *****************************************************************************/ + +extern QCursorData *qt_cursorTable[Qt::LastCursor + 1]; // qcursor.cpp + +int QCursor::handle() const +{ + return d->id; +} + + +QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY) +{ + if (!QCursorData::initialized) + QCursorData::initialize(); + if (bitmap.depth() != 1 || mask.depth() != 1 || bitmap.size() != mask.size()) { + qWarning("QCursor: Cannot create bitmap cursor; invalid bitmap(s)"); + QCursorData *c = qt_cursorTable[0]; + c->ref.ref(); + return c; + } + QCursorData *d = new QCursorData; + d->bm = new QBitmap(bitmap); + d->bmm = new QBitmap(mask); + d->cshape = Qt::BitmapCursor; + d->id = ++nextCursorId; + d->hx = hotX >= 0 ? hotX : bitmap.width() / 2; + d->hy = hotY >= 0 ? hotY : bitmap.height() / 2; + + return d; +} + +void QCursorData::update() +{ +} + +#endif //QT_NO_CURSOR + +extern int qt_last_x,qt_last_y; + +QPoint QCursor::pos() +{ + return QPoint(qt_last_x, qt_last_y); +} + +void QCursor::setPos(int x, int y) +{ + // Need to check, since some X servers generate null mouse move + // events, causing looping in applications which call setPos() on + // every mouse move event. + // + if (pos() == QPoint(x, y)) + return; +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qcursor_x11.cpp b/src/gui/kernel/qcursor_x11.cpp index 74b0bf6..167e8f8 100644 --- a/src/gui/kernel/qcursor_x11.cpp +++ b/src/gui/kernel/qcursor_x11.cpp @@ -55,6 +55,9 @@ #endif // QT_NO_XCURSOR #ifndef QT_NO_XFIXES +#ifndef Status +#define Status int +#endif # include <X11/extensions/Xfixes.h> #endif // QT_NO_XFIXES diff --git a/src/gui/kernel/qdesktopwidget_qpa.cpp b/src/gui/kernel/qdesktopwidget_qpa.cpp new file mode 100644 index 0000000..b9d2577 --- /dev/null +++ b/src/gui/kernel/qdesktopwidget_qpa.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesktopwidget.h" +#include "private/qapplication_p.h" +#include "private/qgraphicssystem_p.h" +#include <QWidget> +#include "private/qwidget_p.h" +#include "private/qdesktopwidget_qpa_p.h" +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +void QDesktopWidgetPrivate::updateScreenList() +{ + Q_Q(QDesktopWidget); + + QList<QPlatformScreen *> screenList = QApplicationPrivate::platformIntegration()->screens(); + int targetLength = screenList.length(); + int currentLength = screens.length(); + + // Add or remove screen widgets as necessary + if(currentLength > targetLength) { + QDesktopScreenWidget *screen; + while (currentLength-- > targetLength) { + screen = screens.takeLast(); + delete screen; + } + } + else if (currentLength < targetLength) { + QDesktopScreenWidget *screen; + while (currentLength < targetLength) { + screen = new QDesktopScreenWidget(currentLength++); + screens.append(screen); + } + } + + QRegion virtualGeometry; + + // update the geometry of each screen widget + for (int i = 0; i < screens.length(); i++) { + QRect screenGeometry = screenList.at(i)->geometry(); + screens.at(i)->setGeometry(screenGeometry); + virtualGeometry += screenGeometry; + } + + q->setGeometry(virtualGeometry.boundingRect()); +} + +QDesktopWidget::QDesktopWidget() + : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop) +{ + Q_D(QDesktopWidget); + setObjectName(QLatin1String("desktop")); + d->updateScreenList(); +} + +QDesktopWidget::~QDesktopWidget() +{ +} + +bool QDesktopWidget::isVirtualDesktop() const +{ + return QApplicationPrivate::platformIntegration()->isVirtualDesktop(); +} + +int QDesktopWidget::primaryScreen() const +{ + return 0; +} + +int QDesktopWidget::numScreens() const +{ + QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); + return qMax(pi->screens().size(), 1); +} + +QWidget *QDesktopWidget::screen(int screen) +{ + Q_D(QDesktopWidget); + if (screen < 0 || screen >= d->screens.length()) + return d->screens.at(0); + return d->screens.at(screen); +} + +const QRect QDesktopWidget::availableGeometry(int screenNo) const +{ + QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); + QList<QPlatformScreen *> screens = pi->screens(); + if (screenNo == -1) + screenNo = 0; + if (screenNo < 0 || screenNo >= screens.size()) + return QRect(); + else + return screens[screenNo]->availableGeometry(); +} + +const QRect QDesktopWidget::screenGeometry(int screenNo) const +{ + QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); + QList<QPlatformScreen *> screens = pi->screens(); + if (screenNo == -1) + screenNo = 0; + if (screenNo < 0 || screenNo >= screens.size()) + return QRect(); + else + return screens[screenNo]->geometry(); +} + +int QDesktopWidget::screenNumber(const QWidget *w) const +{ + if (!w) + return 0; + + QRect frame = w->frameGeometry(); + if (!w->isWindow()) + frame.moveTopLeft(w->mapToGlobal(QPoint(0, 0))); + const QPoint midpoint = (frame.topLeft() + frame.bottomRight()) / 2; + return screenNumber(midpoint); +} + +int QDesktopWidget::screenNumber(const QPoint &p) const +{ + QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); + QList<QPlatformScreen *> screens = pi->screens(); + + for (int i = 0; i < screens.size(); ++i) + if (screens[i]->geometry().contains(p)) + return i; + + return primaryScreen(); //even better would be closest screen +} + +void QDesktopWidget::resizeEvent(QResizeEvent *) +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qdesktopwidget_qpa_p.h b/src/gui/kernel/qdesktopwidget_qpa_p.h new file mode 100644 index 0000000..ddae7f3 --- /dev/null +++ b/src/gui/kernel/qdesktopwidget_qpa_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESKTOPWIDGET_QPA_P_H +#define QDESKTOPWIDGET_QPA_P_H + +#include "QDesktopWidget" +#include "private/qwidget_p.h" + +class QDesktopScreenWidget : public QWidget { + Q_OBJECT +public: + QDesktopScreenWidget(int screenNumber = -1) + { + setWindowFlags(Qt::Desktop); + setVisible(false); + QTLWExtra *topData = d_func()->topData(); + topData->screenIndex = screenNumber; + } +}; + +class QDesktopWidgetPrivate : public QWidgetPrivate { + Q_DECLARE_PUBLIC(QDesktopWidget) + +public: + ~QDesktopWidgetPrivate() {foreach(QDesktopScreenWidget *s, screens) delete s; } + void updateScreenList(); + + QList<QDesktopScreenWidget *> screens; +}; + +#endif // QDESKTOPWIDGET_QPA_P_H diff --git a/src/gui/kernel/qdesktopwidget_win.cpp b/src/gui/kernel/qdesktopwidget_win.cpp index 16aa142..f77cc1f 100644 --- a/src/gui/kernel/qdesktopwidget_win.cpp +++ b/src/gui/kernel/qdesktopwidget_win.cpp @@ -156,10 +156,8 @@ void QDesktopWidgetPrivate::init(QDesktopWidget *that) #ifndef Q_OS_WINCE QSystemLibrary user32Lib(QLatin1String("user32")); - if (user32Lib.load()) { - enumDisplayMonitors = (EnumFunc)user32Lib.resolve("EnumDisplayMonitors"); - getMonitorInfo = (InfoFunc)user32Lib.resolve("GetMonitorInfoW"); - } + enumDisplayMonitors = (EnumFunc)user32Lib.resolve("EnumDisplayMonitors"); + getMonitorInfo = (InfoFunc)user32Lib.resolve("GetMonitorInfoW"); if (!enumDisplayMonitors || !getMonitorInfo) { screenCount = GetSystemMetrics(80); // SM_CMONITORS @@ -174,11 +172,9 @@ void QDesktopWidgetPrivate::init(QDesktopWidget *that) getMonitorInfo = 0; #else QSystemLibrary coreLib(QLatin1String("coredll")); - if (coreLib.load()) { - // CE >= 4.0 case - enumDisplayMonitors = (EnumFunc)coreLib.resolve("EnumDisplayMonitors"); - getMonitorInfo = (InfoFunc)coreLib.resolve("GetMonitorInfo"); - } + // CE >= 4.0 case + enumDisplayMonitors = (EnumFunc)coreLib.resolve("EnumDisplayMonitors"); + getMonitorInfo = (InfoFunc)coreLib.resolve("GetMonitorInfo"); if ((!enumDisplayMonitors || !getMonitorInfo)) { screenCount = GetSystemMetrics(SM_CMONITORS); diff --git a/src/gui/kernel/qdnd_mac.mm b/src/gui/kernel/qdnd_mac.mm index f8361de..77926bd 100644 --- a/src/gui/kernel/qdnd_mac.mm +++ b/src/gui/kernel/qdnd_mac.mm @@ -491,7 +491,7 @@ bool QWidgetPrivate::qt_mac_dnd_event(uint kind, DragRef dragRef) SetThemeCursor(cursor); } if(found_cursor) { - qt_mac_set_cursor(0, QPoint()); //just use our's + qt_mac_set_cursor(0); //just use our's } else { QCursor cursor(Qt::ArrowCursor); if(qApp && qApp->overrideCursor()) { @@ -504,7 +504,7 @@ bool QWidgetPrivate::qt_mac_dnd_event(uint kind, DragRef dragRef) } } } - qt_mac_set_cursor(&cursor, QPoint(mouse.h, mouse.v)); + qt_mac_set_cursor(&cursor); } //idle things diff --git a/src/gui/kernel/qdnd_p.h b/src/gui/kernel/qdnd_p.h index 5681ed8..74fa9ae 100644 --- a/src/gui/kernel/qdnd_p.h +++ b/src/gui/kernel/qdnd_p.h @@ -76,7 +76,7 @@ class QEventLoop; #if !(defined(QT_NO_DRAGANDDROP) && defined(QT_NO_CLIPBOARD)) -class QInternalMimeData : public QMimeData +class Q_GUI_EXPORT QInternalMimeData : public QMimeData { Q_OBJECT public: @@ -261,11 +261,11 @@ public: #endif private: -#ifdef Q_WS_QWS +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) Qt::DropAction currentActionForOverrideCursor; #endif #ifdef Q_OS_SYMBIAN -#ifndef QT_NO_CURSOR +#ifndef QT_NO_CURSOR QCursor overrideCursor; #endif #endif diff --git a/src/gui/kernel/qdnd_x11.cpp b/src/gui/kernel/qdnd_x11.cpp index 750ddf8..5847021 100644 --- a/src/gui/kernel/qdnd_x11.cpp +++ b/src/gui/kernel/qdnd_x11.cpp @@ -1112,21 +1112,6 @@ void qt_xdnd_send_leave() waiting_for_status = false; } -// TODO: remove and use QApplication::currentKeyboardModifiers() in Qt 4.8. -static Qt::KeyboardModifiers currentKeyboardModifiers() -{ - Window root; - Window child; - int root_x, root_y, win_x, win_y; - uint keybstate; - for (int i = 0; i < ScreenCount(X11->display); ++i) { - if (XQueryPointer(X11->display, QX11Info::appRootWindow(i), &root, &child, - &root_x, &root_y, &win_x, &win_y, &keybstate)) - return X11->translateModifiers(keybstate & 0x00ff); - } - return 0; -} - void QX11Data::xdndHandleDrop(QWidget *, const XEvent * xe, bool passive) { DEBUG("xdndHandleDrop"); @@ -1166,16 +1151,24 @@ void QX11Data::xdndHandleDrop(QWidget *, const XEvent * xe, bool passive) // some XEMBEDding, so try to find the real QMimeData used // based on the timestamp for this drop. QMimeData *dropData = 0; - int at = findXdndDropTransactionByTime(qt_xdnd_target_current_time); - if (at != -1) + const int at = findXdndDropTransactionByTime(qt_xdnd_target_current_time); + if (at != -1) { dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data; + // Can't use the source QMimeData if we need the image conversion code from xdndObtainData + if (dropData && dropData->hasImage()) + dropData = 0; + } // if we can't find it, then use the data in the drag manager - if (!dropData) - dropData = (manager->object) ? manager->dragPrivate()->data : manager->dropData; + if (!dropData) { + if (manager->object && !manager->dragPrivate()->data->hasImage()) + dropData = manager->dragPrivate()->data; + else + dropData = manager->dropData; + } // Drop coming from another app? Update keyboard modifiers. if (!qt_xdnd_dragging) { - QApplicationPrivate::modifier_buttons = currentKeyboardModifiers(); + QApplicationPrivate::modifier_buttons = QApplication::queryKeyboardModifiers(); } QDropEvent de(qt_xdnd_current_position, possible_actions, dropData, @@ -1855,8 +1848,16 @@ static QVariant xdndObtainData(const char *format, QVariant::Type requestedType) && (!(w->windowType() == Qt::Desktop) || w->acceptDrops())) { QDragPrivate * o = QDragManager::self()->dragPrivate(); - if (o->data->hasFormat(QLatin1String(format))) - result = o->data->data(QLatin1String(format)); + const QString mimeType = QString::fromLatin1(format); + if (o->data->hasFormat(mimeType)) { + result = o->data->data(mimeType); + } else if (mimeType.startsWith(QLatin1String("image/")) && o->data->hasImage()) { + // ### duplicated from QInternalMimeData::renderDataHelper + QImage image = qvariant_cast<QImage>(o->data->imageData()); + QBuffer buf(&result); + buf.open(QBuffer::WriteOnly); + image.save(&buf, mimeType.mid(mimeType.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper()); + } return result; } diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index a136a6e..e1df267 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -43,6 +43,7 @@ #include "qcursor.h" #include "qapplication.h" #include "private/qapplication_p.h" +#include "private/qevent_p.h" #include "private/qkeysequence_p.h" #include "qwidget.h" #include "qgraphicsview.h" @@ -53,6 +54,10 @@ #include "qgesture.h" #include "qgesture_p.h" +#ifdef Q_OS_SYMBIAN +#include "private/qcore_symbian_p.h" +#endif + QT_BEGIN_NAMESPACE /*! @@ -137,8 +142,7 @@ QInputEvent::~QInputEvent() and QWidget::mouseMoveEvent() to receive mouse events in your own widgets. - \sa QWidget::setMouseTracking() QWidget::grabMouse() - QCursor::pos() + \sa QWidget::setMouseTracking() QWidget::grabMouse() QCursor::pos() */ /*! @@ -724,12 +728,12 @@ QWheelEvent::QWheelEvent(const QPoint &pos, const QPoint& globalPos, int delta, The \a type parameter must be QEvent::KeyPress, QEvent::KeyRelease, or QEvent::ShortcutOverride. - Int \a key is the code for the Qt::Key that the event loop should listen - for. If \a key is 0, the event is not a result of a known key; for + Int \a key is the code for the Qt::Key that the event loop should listen + for. If \a key is 0, the event is not a result of a known key; for example, it may be the result of a compose sequence or keyboard macro. - The \a modifiers holds the keyboard modifiers, and the given \a text - is the Unicode text that the key generated. If \a autorep is true, - isAutoRepeat() will be true. \a count is the number of keys involved + The \a modifiers holds the keyboard modifiers, and the given \a text + is the Unicode text that the key generated. If \a autorep is true, + isAutoRepeat() will be true. \a count is the number of keys involved in the event. */ QKeyEvent::QKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, const QString& text, @@ -1658,7 +1662,7 @@ Qt::ButtonState QContextMenuEvent::state() const string is controlled by the widget only). The AttributeType enum describes the different attributes that can be set. - A class implementing QWidget::inputMethodEvent() or + A class implementing QWidget::inputMethodEvent() or QGraphicsItem::inputMethodEvent() should at least understand and honor the \l TextFormat and \l Cursor attributes. @@ -3023,9 +3027,16 @@ QShowEvent::~QShowEvent() This event is only used to notify the application of a request. It may be safely ignored. - \note This class is currently supported for Mac OS X only. + \note This class is currently supported for Mac OS X and Symbian only. */ +QFileOpenEventPrivate::~QFileOpenEventPrivate() +{ +#ifdef Q_OS_SYMBIAN + file.Close(); +#endif +} + /*! \internal @@ -3049,6 +3060,22 @@ QFileOpenEvent::QFileOpenEvent(const QUrl &url) f = url.toLocalFile(); } +#ifdef Q_OS_SYMBIAN +/*! \internal +*/ +QFileOpenEvent::QFileOpenEvent(const RFile &fileHandle) + : QEvent(FileOpen) +{ + TFileName fullName; + fileHandle.FullName(fullName); + f = qt_TDesC2QString(fullName); + QScopedPointer<QFileOpenEventPrivate> priv(new QFileOpenEventPrivate(QUrl::fromLocalFile(f))); + // Duplicate here allows the file handle to be valid after S60 app construction is complete. + qt_symbian_throwIfError(priv->file.Duplicate(fileHandle)); + d = reinterpret_cast<QEventPrivate *>(priv.take()); +} +#endif + /*! \internal */ QFileOpenEvent::~QFileOpenEvent() @@ -3074,6 +3101,39 @@ QUrl QFileOpenEvent::url() const return reinterpret_cast<const QFileOpenEventPrivate *>(d)->url; } +/*! + \fn bool QFileOpenEvent::openFile(QFile &file, QIODevice::OpenMode flags) const + + Opens a QFile on the \a file referenced by this event in the mode specified + by \a flags. Returns true if successful; otherwise returns false. + + This is necessary as some files cannot be opened by name, but require specific + information stored in this event. + For example, if this QFileOpenEvent contains a request to open a Symbian data caged file, + the QFile could only be opened from the Symbian RFile used in the construction of this event. + + \since 4.8 +*/ +bool QFileOpenEvent::openFile(QFile &file, QIODevice::OpenMode flags) const +{ + file.setFileName(f); +#ifdef Q_OS_SYMBIAN + const QFileOpenEventPrivate *priv = reinterpret_cast<const QFileOpenEventPrivate *>(d); + if (priv->file.SubSessionHandle()) { + RFile dup; + // Duplicate here means that the opened QFile will continue to be valid beyond the lifetime of this QFileOpenEvent. + // It also allows openFile to be used in threads other than the thread in which the QFileOpenEvent was created. + if (dup.Duplicate(priv->file) == KErrNone) { + QScopedPointer<RFile, QScopedPointerRCloser<RFile> > dupCloser(&dup); + bool open = file.open(dup, flags, QFile::AutoCloseHandle); + dupCloser.take(); + return open; + } + } +#endif + return file.open(flags); +} + #ifndef QT_NO_TOOLBAR /*! \internal @@ -3622,7 +3682,7 @@ QMenubarUpdatedEvent::QMenubarUpdatedEvent(QMenuBar * const menuBar) #endif -/*! +/*! \class QTouchEvent \brief The QTouchEvent class contains parameters that describe a touch event. \since 4.6 @@ -4392,7 +4452,7 @@ void QGestureEvent::accept(QGesture *gesture) of calling \l{QGestureEvent::setAccepted()}{setAccepted(gesture, false)}. Clearing the accept flag indicates that the event receiver does not - want the gesture. Unwanted gestures may be propgated to the parent widget. + want the gesture. Unwanted gestures may be propagated to the parent widget. \sa QGestureEvent::accept() */ diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index ffcca9b..4f2e61f 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -54,6 +54,11 @@ #include <QtCore/qvariant.h> #include <QtCore/qmap.h> #include <QtCore/qset.h> +#include <QtCore/qfile.h> + +#ifdef Q_OS_SYMBIAN +class RFile; +#endif QT_BEGIN_HEADER @@ -641,10 +646,14 @@ class Q_GUI_EXPORT QFileOpenEvent : public QEvent public: QFileOpenEvent(const QString &file); QFileOpenEvent(const QUrl &url); +#ifdef Q_OS_SYMBIAN + QFileOpenEvent(const RFile &fileHandle); +#endif ~QFileOpenEvent(); inline QString file() const { return f; } QUrl url() const; + bool openFile(QFile &file, QIODevice::OpenMode flags) const; private: QString f; }; diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index 6d882b3..9aba654 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -46,6 +46,10 @@ #include <QtCore/qurl.h> #include <QtGui/qevent.h> +#ifdef Q_OS_SYMBIAN +#include <f32file.h> +#endif + QT_BEGIN_NAMESPACE // @@ -174,8 +178,12 @@ public: : url(url) { } + ~QFileOpenEventPrivate(); QUrl url; +#ifdef Q_OS_SYMBIAN + RFile file; +#endif }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qeventdispatcher_glib_qpa.cpp b/src/gui/kernel/qeventdispatcher_glib_qpa.cpp new file mode 100644 index 0000000..a9b1018 --- /dev/null +++ b/src/gui/kernel/qeventdispatcher_glib_qpa.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeventdispatcher_glib_qpa_p.h" + +#include "qapplication.h" + +#include "qplatformdefs.h" +#include "qapplication.h" + +#include <glib.h> +#include "qapplication_p.h" + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +struct GUserEventSource +{ + GSource source; + QPAEventDispatcherGlib *q; +}; + +static gboolean userEventSourcePrepare(GSource *s, gint *timeout) +{ + Q_UNUSED(s) + Q_UNUSED(timeout) + + return QWindowSystemInterfacePrivate::windowSystemEventsQueued() > 0; +} + +static gboolean userEventSourceCheck(GSource *source) +{ + return userEventSourcePrepare(source, 0); +} + +static gboolean userEventSourceDispatch(GSource *s, GSourceFunc, gpointer) +{ + GUserEventSource * source = reinterpret_cast<GUserEventSource *>(s); + + QWindowSystemInterfacePrivate::WindowSystemEvent * event; + while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) { + event = QWindowSystemInterfacePrivate::getWindowSystemEvent(); + if (!event) + break; + + // send through event filter + if (source->q->filterEvent(event)) { + delete event; + continue; + } + QApplicationPrivate::processWindowSystemEvent(event); + delete event; + } + + return true; +} + + +static GSourceFuncs userEventSourceFuncs = { + userEventSourcePrepare, + userEventSourceCheck, + userEventSourceDispatch, + NULL, + NULL, + NULL +}; + +QPAEventDispatcherGlibPrivate::QPAEventDispatcherGlibPrivate(GMainContext *context) + : QEventDispatcherGlibPrivate(context) +{ + userEventSource = reinterpret_cast<GUserEventSource *>(g_source_new(&userEventSourceFuncs, + sizeof(GUserEventSource))); + userEventSource->q = 0; + g_source_set_can_recurse(&userEventSource->source, true); + g_source_attach(&userEventSource->source, mainContext); +} + + +QPAEventDispatcherGlib::QPAEventDispatcherGlib(QObject *parent) + : QEventDispatcherGlib(*new QPAEventDispatcherGlibPrivate, parent) +{ + Q_D(QPAEventDispatcherGlib); + d->userEventSource->q = this; +} + +QPAEventDispatcherGlib::~QPAEventDispatcherGlib() +{ + Q_D(QPAEventDispatcherGlib); + + g_source_destroy(&d->userEventSource->source); + g_source_unref(&d->userEventSource->source); + d->userEventSource = 0; +} + +bool QPAEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + static bool init = false; + if (!init) { + if (QApplicationPrivate::platformIntegration()->createEventLoopIntegration()) { + qWarning("Eventloop integration is not supported by the glib event dispatcher"); + qWarning("Use the UNIX event dispatcher by defining environment variable QT_NO_GLIB=1"); + } + init = true; + } + return QEventDispatcherGlib::processEvents(flags); +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qeventdispatcher_glib_qpa_p.h b/src/gui/kernel/qeventdispatcher_glib_qpa_p.h new file mode 100644 index 0000000..8a8559d --- /dev/null +++ b/src/gui/kernel/qeventdispatcher_glib_qpa_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_GLIB_QPA_P_H +#define QEVENTDISPATCHER_GLIB_QPA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qeventdispatcher_glib_p.h> + +typedef struct _GMainContext GMainContext; + +QT_BEGIN_NAMESPACE +class QPAEventDispatcherGlibPrivate; + +class QPAEventDispatcherGlib : public QEventDispatcherGlib +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPAEventDispatcherGlib) + +public: + explicit QPAEventDispatcherGlib(QObject *parent = 0); + ~QPAEventDispatcherGlib(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); +}; + +struct GUserEventSource; + +class QPAEventDispatcherGlibPrivate : public QEventDispatcherGlibPrivate +{ + Q_DECLARE_PUBLIC(QPAEventDispatcherGlib) +public: + QPAEventDispatcherGlibPrivate(GMainContext *context = 0); + GUserEventSource *userEventSource; +}; + + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_GLIB_QPA_P_H diff --git a/src/gui/kernel/qeventdispatcher_mac.mm b/src/gui/kernel/qeventdispatcher_mac.mm index a808ecb..f90fb5a 100644 --- a/src/gui/kernel/qeventdispatcher_mac.mm +++ b/src/gui/kernel/qeventdispatcher_mac.mm @@ -561,6 +561,7 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) wakeUp(); emit awake(); + bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents; bool retVal = false; forever { if (d->interrupt) @@ -571,7 +572,7 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) NSEvent* event = 0; // First, send all previously excluded input events, if any: - if (!(flags & QEventLoop::ExcludeUserInputEvents)) { + if (!excludeUserEvents) { while (!d->queuedUserInputEvents.isEmpty()) { event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst()); if (!filterEvent(event)) { @@ -586,9 +587,12 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) // application, we should not run or stop NSApplication; This will be // done from the application itself. And if processEvents is called // manually (rather than from a QEventLoop), we cannot enter a tight - // loop and block this call, but instead we need to return after one flush: + // loop and block this call, but instead we need to return after one flush. + // Finally, if we are to exclude user input events, we cannot call [NSApp run] + // as we then loose control over which events gets dispatched: const bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning]; - const bool canExec_Qt = flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec; + const bool canExec_Qt = !excludeUserEvents && + (flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec) ; if (canExec_Qt && canExec_3rdParty) { // We can use exec-mode, meaning that we can stay in a tight loop until @@ -616,22 +620,46 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) // Instead we will process all current pending events and return. d->ensureNSAppInitialized(); if (NSModalSession session = d->currentModalSession()) { - if (flags & QEventLoop::WaitForMoreEvents) - qt_mac_waitForMoreModalSessionEvents(); - NSInteger status = [NSApp runModalSession:session]; - if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) { - // INVARIANT: Someone called [NSApp stopModal:] from outside the event - // dispatcher (e.g to stop a native dialog). But that call wrongly stopped - // 'session' as well. As a result, we need to restart all internal sessions: - d->temporarilyStopAllModalSessions(); - } - retVal = true; - } else do { - event = [NSApp nextEventMatchingMask:NSAnyEventMask + // INVARIANT: a modal window is executing. + if (!excludeUserEvents) { + // Since we can dispatch all kinds of events, we choose + // to use cocoa's native way of running modal sessions: + if (flags & QEventLoop::WaitForMoreEvents) + qt_mac_waitForMoreModalSessionEvents(); + NSInteger status = [NSApp runModalSession:session]; + if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) { + // INVARIANT: Someone called [NSApp stopModal:] from outside the event + // dispatcher (e.g to stop a native dialog). But that call wrongly stopped + // 'session' as well. As a result, we need to restart all internal sessions: + d->temporarilyStopAllModalSessions(); + } + retVal = true; + } else do { + // Dispatch all non-user events (but que non-user events up for later). In + // this case, we need more control over which events gets dispatched, and + // cannot use [NSApp runModalSession:session]: + event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil - inMode:NSDefaultRunLoopMode + inMode:NSModalPanelRunLoopMode dequeue: YES]; + if (event) { + if (IsMouseOrKeyEvent(event)) { + [event retain]; + d->queuedUserInputEvents.append(event); + continue; + } + if (!filterEvent(event) && qt_mac_send_event(flags, event, 0)) + retVal = true; + } + } while (!d->interrupt && event != nil); + } else do { + // INVARIANT: No modal window is executing. + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:nil + inMode:NSDefaultRunLoopMode + dequeue: YES]; + if (event) { if (flags & QEventLoop::ExcludeUserInputEvents) { if (IsMouseOrKeyEvent(event)) { @@ -645,6 +673,11 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) } } while (!d->interrupt && event != nil); + // Be sure to flush the Qt posted events when not using exec mode + // (exec mode will always do this call from the event loop source): + if (!d->interrupt) + QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + // Since the window that holds modality might have changed while processing // events, we we need to interrupt when we return back the previous process // event recursion to ensure that we spin the correct modal session. diff --git a/src/gui/kernel/qeventdispatcher_qpa.cpp b/src/gui/kernel/qeventdispatcher_qpa.cpp new file mode 100644 index 0000000..3f2e8b2 --- /dev/null +++ b/src/gui/kernel/qeventdispatcher_qpa.cpp @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qapplication.h" +#include "qeventdispatcher_qpa_p.h" +#include "private/qeventdispatcher_unix_p.h" +#include "qapplication_p.h" +#include "qplatformeventloopintegration_qpa.h" + +#include <QWindowSystemInterface> +#include <QtCore/QElapsedTimer> +#include <QtCore/QAtomicInt> +#include <QtCore/QSemaphore> + +#include <QtCore/QDebug> + +#include <errno.h> + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +class Rendezvous +{ +public: + void checkpoint() + { + if (state.testAndSetOrdered(0,1)) { + semaphore.acquire(); + } else if (state.testAndSetAcquire(1,0)) { + semaphore.release(); + } else { + qWarning("Barrier internal error"); + } + } +private: + QSemaphore semaphore; + QAtomicInt state; +}; + +class SelectWorker : public QThread +{ +public: + SelectWorker(QEventDispatcherQPAPrivate *eventDispatcherPrivate) + : QThread(), + m_edPrivate(eventDispatcherPrivate), + m_retVal(0) + { + } + + void setSelectValues(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds) + { + m_nfds = nfds; + m_readfds = readfds; + m_writefds = writefds; + m_exceptfds = exceptfds; + + + } + + int retVal() const { + return m_retVal; + } + +protected: + void run(); + +private: + QEventDispatcherQPAPrivate *m_edPrivate; + int m_retVal; + + int m_nfds; + fd_set *m_readfds, *m_writefds, *m_exceptfds; +}; + +class QEventDispatcherQPAPrivate : public QEventDispatcherUNIXPrivate +{ + Q_DECLARE_PUBLIC(QEventDispatcherQPA) +public: + QEventDispatcherQPAPrivate() + : eventLoopIntegration(0), + barrierBeforeBlocking(0), + barrierReturnValue(0), + selectReturnMutex(0), + selectWorkerNeedsSync(true), + selectWorkerHasResult(false), + m_integrationInitialised(false), + m_hasIntegration(false), + m_isEventLoopIntegrationRunning(false) + { + + } + + ~QEventDispatcherQPAPrivate() + { + delete selectWorker; + delete eventLoopIntegration; + delete barrierBeforeBlocking; + delete barrierReturnValue; + delete selectReturnMutex; + } + + bool hasIntegration() const + { + if (!m_integrationInitialised) { + QEventDispatcherQPAPrivate *that = const_cast<QEventDispatcherQPAPrivate *>(this); + if (qApp && (qApp->thread() == QThread::currentThread())) { // guiThread + if (QApplicationPrivate::platformIntegration()) { + that->eventLoopIntegration = QApplicationPrivate::platformIntegration()->createEventLoopIntegration(); + if (that->eventLoopIntegration) { + that->selectWorker = new SelectWorker(that); + that->barrierBeforeBlocking = new Rendezvous; + that->barrierReturnValue = new Rendezvous; + that->selectReturnMutex = new QMutex; + that->selectWorker->start(); + that->m_hasIntegration = true; + if (!QElapsedTimer::isMonotonic()) + qWarning("Having eventloop integration without monotonic timers can lead to undefined behaviour"); + } + } + } + that->m_integrationInitialised = true; + } + return m_hasIntegration; + } + + bool isEventLoopIntegrationRunning() const + { + return m_isEventLoopIntegrationRunning; + } + + void runEventLoopIntegration() + { + if (qApp && (qApp->thread() == QThread::currentThread())) { + m_isEventLoopIntegrationRunning = true; + eventLoopIntegration->startEventLoop(); + } + } + + QPlatformEventLoopIntegration *eventLoopIntegration; + Rendezvous *barrierBeforeBlocking; + Rendezvous *barrierReturnValue; + + QMutex *selectReturnMutex; + bool selectWorkerNeedsSync; + bool selectWorkerHasResult; + + SelectWorker *selectWorker; +private: + bool m_integrationInitialised; + bool m_hasIntegration; + bool m_isEventLoopIntegrationRunning; +}; + +QEventDispatcherQPA::QEventDispatcherQPA(QObject *parent) + : QEventDispatcherUNIX(*new QEventDispatcherQPAPrivate, parent) +{ } + +QEventDispatcherQPA::~QEventDispatcherQPA() +{ } + +bool QEventDispatcherQPA::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_D(QEventDispatcherQPA); + + if (d->hasIntegration()) { + if (!d->isEventLoopIntegrationRunning()) { + d->runEventLoopIntegration(); + } + if (d->threadData->quitNow) { + d->eventLoopIntegration->quitEventLoop(); + return false; + } + } + + int nevents = 0; + + // handle gui and posted events + d->interrupt = false; + QApplication::sendPostedEvents(); + + while (!d->interrupt) { // also flushes output buffer ###can be optimized + QWindowSystemInterfacePrivate::WindowSystemEvent *event; + if (!(flags & QEventLoop::ExcludeUserInputEvents) + && QWindowSystemInterfacePrivate::windowSystemEventsQueued() > 0) { + // process a pending user input event + event = QWindowSystemInterfacePrivate::getWindowSystemEvent(); + if (!event) + break; + } else { + break; + } + + if (filterEvent(event)) { + delete event; + continue; + } + nevents++; + + QApplicationPrivate::processWindowSystemEvent(event); + delete event; + } + + if (!d->interrupt) { + if (QEventDispatcherUNIX::processEvents(flags)) { + QEventDispatcherUNIX::processEvents(flags); + return true; + } + } + return (nevents > 0); +} + +bool QEventDispatcherQPA::hasPendingEvents() +{ + extern uint qGlobalPostedEventsCount(); // from qapplication.cpp + return qGlobalPostedEventsCount() || QWindowSystemInterfacePrivate::windowSystemEventsQueued(); +} + +void QEventDispatcherQPA::registerSocketNotifier(QSocketNotifier *notifier) +{ + Q_D(QEventDispatcherQPA); + QEventDispatcherUNIX::registerSocketNotifier(notifier); + if (d->hasIntegration()) + wakeUp(); + +} + +void QEventDispatcherQPA::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + Q_D(QEventDispatcherQPA); + QEventDispatcherUNIX::unregisterSocketNotifier(notifier); + if (d->hasIntegration()) + wakeUp(); +} + +void QEventDispatcherQPA::flush() +{ + if(qApp) + qApp->sendPostedEvents(); +} + +int QEventDispatcherQPA::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timeval *timeout) +{ + Q_D(QEventDispatcherQPA); + int retVal = 0; + if (d->hasIntegration()) { + qint64 timeoutmsec = 1000; // wait a second if we don't have timers + if (timeout) + timeoutmsec = timeout->tv_sec * 1000 + (timeout->tv_usec/1000); + d->selectReturnMutex->lock(); + if (d->selectWorkerNeedsSync) { + if (d->selectWorkerHasResult) { + retVal = d->selectWorker->retVal(); + d->selectWorkerHasResult = false; + + d->selectReturnMutex->unlock(); + d->barrierReturnValue->checkpoint(); + d->eventLoopIntegration->setNextTimerEvent(0); + return retVal; + } else { + d->selectWorkerNeedsSync = false; + d->selectWorker->setSelectValues(nfds,readfds, writefds, exceptfds); + d->barrierBeforeBlocking->checkpoint(); + } + } + d->selectReturnMutex->unlock(); + d->eventLoopIntegration->setNextTimerEvent(timeoutmsec); + retVal = 0; //is 0 if select has not returned + } else { + retVal = QEventDispatcherUNIX::select(nfds, readfds, writefds, exceptfds, timeout); + } + return retVal; +} + + +void SelectWorker::run() +{ + + while(true) { + m_retVal = 0; + m_edPrivate->barrierBeforeBlocking->checkpoint(); // wait for mainthread + int tmpRet = qt_safe_select(m_nfds,m_readfds,m_writefds,m_exceptfds,0); + m_edPrivate->selectReturnMutex->lock(); + m_edPrivate->eventLoopIntegration->qtNeedsToProcessEvents(); + + m_edPrivate->selectWorkerNeedsSync = true; + m_edPrivate->selectWorkerHasResult = true; + m_retVal = tmpRet; + + m_edPrivate->selectReturnMutex->unlock(); + m_edPrivate->barrierReturnValue->checkpoint(); + } +} +QT_END_NAMESPACE diff --git a/src/gui/kernel/qeventdispatcher_qpa_p.h b/src/gui/kernel/qeventdispatcher_qpa_p.h new file mode 100644 index 0000000..c145e38 --- /dev/null +++ b/src/gui/kernel/qeventdispatcher_qpa_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_QPA_P_H +#define QEVENTDISPATCHER_QPA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qeventdispatcher_unix_p.h" + +QT_BEGIN_NAMESPACE + +class QEventDispatcherQPAPrivate; + +class QEventDispatcherQPA : public QEventDispatcherUNIX +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QEventDispatcherQPA) + +public: + explicit QEventDispatcherQPA(QObject *parent = 0); + ~QEventDispatcherQPA(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + bool hasPendingEvents(); + + void registerSocketNotifier(QSocketNotifier *notifier); + void unregisterSocketNotifier(QSocketNotifier *notifier); + + void flush(); + +protected: + int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timeval *timeout); +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_QPA_P_H diff --git a/src/gui/kernel/qeventdispatcher_s60.cpp b/src/gui/kernel/qeventdispatcher_s60.cpp index 3f20c08..d5a2761 100644 --- a/src/gui/kernel/qeventdispatcher_s60.cpp +++ b/src/gui/kernel/qeventdispatcher_s60.cpp @@ -45,62 +45,6 @@ QT_BEGIN_NAMESPACE -QtEikonEnv::QtEikonEnv() - : m_lastIterationCount(0) - , m_savedStatusCode(KRequestPending) - , m_hasAlreadyRun(false) -{ -} - -QtEikonEnv::~QtEikonEnv() -{ -} - -void QtEikonEnv::RunL() -{ - QEventDispatcherS60 *dispatcher = qobject_cast<QEventDispatcherS60 *>(QAbstractEventDispatcher::instance()); - if (!dispatcher) { - CEikonEnv::RunL(); - return; - } - - if (m_lastIterationCount != dispatcher->iterationCount()) { - m_hasAlreadyRun = false; - m_lastIterationCount = dispatcher->iterationCount(); - } - - if (m_hasAlreadyRun) { - // Fool the active scheduler into believing we are still waiting for events. - // The window server thinks we are not, however. - m_savedStatusCode = iStatus.Int(); - iStatus = KRequestPending; - SetActive(); - dispatcher->queueDeferredActiveObjectsCompletion(); - } else { - m_hasAlreadyRun = true; - CEikonEnv::RunL(); - } -} - -void QtEikonEnv::DoCancel() -{ - complete(); - - CEikonEnv::DoCancel(); -} - -void QtEikonEnv::complete() -{ - if (m_hasAlreadyRun) { - if (m_savedStatusCode != KRequestPending) { - TRequestStatus *status = &iStatus; - QEventDispatcherSymbian::RequestComplete(status, m_savedStatusCode); - m_savedStatusCode = KRequestPending; - } - m_hasAlreadyRun = false; - } -} - QEventDispatcherS60::QEventDispatcherS60(QObject *parent) : QEventDispatcherSymbian(parent), m_noInputEvents(false) @@ -183,14 +127,4 @@ void QEventDispatcherS60::removeInputEventsForWidget(QObject *object) } } -// reimpl -void QEventDispatcherS60::reactivateDeferredActiveObjects() -{ - if (S60->qtOwnsS60Environment) { - static_cast<QtEikonEnv *>(CCoeEnv::Static())->complete(); - } - - QEventDispatcherSymbian::reactivateDeferredActiveObjects(); -} - QT_END_NAMESPACE diff --git a/src/gui/kernel/qeventdispatcher_s60_p.h b/src/gui/kernel/qeventdispatcher_s60_p.h index 49ec568..c7cf2b2 100644 --- a/src/gui/kernel/qeventdispatcher_s60_p.h +++ b/src/gui/kernel/qeventdispatcher_s60_p.h @@ -62,31 +62,6 @@ QT_BEGIN_NAMESPACE class QEventDispatcherS60; -class QtEikonEnv : public CEikonEnv -{ -public: - QtEikonEnv(); - ~QtEikonEnv(); - - // from CActive. - void RunL(); - void DoCancel(); - - void complete(); - -private: - // Workaround for a BC break from S60 3.2 -> 5.0, where the CEikonEnv override was removed. - // To avoid linking to that when we build against 3.2, define an empty body here. - // Reserved_*() have been verified to be empty in the S60 code. - void Reserved_1() {} - void Reserved_2() {} - -private: - int m_lastIterationCount; - TInt m_savedStatusCode; - bool m_hasAlreadyRun; -}; - class Q_GUI_EXPORT QEventDispatcherS60 : public QEventDispatcherSymbian { Q_OBJECT @@ -102,8 +77,6 @@ public: void saveInputEvent(QSymbianControl *control, QWidget *widget, QInputEvent *event); - void reactivateDeferredActiveObjects(); - private: bool sendDeferredInputEvents(); diff --git a/src/gui/kernel/qgenericplugin_qpa.cpp b/src/gui/kernel/qgenericplugin_qpa.cpp new file mode 100644 index 0000000..dae512d --- /dev/null +++ b/src/gui/kernel/qgenericplugin_qpa.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgenericplugin_qpa.h" + +#ifndef QT_NO_LIBRARY + +QT_BEGIN_NAMESPACE + +/*! + \class QGenericPlugin + \ingroup plugins + \ingroup qpa + \since 4.8 + + \brief The QGenericPlugin class is an abstract base class for + window-system related plugins in Qt QPA. + + Note that this class is only available in Qt QPA. + + A mouse plugin can be created by subclassing + QGenericPlugin and reimplementing the pure virtual keys() and + create() functions. By exporting the derived class using the + Q_EXPORT_PLUGIN2() macro, The default implementation of the + QGenericPluginFactory class will automatically detect the plugin and + load the driver into the server application at run-time. See \l + {How to Create Qt Plugins} for details. + + \sa QGenericPluginFactory +*/ + +/*! + \fn QStringList QGenericPlugin::keys() const + + Implement this function to return the list of valid keys, i.e. the + drivers supported by this plugin. + + \sa create() +*/ + +/*! + Constructs a plugin with the given \a parent. + + Note that this constructor is invoked automatically by the + Q_EXPORT_PLUGIN2() macro, so there is no need for calling it + explicitly. +*/ +QGenericPlugin::QGenericPlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys the plugin. + + Note that Qt destroys a plugin automatically when it is no longer + used, so there is no need for calling the destructor explicitly. +*/ +QGenericPlugin::~QGenericPlugin() +{ +} + +/*! + \fn QObject* QGenericPlugin::create(const QString &key, const QString& specification) + + Implement this function to create a driver matching the type + specified by the given \a key and \a specification parameters. Note that + keys are case-insensitive. + + \sa keys() +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_LIBRARY diff --git a/src/gui/kernel/qgenericplugin_qpa.h b/src/gui/kernel/qgenericplugin_qpa.h new file mode 100644 index 0000000..c4dd5a9 --- /dev/null +++ b/src/gui/kernel/qgenericplugin_qpa.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGENERICPLUGIN_QPA_H +#define QGENERICPLUGIN_QPA_H + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_LIBRARY + +//class QGenericObject; ????? + + struct Q_GUI_EXPORT QGenericPluginFactoryInterface : public QFactoryInterface +{ + virtual QObject* create(const QString &name, const QString &spec) = 0; +}; + +#define QGenericPluginFactoryInterface_iid "com.trolltech.Qt.QGenericPluginFactoryInterface" +Q_DECLARE_INTERFACE(QGenericPluginFactoryInterface, QGenericPluginFactoryInterface_iid) + +class Q_GUI_EXPORT QGenericPlugin : public QObject, public QGenericPluginFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QGenericPluginFactoryInterface:QFactoryInterface) +public: + explicit QGenericPlugin(QObject *parent = 0); + ~QGenericPlugin(); + + virtual QStringList keys() const = 0; + virtual QObject* create(const QString& name, const QString &spec) = 0; +}; + +#endif // QT_NO_LIBRARY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGENERICPLUGIN_QPA_H diff --git a/src/gui/kernel/qgenericpluginfactory_qpa.cpp b/src/gui/kernel/qgenericpluginfactory_qpa.cpp new file mode 100644 index 0000000..86e146a --- /dev/null +++ b/src/gui/kernel/qgenericpluginfactory_qpa.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgenericpluginfactory_qpa.h" + +#include "qapplication.h" +#include "private/qfactoryloader_p.h" +#include "qgenericplugin_qpa.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QGenericPluginFactoryInterface_iid, + QLatin1String("/generic"), Qt::CaseInsensitive)) + +#endif //QT_NO_LIBRARY +#endif //QT_MAKEDLL + +/*! + \class QGenericPluginFactory + \ingroup qpa + \since 4.8 + + \brief The QGenericPluginFactory class creates window-system + related plugin drivers in Qt QPA. + + Note that this class is only available in Qt QPA. + + \sa QGenericPlugin +*/ + +/*! + Creates the driver specified by \a key, using the given \a specification. + + Note that the keys are case-insensitive. + + \sa keys() +*/ +QObject *QGenericPluginFactory::create(const QString& key, const QString &specification) +{ + QString driver = key.toLower(); + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + if (QGenericPluginFactoryInterface *factory = qobject_cast<QGenericPluginFactoryInterface*>(loader()->instance(driver))) + return factory->create(driver, specification); +#endif +#endif + return 0; +} + +/*! + Returns the list of valid keys, i.e. the available mouse drivers. + + \sa create() +*/ +QStringList QGenericPluginFactory::keys() +{ + QStringList list; + +#if !defined(Q_OS_WIN32) || defined(QT_MAKEDLL) +#ifndef QT_NO_LIBRARY + QStringList plugins = loader()->keys(); + for (int i = 0; i < plugins.size(); ++i) { + if (!list.contains(plugins.at(i))) + list += plugins.at(i); + } +#endif //QT_NO_LIBRARY +#endif //QT_MAKEDLL + return list; +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qgenericpluginfactory_qpa.h b/src/gui/kernel/qgenericpluginfactory_qpa.h new file mode 100644 index 0000000..2359de9 --- /dev/null +++ b/src/gui/kernel/qgenericpluginfactory_qpa.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGENERICPLUGINFACTORY_QPA_H +#define QGENERICPLUGINFACTORY_QPA_H + +#include <QtCore/qstringlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QString; +class QObject; + +class Q_GUI_EXPORT QGenericPluginFactory +{ +public: + static QStringList keys(); + static QObject *create(const QString&, const QString &); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGENERICPLUGINFACTORY_QPA_H diff --git a/src/gui/kernel/qgesturemanager.cpp b/src/gui/kernel/qgesturemanager.cpp index b87da08..868387d 100644 --- a/src/gui/kernel/qgesturemanager.cpp +++ b/src/gui/kernel/qgesturemanager.cpp @@ -243,52 +243,53 @@ bool QGestureManager::filterEventThroughContexts(const QMultiMap<QObject *, // TODO: sort contexts by the gesture type and check if one of the contexts // is already active. - bool ret = false; + bool consumeEventHint = false; // filter the event through recognizers typedef QMultiMap<QObject *, Qt::GestureType>::const_iterator ContextIterator; - for (ContextIterator cit = contexts.begin(), ce = contexts.end(); cit != ce; ++cit) { - Qt::GestureType gestureType = cit.value(); + ContextIterator contextEnd = contexts.end(); + for (ContextIterator context = contexts.begin(); context != contextEnd; ++context) { + Qt::GestureType gestureType = context.value(); QMap<Qt::GestureType, QGestureRecognizer *>::const_iterator - rit = m_recognizers.lowerBound(gestureType), - re = m_recognizers.upperBound(gestureType); - for (; rit != re; ++rit) { - QGestureRecognizer *recognizer = rit.value(); - QObject *target = cit.key(); + typeToRecognizerIterator = m_recognizers.lowerBound(gestureType), + typeToRecognizerEnd = m_recognizers.upperBound(gestureType); + for (; typeToRecognizerIterator != typeToRecognizerEnd; ++typeToRecognizerIterator) { + QGestureRecognizer *recognizer = typeToRecognizerIterator.value(); + QObject *target = context.key(); QGesture *state = getState(target, recognizer, gestureType); if (!state) continue; - QGestureRecognizer::Result result = recognizer->recognize(state, target, event); - QGestureRecognizer::Result type = result & QGestureRecognizer::ResultState_Mask; - result &= QGestureRecognizer::ResultHint_Mask; - if (type == QGestureRecognizer::TriggerGesture) { + QGestureRecognizer::Result recognizerResult = recognizer->recognize(state, target, event); + QGestureRecognizer::Result recognizerState = recognizerResult & QGestureRecognizer::ResultState_Mask; + QGestureRecognizer::Result resultHint = recognizerResult & QGestureRecognizer::ResultHint_Mask; + if (recognizerState == QGestureRecognizer::TriggerGesture) { DEBUG() << "QGestureManager:Recognizer: gesture triggered: " << state; triggeredGestures << state; - } else if (type == QGestureRecognizer::FinishGesture) { + } else if (recognizerState == QGestureRecognizer::FinishGesture) { DEBUG() << "QGestureManager:Recognizer: gesture finished: " << state; finishedGestures << state; - } else if (type == QGestureRecognizer::MayBeGesture) { + } else if (recognizerState == QGestureRecognizer::MayBeGesture) { DEBUG() << "QGestureManager:Recognizer: maybe gesture: " << state; newMaybeGestures << state; - } else if (type == QGestureRecognizer::CancelGesture) { + } else if (recognizerState == QGestureRecognizer::CancelGesture) { DEBUG() << "QGestureManager:Recognizer: not gesture: " << state; notGestures << state; - } else if (type == QGestureRecognizer::Ignore) { + } else if (recognizerState == QGestureRecognizer::Ignore) { DEBUG() << "QGestureManager:Recognizer: ignored the event: " << state; } else { DEBUG() << "QGestureManager:Recognizer: hm, lets assume the recognizer" << "ignored the event: " << state; } - if (result & QGestureRecognizer::ConsumeEventHint) { + if (resultHint & QGestureRecognizer::ConsumeEventHint) { DEBUG() << "QGestureManager: we were asked to consume the event: " << state; - ret = true; + consumeEventHint = true; } } } if (triggeredGestures.isEmpty() && finishedGestures.isEmpty() && newMaybeGestures.isEmpty() && notGestures.isEmpty()) - return ret; + return consumeEventHint; QSet<QGesture *> startedGestures = triggeredGestures - m_activeGestures; triggeredGestures &= m_activeGestures; @@ -390,7 +391,7 @@ bool QGestureManager::filterEventThroughContexts(const QMultiMap<QObject *, qDeleteAll(m_gesturesToDelete); m_gesturesToDelete.clear(); - return ret; + return consumeEventHint; } // Cancel all gestures of children of the widget that original is associated with @@ -565,7 +566,6 @@ void QGestureManager::getGestureTargets(const QSet<QGesture*> &gestures, = w->d_func()->gestureContext.find(type); if (it != w->d_func()->gestureContext.end()) { // i.e. 'w' listens to gesture 'type' - Qt::GestureFlags flags = it.value(); if (!(it.value() & Qt::DontStartGestureOnChildren) && w != widget) { // conflicting gesture! (*conflicts)[widget].append(gestures[widget]); diff --git a/src/gui/kernel/qguifunctions_wince.cpp b/src/gui/kernel/qguifunctions_wince.cpp index 934a22d..78dc469 100644 --- a/src/gui/kernel/qguifunctions_wince.cpp +++ b/src/gui/kernel/qguifunctions_wince.cpp @@ -112,6 +112,9 @@ struct AygSIPINFO #ifndef SPI_GETSIPINFO #define SPI_GETSIPINFO 225 #endif +#ifndef SPI_GETPLATFORMTYPE +#define SPI_GETPLATFORMTYPE 257 +#endif typedef BOOL (*AygInitDialog)(AygSHINITDLGINFO*); typedef BOOL (*AygFullScreen)(HWND, DWORD); @@ -129,8 +132,6 @@ static void resolveAygLibs() if (!aygResolved) { aygResolved = true; QLibrary ayglib(QLatin1String("aygshell")); - if (!ayglib.load()) - return; ptrAygInitDialog = (AygInitDialog) ayglib.resolve("SHInitDialog"); ptrAygFullScreen = (AygFullScreen) ayglib.resolve("SHFullScreen"); ptrAygSHSipInfo = (AygSHSipInfo) ayglib.resolve("SHSipInfo"); diff --git a/src/gui/kernel/qguiplatformplugin.cpp b/src/gui/kernel/qguiplatformplugin.cpp index 10cf103..708859d 100644 --- a/src/gui/kernel/qguiplatformplugin.cpp +++ b/src/gui/kernel/qguiplatformplugin.cpp @@ -152,7 +152,7 @@ QString QGuiPlatformPlugin::styleName() return QLatin1String("Windows"); // default style for Symbian without S60 #elif defined(Q_WS_X11) && defined(Q_OS_IRIX) return QLatin1String("SGI"); // default style for X11 on IRIX -#elif defined(Q_WS_QWS) +#elif defined(Q_WS_QWS) || defined(Q_WS_QPA) return QLatin1String("Plastique"); // default style for X11 and small devices #elif defined(Q_WS_MAC) return QLatin1String("Macintosh"); // default style for all Mac's diff --git a/src/gui/kernel/qkeymapper_p.h b/src/gui/kernel/qkeymapper_p.h index ed61e24..1d624f9 100644 --- a/src/gui/kernel/qkeymapper_p.h +++ b/src/gui/kernel/qkeymapper_p.h @@ -213,6 +213,9 @@ public: int mapS60ScanCodesToQt(TUint s60key); int mapQtToS60Key(int qtKey); int mapQtToS60ScanCodes(int qtKey); + int mapS60RemConIdToS60Key(int s60RemConId); + int mapS60RemConIdToS60ScanCodes(int s60RemConId); + void updateInputLanguage(); #endif }; diff --git a/src/gui/kernel/qkeymapper_s60.cpp b/src/gui/kernel/qkeymapper_s60.cpp index e4ecdd8..2a7083e 100644 --- a/src/gui/kernel/qkeymapper_s60.cpp +++ b/src/gui/kernel/qkeymapper_s60.cpp @@ -40,7 +40,11 @@ ****************************************************************************/ #include "private/qkeymapper_p.h" +#include <private/qcore_symbian_p.h> #include <e32keys.h> +#include <e32cmn.h> +#include <centralrepository.h> +#include <biditext.h> QT_BEGIN_NAMESPACE @@ -65,8 +69,17 @@ void QKeyMapperPrivate::clearMappings() QString QKeyMapperPrivate::translateKeyEvent(int keySym, Qt::KeyboardModifiers /* modifiers */) { - if (keySym >= Qt::Key_Escape) - return QString(); + if (keySym >= Qt::Key_Escape) { + switch (keySym) { + case Qt::Key_Tab: + return QString(QChar('\t')); + case Qt::Key_Return: // fall through + case Qt::Key_Enter: + return QString(QChar('\r')); + default: + return QString(); + } + } // Symbian doesn't actually use modifiers, but gives us the character code directly. @@ -74,97 +87,104 @@ QString QKeyMapperPrivate::translateKeyEvent(int keySym, Qt::KeyboardModifiers / } #include <e32keys.h> +#include <remconcoreapi.h> struct KeyMapping{ TKeyCode s60KeyCode; TStdScanCode s60ScanCode; + TRemConCoreApiOperationId s60RemConId; Qt::Key qtKey; }; using namespace Qt; +// key mapping table in the format of +// {S60 key code, S60 scan code, S60 RemCon operation Id, Qt key code} static const KeyMapping keyMapping[] = { - {EKeyBackspace, EStdKeyBackspace, Key_Backspace}, - {EKeyTab, EStdKeyTab, Key_Tab}, - {EKeyEnter, EStdKeyEnter, Key_Enter}, - {EKeyEscape, EStdKeyEscape, Key_Escape}, - {EKeySpace, EStdKeySpace, Key_Space}, - {EKeyDelete, EStdKeyDelete, Key_Delete}, - {EKeyPrintScreen, EStdKeyPrintScreen, Key_SysReq}, - {EKeyPause, EStdKeyPause, Key_Pause}, - {EKeyHome, EStdKeyHome, Key_Home}, - {EKeyEnd, EStdKeyEnd, Key_End}, - {EKeyPageUp, EStdKeyPageUp, Key_PageUp}, - {EKeyPageDown, EStdKeyPageDown, Key_PageDown}, - {EKeyInsert, EStdKeyInsert, Key_Insert}, - {EKeyLeftArrow, EStdKeyLeftArrow, Key_Left}, - {EKeyRightArrow, EStdKeyRightArrow, Key_Right}, - {EKeyUpArrow, EStdKeyUpArrow, Key_Up}, - {EKeyDownArrow, EStdKeyDownArrow, Key_Down}, - {EKeyLeftShift, EStdKeyLeftShift, Key_Shift}, - {EKeyRightShift, EStdKeyRightShift, Key_Shift}, - {EKeyLeftAlt, EStdKeyLeftAlt, Key_Alt}, - {EKeyRightAlt, EStdKeyRightAlt, Key_AltGr}, - {EKeyLeftCtrl, EStdKeyLeftCtrl, Key_Control}, - {EKeyRightCtrl, EStdKeyRightCtrl, Key_Control}, - {EKeyLeftFunc, EStdKeyLeftFunc, Key_Super_L}, - {EKeyRightFunc, EStdKeyRightFunc, Key_Super_R}, - {EKeyCapsLock, EStdKeyCapsLock, Key_CapsLock}, - {EKeyNumLock, EStdKeyNumLock, Key_NumLock}, - {EKeyScrollLock, EStdKeyScrollLock, Key_ScrollLock}, - {EKeyF1, EStdKeyF1, Key_F1}, - {EKeyF2, EStdKeyF2, Key_F2}, - {EKeyF3, EStdKeyF3, Key_F3}, - {EKeyF4, EStdKeyF4, Key_F4}, - {EKeyF5, EStdKeyF5, Key_F5}, - {EKeyF6, EStdKeyF6, Key_F6}, - {EKeyF7, EStdKeyF7, Key_F7}, - {EKeyF8, EStdKeyF8, Key_F8}, - {EKeyF9, EStdKeyF9, Key_F9}, - {EKeyF10, EStdKeyF10, Key_F10}, - {EKeyF11, EStdKeyF11, Key_F11}, - {EKeyF12, EStdKeyF12, Key_F12}, - {EKeyF13, EStdKeyF13, Key_F13}, - {EKeyF14, EStdKeyF14, Key_F14}, - {EKeyF15, EStdKeyF15, Key_F15}, - {EKeyF16, EStdKeyF16, Key_F16}, - {EKeyF17, EStdKeyF17, Key_F17}, - {EKeyF18, EStdKeyF18, Key_F18}, - {EKeyF19, EStdKeyF19, Key_F19}, - {EKeyF20, EStdKeyF20, Key_F20}, - {EKeyF21, EStdKeyF21, Key_F21}, - {EKeyF22, EStdKeyF22, Key_F22}, - {EKeyF23, EStdKeyF23, Key_F23}, - {EKeyF24, EStdKeyF24, Key_F24}, - {EKeyOff, EStdKeyOff, Key_PowerOff}, -// {EKeyMenu, EStdKeyMenu, Key_Menu}, // Menu is EKeyApplication0 - {EKeyHelp, EStdKeyHelp, Key_Help}, - {EKeyDial, EStdKeyDial, Key_Call}, - {EKeyIncVolume, EStdKeyIncVolume, Key_VolumeUp}, - {EKeyDecVolume, EStdKeyDecVolume, Key_VolumeDown}, - {EKeyDevice0, EStdKeyDevice0, Key_Context1}, // Found by manual testing. - {EKeyDevice1, EStdKeyDevice1, Key_Context2}, // Found by manual testing. - {EKeyDevice3, EStdKeyDevice3, Key_Select}, - {EKeyDevice7, EStdKeyDevice7, Key_Camera}, - {EKeyApplication0, EStdKeyApplication0, Key_Menu}, // Found by manual testing. - {EKeyApplication1, EStdKeyApplication1, Key_Launch1}, // Found by manual testing. - {EKeyApplication2, EStdKeyApplication2, Key_MediaPlay}, // Found by manual testing. - {EKeyApplication3, EStdKeyApplication3, Key_MediaStop}, // Found by manual testing. - {EKeyApplication4, EStdKeyApplication4, Key_MediaNext}, // Found by manual testing. - {EKeyApplication5, EStdKeyApplication5, Key_MediaPrevious}, // Found by manual testing. - {EKeyApplication6, EStdKeyApplication6, Key_Launch6}, - {EKeyApplication7, EStdKeyApplication7, Key_Launch7}, - {EKeyApplication8, EStdKeyApplication8, Key_Launch8}, - {EKeyApplication9, EStdKeyApplication9, Key_Launch9}, - {EKeyApplicationA, EStdKeyApplicationA, Key_LaunchA}, - {EKeyApplicationB, EStdKeyApplicationB, Key_LaunchB}, - {EKeyApplicationC, EStdKeyApplicationC, Key_LaunchC}, - {EKeyApplicationD, EStdKeyApplicationD, Key_LaunchD}, - {EKeyApplicationE, EStdKeyApplicationE, Key_LaunchE}, - {EKeyApplicationF, EStdKeyApplicationF, Key_LaunchF}, - {EKeyApplication19, EStdKeyApplication19, Key_CameraFocus}, - {EKeyYes, EStdKeyYes, Key_Yes}, - {EKeyNo, EStdKeyNo, Key_No}, - {TKeyCode(0), TStdScanCode(0), Qt::Key(0)} + {EKeyBackspace, EStdKeyBackspace, ENop, Key_Backspace}, + {EKeyTab, EStdKeyTab, ENop, Key_Tab}, + {EKeyEnter, EStdKeyEnter, ERemConCoreApiEnter, Key_Enter}, + {EKeyEscape, EStdKeyEscape, ENop, Key_Escape}, + {EKeySpace, EStdKeySpace, ENop, Key_Space}, + {EKeyDelete, EStdKeyDelete, ENop, Key_Delete}, + {EKeyPrintScreen, EStdKeyPrintScreen, ENop, Key_SysReq}, + {EKeyPause, EStdKeyPause, ENop, Key_Pause}, + {EKeyHome, EStdKeyHome, ENop, Key_Home}, + {EKeyEnd, EStdKeyEnd, ENop, Key_End}, + {EKeyPageUp, EStdKeyPageUp, ERemConCoreApiPageUp, Key_PageUp}, + {EKeyPageDown, EStdKeyPageDown, ERemConCoreApiPageDown, Key_PageDown}, + {EKeyInsert, EStdKeyInsert, ENop, Key_Insert}, + {EKeyLeftArrow, EStdKeyLeftArrow, ERemConCoreApiLeft, Key_Left}, + {EKeyRightArrow, EStdKeyRightArrow, ERemConCoreApiRight, Key_Right}, + {EKeyUpArrow, EStdKeyUpArrow, ERemConCoreApiUp, Key_Up}, + {EKeyDownArrow, EStdKeyDownArrow, ERemConCoreApiDown, Key_Down}, + {EKeyLeftShift, EStdKeyLeftShift, ENop, Key_Shift}, + {EKeyRightShift, EStdKeyRightShift, ENop, Key_Shift}, + {EKeyLeftAlt, EStdKeyLeftAlt, ENop, Key_Alt}, + {EKeyRightAlt, EStdKeyRightAlt, ENop, Key_AltGr}, + {EKeyLeftCtrl, EStdKeyLeftCtrl, ENop, Key_Control}, + {EKeyRightCtrl, EStdKeyRightCtrl, ENop, Key_Control}, + {EKeyLeftFunc, EStdKeyLeftFunc, ENop, Key_Super_L}, + {EKeyRightFunc, EStdKeyRightFunc, ENop, Key_Super_R}, + {EKeyCapsLock, EStdKeyCapsLock, ENop, Key_CapsLock}, + {EKeyNumLock, EStdKeyNumLock, ENop, Key_NumLock}, + {EKeyScrollLock, EStdKeyScrollLock, ENop, Key_ScrollLock}, + {EKeyF1, EStdKeyF1, ERemConCoreApiF1, Key_F1}, + {EKeyF2, EStdKeyF2, ERemConCoreApiF2, Key_F2}, + {EKeyF3, EStdKeyF3, ERemConCoreApiF3, Key_F3}, + {EKeyF4, EStdKeyF4, ERemConCoreApiF4, Key_F4}, + {EKeyF5, EStdKeyF5, ERemConCoreApiF5, Key_F5}, + {EKeyF6, EStdKeyF6, ENop, Key_F6}, + {EKeyF7, EStdKeyF7, ENop, Key_F7}, + {EKeyF8, EStdKeyF8, ENop, Key_F8}, + {EKeyF9, EStdKeyF9, ENop, Key_F9}, + {EKeyF10, EStdKeyF10, ENop, Key_F10}, + {EKeyF11, EStdKeyF11, ENop, Key_F11}, + {EKeyF12, EStdKeyF12, ENop, Key_F12}, + {EKeyF13, EStdKeyF13, ENop, Key_F13}, + {EKeyF14, EStdKeyF14, ENop, Key_F14}, + {EKeyF15, EStdKeyF15, ENop, Key_F15}, + {EKeyF16, EStdKeyF16, ENop, Key_F16}, + {EKeyF17, EStdKeyF17, ENop, Key_F17}, + {EKeyF18, EStdKeyF18, ENop, Key_F18}, + {EKeyF19, EStdKeyF19, ENop, Key_F19}, + {EKeyF20, EStdKeyF20, ENop, Key_F20}, + {EKeyF21, EStdKeyF21, ENop, Key_F21}, + {EKeyF22, EStdKeyF22, ENop, Key_F22}, + {EKeyF23, EStdKeyF23, ENop, Key_F23}, + {EKeyF24, EStdKeyF24, ENop, Key_F24}, + {EKeyOff, EStdKeyOff, ENop, Key_PowerOff}, +// {EKeyMenu, EStdKeyMenu, ENop, Key_Menu}, // Menu is EKeyApplication0 + {EKeyHelp, EStdKeyHelp, ERemConCoreApiHelp, Key_Help}, + {EKeyDial, EStdKeyDial, ENop, Key_Call}, + {EKeyIncVolume, EStdKeyIncVolume, ERemConCoreApiVolumeUp, Key_VolumeUp}, + {EKeyDecVolume, EStdKeyDecVolume, ERemConCoreApiVolumeDown, Key_VolumeDown}, + {EKeyDevice0, EStdKeyDevice0, ENop, Key_Context1}, // Found by manual testing. + {EKeyDevice1, EStdKeyDevice1, ENop, Key_Context2}, // Found by manual testing. + {EKeyDevice3, EStdKeyDevice3, ERemConCoreApiSelect, Key_Select}, + {EKeyDevice7, EStdKeyDevice7, ENop, Key_Camera}, + {EKeyApplication0, EStdKeyApplication0, ENop, Key_Menu}, // Found by manual testing. + {EKeyApplication1, EStdKeyApplication1, ENop, Key_Launch1}, // Found by manual testing. + {EKeyApplication2, EStdKeyApplication2, ERemConCoreApiPlay, Key_MediaPlay}, // Found by manual testing. + {EKeyApplication3, EStdKeyApplication3, ERemConCoreApiStop, Key_MediaStop}, // Found by manual testing. + {EKeyApplication4, EStdKeyApplication4, ERemConCoreApiForward, Key_MediaNext}, // Found by manual testing. + {EKeyApplication5, EStdKeyApplication5, ERemConCoreApiBackward, Key_MediaPrevious}, // Found by manual testing. + {EKeyApplication6, EStdKeyApplication6, ENop, Key_Launch6}, + {EKeyApplication7, EStdKeyApplication7, ENop, Key_Launch7}, + {EKeyApplication8, EStdKeyApplication8, ENop, Key_Launch8}, + {EKeyApplication9, EStdKeyApplication9, ENop, Key_Launch9}, + {EKeyApplicationA, EStdKeyApplicationA, ENop, Key_LaunchA}, + {EKeyApplicationB, EStdKeyApplicationB, ENop, Key_LaunchB}, + {EKeyApplicationC, EStdKeyApplicationC, ENop, Key_LaunchC}, + {EKeyApplicationD, EStdKeyApplicationD, ENop, Key_LaunchD}, + {EKeyApplicationE, EStdKeyApplicationE, ENop, Key_LaunchE}, + {EKeyApplicationF, EStdKeyApplicationF, ENop, Key_LaunchF}, + {EKeyApplication19, EStdKeyApplication19, ENop, Key_CameraFocus}, + {EKeyYes, EStdKeyYes, ENop, Key_Yes}, + {EKeyNo, EStdKeyNo, ENop, Key_No}, + {EKeyDevice20, EStdKeyDevice20, ERemConCoreApiPausePlayFunction, Key_MediaTogglePlayPause}, + {EKeyDevice21, EStdKeyDevice21, ERemConCoreApiRewind, Key_AudioRewind}, + {EKeyDevice22, EStdKeyDevice22, ERemConCoreApiFastForward, Key_AudioForward}, + {TKeyCode(0), TStdScanCode(0), ENop, Qt::Key(0)} }; int QKeyMapperPrivate::mapS60KeyToQt(TUint s60key) @@ -214,4 +234,56 @@ int QKeyMapperPrivate::mapQtToS60ScanCodes(int qtKey) } return res; } + +int QKeyMapperPrivate::mapS60RemConIdToS60Key(int s60RemConId) +{ + int res = KErrUnknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].s60RemConId == s60RemConId) { + res = keyMapping[i].s60KeyCode; + break; + } + } + return res; +} + +int QKeyMapperPrivate::mapS60RemConIdToS60ScanCodes(int s60RemConId) +{ + int res = KErrUnknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].s60RemConId == s60RemConId) { + res = keyMapping[i].s60ScanCode; + break; + } + } + return res; +} + +void QKeyMapperPrivate::updateInputLanguage() +{ +#ifdef Q_WS_S60 + TInt err; + CRepository *repo; + const TUid KCRUidAknFep = TUid::Uid(0x101F876D); + const TUint32 KAknFepInputTxtLang = 0x00000005; + TRAP(err, repo = CRepository::NewL(KCRUidAknFep)); + if (err != KErrNone) + return; + + TInt symbianLang; + err = repo->Get(KAknFepInputTxtLang, symbianLang); + delete repo; + if (err != KErrNone) + return; + + QString qtLang = QString::fromAscii(qt_symbianLocaleName(symbianLang)); + keyboardInputLocale = QLocale(qtLang); + keyboardInputDirection = (TBidiText::ScriptDirectionality(TLanguage(symbianLang)) == TBidiText::ERightToLeft) + ? Qt::RightToLeft : Qt::LeftToRight; +#else + keyboardInputLocale = QLocale(); + keyboardInputDirection = Qt::LeftToRight; +#endif +} + QT_END_NAMESPACE diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp index 8635bf2..469973f 100644 --- a/src/gui/kernel/qkeysequence.cpp +++ b/src/gui/kernel/qkeysequence.cpp @@ -76,7 +76,7 @@ static const MacSpecialKey entries[NumEntries] = { { Qt::Key_Backtab, 0x21E4 }, { Qt::Key_Backspace, 0x232B }, { Qt::Key_Return, 0x21B5 }, - { Qt::Key_Enter, 0x21B5 }, + { Qt::Key_Enter, 0x2324 }, { Qt::Key_Delete, 0x2326 }, { Qt::Key_Home, 0x2196 }, { Qt::Key_End, 0x2198 }, @@ -240,7 +240,7 @@ void Q_GUI_EXPORT qt_set_sequence_auto_mnemonic(bool b) { qt_sequence_no_mnemoni \row \i Paste \i Ctrl+V, Shift+Ins \i Ctrl+V \i Ctrl+V, F18, Shift+Ins \i Ctrl+V, F18, Shift+Ins \i Ctrl+V \row \i Preferences \i \i Ctrl+, \i \i \i (none) \row \i Undo \i Ctrl+Z, Alt+Backspace \i Ctrl+Z \i Ctrl+Z, F14 \i Ctrl+Z, F14 \i Ctrl+Z - \row \i Redo \i Ctrl+Y, Shift+Ctrl+Z, Alt+Shift+Backspace \i Ctrl+Shift+Z, Ctrl+Y \i Ctrl+Shift+Z \i Ctrl+Shift+Z \i (none) + \row \i Redo \i Ctrl+Y, Shift+Ctrl+Z, Alt+Shift+Backspace \i Ctrl+Shift+Z \i Ctrl+Shift+Z \i Ctrl+Shift+Z \i (none) \row \i Back \i Alt+Left, Backspace \i Ctrl+[ \i Alt+Left \i Alt+Left \i (none) \row \i Forward \i Alt+Right, Shift+Backspace \i Ctrl+] \i Alt+Right \i Alt+Right \i (none) \row \i Refresh \i F5 \i F5 \i F5 \i Ctrl+R, F5 \i (none) @@ -718,7 +718,6 @@ const QKeyBinding QKeySequencePrivate::keyBindings[] = { {QKeySequence::Close, 1, Qt::CTRL | Qt::Key_W, QApplicationPrivate::KB_Mac}, {QKeySequence::Cut, 1, Qt::CTRL | Qt::Key_X, QApplicationPrivate::KB_All}, {QKeySequence::Redo, 1, Qt::CTRL | Qt::Key_Y, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_S60}, - {QKeySequence::Redo, 0, Qt::CTRL | Qt::Key_Y, QApplicationPrivate::KB_Mac},//different priority from above {QKeySequence::Undo, 1, Qt::CTRL | Qt::Key_Z, QApplicationPrivate::KB_All}, {QKeySequence::Back, 1, Qt::CTRL | Qt::Key_BracketLeft, QApplicationPrivate::KB_Mac}, {QKeySequence::Forward, 1, Qt::CTRL | Qt::Key_BracketRight, QApplicationPrivate::KB_Mac}, @@ -747,7 +746,7 @@ const QKeyBinding QKeySequencePrivate::keyBindings[] = { {QKeySequence::AddTab, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_N, QApplicationPrivate::KB_KDE}, {QKeySequence::SaveAs, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_S, QApplicationPrivate::KB_Gnome | QApplicationPrivate::KB_Mac}, {QKeySequence::Redo, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Z, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11 | QApplicationPrivate::KB_S60}, - {QKeySequence::Redo, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Z, QApplicationPrivate::KB_Mac}, //different priority from above + {QKeySequence::Redo, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Z, QApplicationPrivate::KB_Mac}, {QKeySequence::PreviousChild, 1, Qt::CTRL | Qt::SHIFT | Qt::Key_Backtab, QApplicationPrivate::KB_Win | QApplicationPrivate::KB_X11}, {QKeySequence::PreviousChild, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Backtab, QApplicationPrivate::KB_Mac },//different priority from above {QKeySequence::Paste, 0, Qt::CTRL | Qt::SHIFT | Qt::Key_Insert, QApplicationPrivate::KB_X11}, @@ -1382,11 +1381,11 @@ QString QKeySequencePrivate::encodeString(int key, QKeySequence::SequenceFormat QString p; if (key && key < Qt::Key_Escape && key != Qt::Key_Space) { - if (key < 0x10000) { - p = QChar(key & 0xffff).toUpper(); + if (!QChar::requiresSurrogates(key)) { + p = QChar(ushort(key)).toUpper(); } else { - p = QChar((key-0x10000)/0x400+0xd800); - p += QChar((key-0x10000)%400+0xdc00); + p += QChar(QChar::highSurrogate(key)); + p += QChar(QChar::lowSurrogate(key)); } } else if (key >= Qt::Key_F1 && key <= Qt::Key_F35) { p = nativeText ? QShortcut::tr("F%1").arg(key - Qt::Key_F1 + 1) @@ -1419,11 +1418,11 @@ NonSymbol: // Or else characters like Qt::Key_aring may not get displayed // (Really depends on you locale) if (!keyname[i].name) { - if (key < 0x10000) { - p = QChar(key & 0xffff).toUpper(); + if (!QChar::requiresSurrogates(key)) { + p = QChar(ushort(key)).toUpper(); } else { - p = QChar((key-0x10000)/0x400+0xd800); - p += QChar((key-0x10000)%400+0xdc00); + p += QChar(QChar::highSurrogate(key)); + p += QChar(QChar::lowSurrogate(key)); } } } @@ -1522,6 +1521,14 @@ QKeySequence &QKeySequence::operator=(const QKeySequence &other) } /*! + \fn void QKeySequence::swap(QKeySequence &other) + \since 4.8 + + Swaps key sequence \a other with this key sequence. This operation is very + fast and never fails. +*/ + +/*! \fn bool QKeySequence::operator!=(const QKeySequence &other) const Returns true if this key sequence is not equal to the \a other diff --git a/src/gui/kernel/qkeysequence.h b/src/gui/kernel/qkeysequence.h index 610f002..22bdfc4 100644 --- a/src/gui/kernel/qkeysequence.h +++ b/src/gui/kernel/qkeysequence.h @@ -179,6 +179,11 @@ public: operator int() const; int operator[](uint i) const; QKeySequence &operator=(const QKeySequence &other); +#ifdef Q_COMPILER_RVALUE_REFS + inline QKeySequence &operator=(QKeySequence &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QKeySequence &other) { qSwap(d, other.d); } bool operator==(const QKeySequence &other) const; inline bool operator!= (const QKeySequence &other) const { return !(*this == other); } diff --git a/src/gui/kernel/qlayoutitem.cpp b/src/gui/kernel/qlayoutitem.cpp index 5c8c244..e385745 100644 --- a/src/gui/kernel/qlayoutitem.cpp +++ b/src/gui/kernel/qlayoutitem.cpp @@ -516,9 +516,7 @@ bool QWidgetItem::hasHeightForWidth() const { if (isEmpty()) return false; - if (wid->layout()) - return wid->layout()->hasHeightForWidth(); - return wid->sizePolicy().hasHeightForWidth(); + return wid->d_func()->hasHeightForWidth(); } /*! diff --git a/src/gui/kernel/qmacdefines_mac.h b/src/gui/kernel/qmacdefines_mac.h index 380027c..0236087 100644 --- a/src/gui/kernel/qmacdefines_mac.h +++ b/src/gui/kernel/qmacdefines_mac.h @@ -94,12 +94,6 @@ Yes, it is an informative comment ;-) #include <QtCore/qglobal.h> -#undef OLD_DEBUG -#ifdef DEBUG -# define OLD_DEBUG DEBUG -# undef DEBUG -#endif -#define DEBUG 0 #ifdef qDebug # define old_qDebug qDebug # undef qDebug @@ -179,12 +173,6 @@ typedef AERecord AppleEvent; #undef check #endif -#undef DEBUG -#ifdef OLD_DEBUG -# define DEBUG OLD_DEBUG -# undef OLD_DEBUG -#endif - #ifdef old_qDebug # undef qDebug # define qDebug QT_NO_QDEBUG_MACRO diff --git a/src/gui/kernel/qmime_mac.cpp b/src/gui/kernel/qmime_mac.cpp index a1df34e..8b47d8e 100644 --- a/src/gui/kernel/qmime_mac.cpp +++ b/src/gui/kernel/qmime_mac.cpp @@ -119,10 +119,6 @@ const QStringList& qEnabledDraggedTypes() *****************************************************************************/ //#define DEBUG_MIME_MAPS -//functions -extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp -extern void qt_mac_from_pascal_string(QString, Str255, TextEncoding encoding=0, int len=-1); //qglobal.cpp - ScrapFlavorType qt_mac_mime_type = 'CUTE'; CFStringRef qt_mac_mime_typeUTI = CFSTR("com.pasteboard.trolltech.marker"); diff --git a/src/gui/kernel/qpalette.h b/src/gui/kernel/qpalette.h index d9e2cd3..671a56c 100644 --- a/src/gui/kernel/qpalette.h +++ b/src/gui/kernel/qpalette.h @@ -78,6 +78,14 @@ public: QPalette(const QPalette &palette); ~QPalette(); QPalette &operator=(const QPalette &palette); +#ifdef Q_COMPILER_RVALUE_REFS + inline QPalette &operator=(QPalette &&other) + { + resolve_mask = other.resolve_mask; + current_group = other.current_group; + qSwap(d, other.d); return *this; + } +#endif operator QVariant() const; // Do not change the order, the serialization format depends on it diff --git a/src/gui/kernel/qplatformclipboard_qpa.cpp b/src/gui/kernel/qplatformclipboard_qpa.cpp new file mode 100644 index 0000000..f8e4b62 --- /dev/null +++ b/src/gui/kernel/qplatformclipboard_qpa.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qplatformclipboard_qpa.h" + +#ifndef QT_NO_CLIPBOARD + +#include <QtGui/private/qapplication_p.h> + +QT_BEGIN_NAMESPACE + +class QClipboardData +{ +public: + QClipboardData(); + ~QClipboardData(); + + void setSource(QMimeData* s) + { + if (s == src) + return; + delete src; + src = s; + } + QMimeData* source() + { return src; } + +private: + QMimeData* src; +}; + +QClipboardData::QClipboardData() +{ + src = 0; +} + +QClipboardData::~QClipboardData() +{ + delete src; +} + +Q_GLOBAL_STATIC(QClipboardData,q_clipboardData); + +QPlatformClipboard::~QPlatformClipboard() +{ + +} + +QMimeData *QPlatformClipboard::mimeData(QClipboard::Mode mode) +{ + //we know its clipboard + Q_UNUSED(mode); + return q_clipboardData()->source(); +} + +void QPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) +{ + //we know its clipboard + Q_UNUSED(mode); + q_clipboardData()->setSource(data); +} + +bool QPlatformClipboard::supportsMode(QClipboard::Mode mode) const +{ + return mode == QClipboard::Clipboard; +} + +void QPlatformClipboard::emitChanged(QClipboard::Mode mode) +{ + QApplication::clipboard()->emitChanged(mode); +} + +QT_END_NAMESPACE + +#endif //QT_NO_CLIPBOARD diff --git a/src/gui/kernel/qplatformclipboard_qpa.h b/src/gui/kernel/qplatformclipboard_qpa.h new file mode 100644 index 0000000..6a40fbc --- /dev/null +++ b/src/gui/kernel/qplatformclipboard_qpa.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMCLIPBOARD_QPA_H +#define QPLATFORMCLIPBOARD_QPA_H + +#include <qplatformdefs.h> + +#ifndef QT_NO_CLIPBOARD + +#include <QtGui/QClipboard> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QPlatformClipboard +{ +public: + virtual ~QPlatformClipboard(); + + virtual QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard); + virtual void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard); + virtual bool supportsMode(QClipboard::Mode mode) const; + void emitChanged(QClipboard::Mode mode); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_CLIPBOARD + +#endif //QPLATFORMCLIPBOARD_QPA_H diff --git a/src/gui/kernel/qplatformcursor_qpa.cpp b/src/gui/kernel/qplatformcursor_qpa.cpp new file mode 100644 index 0000000..0426226 --- /dev/null +++ b/src/gui/kernel/qplatformcursor_qpa.cpp @@ -0,0 +1,639 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qplatformcursor_qpa.h" + +#include <QWidget> +#include <QPainter> +#include <QBitmap> +#include <QApplication> + +#include <QDebug> + +QT_BEGIN_NAMESPACE + +QList <QWeakPointer<QPlatformCursor> > QPlatformCursorPrivate::instances; + +/*! + \class QPlatformCursor + \since 4.8 + + \brief The QPlatformCursor class provides information about + pointer device events (movement, buttons), and requests to change + the currently displayed cursor. + + Note that QPlatformCursor does not include any graphics for + display. An application that sets a QCursor may provide its own + graphics. + + \sa QPlatformCursorImage +*/ + +/*! + \fn virtual void QPlatformCursor::pointerEvent(const QMouseEvent & event) + + This method is called by Qt whenever a QMouseEvent is generated by the + underlying pointer input. \a event is a reference to the QMouseEvent in + question. A default do-nothing implementation is provided. +*/ + +/*! + \fn virtual void QPlatformCursor::changeCursor(QCursor * widgetCursor, QWidget * widget) + + \brief This method is called by Qt whenever the cursor graphic should be changed. + + Implementation of this method is mandatory for a subclass of QPlatformCursor. + + \a widgetCursor is a pointer to the QCursor that should be displayed. + + \a widget is a pointer to the widget currently displayed at QCursor::pos(). Note + that this may be 0 if the current position is not occupied by a displayed widget. + + \sa QCursor::pos() +*/ + +/*! + \fn QPlatformCursor::QPlatformCursor(QPlatformScreen *screen) + + Constructs a QPlatformCursor for the given \a screen. +*/ +QPlatformCursor::QPlatformCursor(QPlatformScreen *scr ) + : screen(scr) +{ + QPlatformCursorPrivate::instances.append(this); +} + +// End of display and pointer event handling code +// Beginning of built-in cursor graphics +// from src/gui/embedded/QGraphicsSystemCursorImage_qws.cpp + +/*! + \class QPlatformCursorImage + \since 4.8 + + \brief The QPlatformCursorImage class provides a set of graphics + intended to be used as cursors. + + \sa QPlatformCursor +*/ + +static QPlatformCursorImage *systemCursorTable[Qt::LastCursor+1]; +static bool systemCursorTableInit = false; + +// 16 x 16 +static const uchar cur_arrow_bits[] = { + 0x07, 0x00, 0x39, 0x00, 0xc1, 0x01, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x08, + 0x04, 0x04, 0x04, 0x02, 0x04, 0x04, 0x88, 0x08, 0x48, 0x11, 0x28, 0x22, + 0x10, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 0x00 }; +static const uchar mcur_arrow_bits[] = { + 0x07, 0x00, 0x3f, 0x00, 0xff, 0x01, 0xfe, 0x0f, 0xfe, 0x1f, 0xfe, 0x0f, + 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x07, 0xf8, 0x0f, 0x78, 0x1f, 0x38, 0x3e, + 0x10, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00 }; + +static const unsigned char cur_up_arrow_bits[] = { + 0x80, 0x00, 0x40, 0x01, 0x40, 0x01, 0x20, 0x02, 0x20, 0x02, 0x10, 0x04, + 0x10, 0x04, 0x08, 0x08, 0x78, 0x0f, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, + 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0xc0, 0x01}; +static const unsigned char mcur_up_arrow_bits[] = { + 0x80, 0x00, 0xc0, 0x01, 0xc0, 0x01, 0xe0, 0x03, 0xe0, 0x03, 0xf0, 0x07, + 0xf0, 0x07, 0xf8, 0x0f, 0xf8, 0x0f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01}; + +static const unsigned char cur_cross_bits[] = { + 0xc0, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, + 0x7f, 0x7f, 0x01, 0x40, 0x7f, 0x7f, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, + 0x40, 0x01, 0x40, 0x01, 0xc0, 0x01, 0x00, 0x00}; +static const unsigned char mcur_cross_bits[] = { + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00}; + +static const uchar cur_ibeam_bits[] = { + 0x00, 0x00, 0xe0, 0x03, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, + 0x80, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_ibeam_bits[] = { + 0xf0, 0x07, 0xf0, 0x07, 0xf0, 0x07, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, + 0xf0, 0x07, 0xf0, 0x07, 0xf0, 0x07, 0x00, 0x00 }; + +static const uchar cur_ver_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f, + 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00 }; +static const uchar mcur_ver_bits[] = { + 0x00, 0x00, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, + 0xfc, 0x7f, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xfc, 0x7f, 0xf8, 0x3f, + 0xf0, 0x1f, 0xe0, 0x0f, 0xc0, 0x07, 0x80, 0x03 }; + +static const uchar cur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x30, 0x18, + 0x38, 0x38, 0xfc, 0x7f, 0xfc, 0x7f, 0x38, 0x38, 0x30, 0x18, 0x20, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_hor_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x60, 0x0c, 0x70, 0x1c, 0x78, 0x3c, + 0xfc, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfc, 0x7f, 0x78, 0x3c, + 0x70, 0x1c, 0x60, 0x0c, 0x40, 0x04, 0x00, 0x00 }; +static const uchar cur_bdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e, + 0x00, 0x37, 0x88, 0x23, 0xd8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0xf8, 0x00, + 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_bdiag_bits[] = { + 0x00, 0x00, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7f, 0x00, 0x7e, 0x04, 0x7f, + 0x8c, 0x7f, 0xdc, 0x77, 0xfc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01, + 0xfc, 0x03, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00 }; +static const uchar cur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, + 0xf8, 0x00, 0xd8, 0x01, 0x88, 0x23, 0x00, 0x37, 0x00, 0x3e, 0x00, 0x3c, + 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00 }; +static const uchar mcur_fdiag_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00, + 0xfc, 0x41, 0xfc, 0x63, 0xdc, 0x77, 0x8c, 0x7f, 0x04, 0x7f, 0x00, 0x7e, + 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x7f, 0x00, 0x00 }; + +// 20 x 20 +static const uchar forbidden_bits[] = { + 0x00,0x00,0x00,0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xf0,0x00,0x38,0xc0,0x01, + 0x7c,0x80,0x03,0xec,0x00,0x03,0xce,0x01,0x07,0x86,0x03,0x06,0x06,0x07,0x06, + 0x06,0x0e,0x06,0x06,0x1c,0x06,0x0e,0x38,0x07,0x0c,0x70,0x03,0x1c,0xe0,0x03, + 0x38,0xc0,0x01,0xf0,0xe0,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00,0x00,0x00,0x00 }; + +static const uchar forbiddenm_bits[] = { + 0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xff,0x00,0xf8,0xff,0x01,0xfc,0xf0,0x03, + 0xfe,0xc0,0x07,0xfe,0x81,0x07,0xff,0x83,0x0f,0xcf,0x07,0x0f,0x8f,0x0f,0x0f, + 0x0f,0x1f,0x0f,0x0f,0x3e,0x0f,0x1f,0xfc,0x0f,0x1e,0xf8,0x07,0x3e,0xf0,0x07, + 0xfc,0xe0,0x03,0xf8,0xff,0x01,0xf0,0xff,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00}; + +// 32 x 32 +static const uchar wait_data_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0x04, 0x40, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x08, 0x20, 0x00, + 0x00, 0x08, 0x20, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x08, 0x20, 0x00, + 0x00, 0x50, 0x15, 0x00, 0x00, 0xa0, 0x0a, 0x00, 0x00, 0x40, 0x05, 0x00, + 0x00, 0x80, 0x02, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x20, 0x08, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x21, 0x00, 0x00, 0x88, 0x22, 0x00, + 0x00, 0x48, 0x25, 0x00, 0x00, 0xa8, 0x2a, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0x04, 0x40, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar wait_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xf8, 0x3f, 0x00, + 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, + 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xc0, 0x07, 0x00, + 0x00, 0x80, 0x03, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xe0, 0x0f, 0x00, + 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, + 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x7f, 0x00, + 0x00, 0xfc, 0x7f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uchar hsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03, + 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, + 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar hsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00, + 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07, + 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00, + 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, + 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar vsplit_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar vsplitm_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar phand_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, + 0x7e, 0x04, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x70, 0x08, 0x00, 0x00, + 0x08, 0x08, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, 0x08, 0x22, 0x00, 0x00, + 0x30, 0x41, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x40, 0x12, 0x00, 0x00, + 0x80, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar phandm_bits[] = { + 0xfe, 0x01, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, + 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, + 0xfc, 0x1f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, + 0xf8, 0xff, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00, + 0xc0, 0x1f, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uchar size_all_data_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x80, 0x81, 0xc0, 0x00, + 0xc0, 0xff, 0xff, 0x01, 0x80, 0x81, 0xc0, 0x00, 0x00, 0x81, 0x40, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const uchar size_all_mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc2, 0x21, 0x00, + 0x00, 0xc3, 0x61, 0x00, 0x80, 0xc3, 0xe1, 0x00, 0xc0, 0xff, 0xff, 0x01, + 0xe0, 0xff, 0xff, 0x03, 0xc0, 0xff, 0xff, 0x01, 0x80, 0xc3, 0xe1, 0x00, + 0x00, 0xc3, 0x61, 0x00, 0x00, 0xc2, 0x21, 0x00, 0x00, 0xc0, 0x01, 0x00, + 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const uchar whatsthis_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0xf0, 0x07, 0x00, + 0x09, 0x18, 0x0e, 0x00, 0x11, 0x1c, 0x0e, 0x00, 0x21, 0x1c, 0x0e, 0x00, + 0x41, 0x1c, 0x0e, 0x00, 0x81, 0x1c, 0x0e, 0x00, 0x01, 0x01, 0x07, 0x00, + 0x01, 0x82, 0x03, 0x00, 0xc1, 0xc7, 0x01, 0x00, 0x49, 0xc0, 0x01, 0x00, + 0x95, 0xc0, 0x01, 0x00, 0x93, 0xc0, 0x01, 0x00, 0x21, 0x01, 0x00, 0x00, + 0x20, 0xc1, 0x01, 0x00, 0x40, 0xc2, 0x01, 0x00, 0x40, 0x02, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; +static const uchar whatsthism_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x07, 0x00, 0x07, 0xf8, 0x0f, 0x00, + 0x0f, 0xfc, 0x1f, 0x00, 0x1f, 0x3e, 0x1f, 0x00, 0x3f, 0x3e, 0x1f, 0x00, + 0x7f, 0x3e, 0x1f, 0x00, 0xff, 0x3e, 0x1f, 0x00, 0xff, 0x9d, 0x0f, 0x00, + 0xff, 0xc3, 0x07, 0x00, 0xff, 0xe7, 0x03, 0x00, 0x7f, 0xe0, 0x03, 0x00, + 0xf7, 0xe0, 0x03, 0x00, 0xf3, 0xe0, 0x03, 0x00, 0xe1, 0xe1, 0x03, 0x00, + 0xe0, 0xe1, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + +static const uchar busy_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x41, 0xe0, 0xff, 0x00, 0x81, 0x20, 0x80, 0x00, 0x01, 0xe1, 0xff, 0x00, + 0x01, 0x42, 0x40, 0x00, 0xc1, 0x47, 0x40, 0x00, 0x49, 0x40, 0x55, 0x00, + 0x95, 0x80, 0x2a, 0x00, 0x93, 0x00, 0x15, 0x00, 0x21, 0x01, 0x0a, 0x00, + 0x20, 0x01, 0x11, 0x00, 0x40, 0x82, 0x20, 0x00, 0x40, 0x42, 0x44, 0x00, + 0x80, 0x41, 0x4a, 0x00, 0x00, 0x40, 0x55, 0x00, 0x00, 0xe0, 0xff, 0x00, + 0x00, 0x20, 0x80, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const uchar busym_bits[] = { + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x7f, 0xe0, 0xff, 0x00, 0xff, 0xe0, 0xff, 0x00, 0xff, 0xe1, 0xff, 0x00, + 0xff, 0xc3, 0x7f, 0x00, 0xff, 0xc7, 0x7f, 0x00, 0x7f, 0xc0, 0x7f, 0x00, + 0xf7, 0x80, 0x3f, 0x00, 0xf3, 0x00, 0x1f, 0x00, 0xe1, 0x01, 0x0e, 0x00, + 0xe0, 0x01, 0x1f, 0x00, 0xc0, 0x83, 0x3f, 0x00, 0xc0, 0xc3, 0x7f, 0x00, + 0x80, 0xc1, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0xe0, 0xff, 0x00, + 0x00, 0xe0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// 16 x 16 +static const uchar openhand_bits[] = { + 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92, + 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20, + 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00}; +static const uchar openhandm_bits[] = { + 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff, + 0xfe,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f, + 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00}; +static const uchar closedhand_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50, + 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10, + 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00}; +static const uchar closedhandm_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f, + 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f, + 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00}; + +void QPlatformCursorImage::createSystemCursor(int id) +{ + if (!systemCursorTableInit) { + for (int i = 0; i <= Qt::LastCursor; i++) + systemCursorTable[i] = 0; + systemCursorTableInit = true; + } + switch (id) { + // 16x16 cursors + case Qt::ArrowCursor: + systemCursorTable[Qt::ArrowCursor] = + new QPlatformCursorImage(cur_arrow_bits, mcur_arrow_bits, 16, 16, 0, 0); + break; + + case Qt::UpArrowCursor: + systemCursorTable[Qt::UpArrowCursor] = + new QPlatformCursorImage(cur_up_arrow_bits, mcur_up_arrow_bits, 16, 16, 7, 0); + break; + + case Qt::CrossCursor: + systemCursorTable[Qt::CrossCursor] = + new QPlatformCursorImage(cur_cross_bits, mcur_cross_bits, 16, 16, 7, 7); + break; + + case Qt::IBeamCursor: + systemCursorTable[Qt::IBeamCursor] = + new QPlatformCursorImage(cur_ibeam_bits, mcur_ibeam_bits, 16, 16, 7, 7); + break; + + case Qt::SizeVerCursor: + systemCursorTable[Qt::SizeVerCursor] = + new QPlatformCursorImage(cur_ver_bits, mcur_ver_bits, 16, 16, 7, 7); + break; + + case Qt::SizeHorCursor: + systemCursorTable[Qt::SizeHorCursor] = + new QPlatformCursorImage(cur_hor_bits, mcur_hor_bits, 16, 16, 7, 7); + break; + + case Qt::SizeBDiagCursor: + systemCursorTable[Qt::SizeBDiagCursor] = + new QPlatformCursorImage(cur_bdiag_bits, mcur_bdiag_bits, 16, 16, 7, 7); + break; + + case Qt::SizeFDiagCursor: + systemCursorTable[Qt::SizeFDiagCursor] = + new QPlatformCursorImage(cur_fdiag_bits, mcur_fdiag_bits, 16, 16, 7, 7); + break; + + case Qt::BlankCursor: + systemCursorTable[Qt::BlankCursor] = + new QPlatformCursorImage(0, 0, 0, 0, 0, 0); + break; + + // 20x20 cursors + case Qt::ForbiddenCursor: + systemCursorTable[Qt::ForbiddenCursor] = + new QPlatformCursorImage(forbidden_bits, forbiddenm_bits, 20, 20, 10, 10); + break; + + // 32x32 cursors + case Qt::WaitCursor: + systemCursorTable[Qt::WaitCursor] = + new QPlatformCursorImage(wait_data_bits, wait_mask_bits, 32, 32, 15, 15); + break; + + case Qt::SplitVCursor: + systemCursorTable[Qt::SplitVCursor] = + new QPlatformCursorImage(vsplit_bits, vsplitm_bits, 32, 32, 15, 15); + break; + + case Qt::SplitHCursor: + systemCursorTable[Qt::SplitHCursor] = + new QPlatformCursorImage(hsplit_bits, hsplitm_bits, 32, 32, 15, 15); + break; + + case Qt::SizeAllCursor: + systemCursorTable[Qt::SizeAllCursor] = + new QPlatformCursorImage(size_all_data_bits, size_all_mask_bits, 32, 32, 15, 15); + break; + + case Qt::PointingHandCursor: + systemCursorTable[Qt::PointingHandCursor] = + new QPlatformCursorImage(phand_bits, phandm_bits, 32, 32, 0, 0); + break; + + case Qt::WhatsThisCursor: + systemCursorTable[Qt::WhatsThisCursor] = + new QPlatformCursorImage(whatsthis_bits, whatsthism_bits, 32, 32, 0, 0); + break; + case Qt::BusyCursor: + systemCursorTable[Qt::BusyCursor] = + new QPlatformCursorImage(busy_bits, busym_bits, 32, 32, 0, 0); + break; + + case Qt::OpenHandCursor: + systemCursorTable[Qt::OpenHandCursor] = + new QPlatformCursorImage(openhand_bits, openhandm_bits, 16, 16, 8, 8); + break; + case Qt::ClosedHandCursor: + systemCursorTable[Qt::ClosedHandCursor] = + new QPlatformCursorImage(closedhand_bits, closedhandm_bits, 16, 16, 8, 8); + break; + default: + qWarning("Unknown system cursor %d", id); + } +} + +/*! + \fn void QPlatformCursorImage::set(Qt::CursorShape id) + + \brief Calling this method sets the cursor image to the specified shape + + \a id is one of the defined Qt::CursorShape values. + + If id is invalid, Qt::BitmapCursor, or unknown by the implementation, + Qt::ArrowCursor is used instead. +*/ + +void QPlatformCursorImage::set(Qt::CursorShape id) +{ + QPlatformCursorImage *cursor = 0; + if (id >= 0 && id <= Qt::LastCursor) { + if (!systemCursorTable[id]) + createSystemCursor(id); + cursor = systemCursorTable[id]; + } + + if (cursor == 0) { + if (!systemCursorTable[Qt::ArrowCursor]) + createSystemCursor(Qt::ArrowCursor); + cursor = systemCursorTable[Qt::ArrowCursor]; + } + cursorImage = cursor->cursorImage; + hot = cursor->hot; +} + +/*! + Sets the cursor image to the given \a image, with the hotspot at the + point specified by (\a hx, \a hy). +*/ + +void QPlatformCursorImage::set(const QImage &image, int hx, int hy) +{ + hot.setX(hx); + hot.setY(hy); + cursorImage = image; +} + +/*! + \fn void QPlatformCursorImage::set(const uchar *data, const uchar *mask, int width, int height, int hx, int hy) + + Sets the cursor image to the graphic represented by the combination of + \a data and \a mask, with dimensions given by \a width and \a height and a + hotspot at the point specified by (\a hx, \a hy). + + The image data specified by \a data must be supplied in the format + described by QImage::Format_Indexed8. + + The corresponding mask data specified by \a mask must be supplied in a + character array containing packed 1 bit per pixel format data, with any + padding bits at the end of the array. Bits of value 0 represent transparent + pixels in the image data. +*/ +void QPlatformCursorImage::set(const uchar *data, const uchar *mask, + int width, int height, int hx, int hy) +{ + hot.setX(hx); + hot.setY(hy); + + cursorImage = QImage(width,height, QImage::Format_Indexed8); + + if (!width || !height || !data || !mask || cursorImage.isNull()) + return; + + cursorImage.setNumColors(3); + cursorImage.setColor(0, 0xff000000); + cursorImage.setColor(1, 0xffffffff); + cursorImage.setColor(2, 0x00000000); + + int bytesPerLine = (width + 7) / 8; + int p = 0; + int d, m; + + int x = -1, w = 0; + + uchar *cursor_data = cursorImage.bits(); + int bpl = cursorImage.bytesPerLine(); + for (int i = 0; i < height; i++) + { + for (int j = 0; j < bytesPerLine; j++, data++, mask++) + { + for (int b = 0; b < 8 && j*8+b < width; b++) + { + d = *data & (1 << b); + m = *mask & (1 << b); + if (d && m) p = 0; + else if (!d && m) p = 1; + else p = 2; + cursor_data[j*8+b] = p; + + // calc region + if (x < 0 && m) + x = j*8+b; + else if (x >= 0 && !m) { + x = -1; + w = 0; + } + if (m) + w++; + } + } + if (x >= 0) { + x = -1; + w = 0; + } + cursor_data += bpl; + } + +} + +/*! + \fn QPlatformCursorImage::QPlatformCursorImage(const uchar *data, const uchar *mask, int width, int height, int hotX, int hotY) + + Sets the cursor image to the graphic represented by the combination of + \a data and \a mask, with dimensions given by \a width and \a height and a + hotspot at the point specified by (\a hotX, \a hotY). + + \sa set() +*/ + +/*! + \fn QImage *QPlatformCursorImage::image() + + \brief Return the cursor graphic as a pointer to a QImage +*/ + +/*! + \fn QPoint QPlatformCursorImage::hotspot() + + \brief Return the cursor's hotspot +*/ + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformcursor_qpa.h b/src/gui/kernel/qplatformcursor_qpa.h new file mode 100644 index 0000000..852a369 --- /dev/null +++ b/src/gui/kernel/qplatformcursor_qpa.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenVG module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGRAPHICSSYSTEMCURSOR_H +#define QGRAPHICSSYSTEMCURSOR_H + +#include <QtCore/QList> +#include <QtGui/QImage> +#include <QtGui/QMouseEvent> +#include <QtCore/QWeakPointer> +#include <QtCore/QObject> +#include <QtGui/QPlatformScreen> +#include <QtGui/QCursor> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +// Cursor graphics management +class Q_GUI_EXPORT QPlatformCursorImage { +public: + QPlatformCursorImage(const uchar *data, const uchar *mask, int width, int height, int hotX, int hotY) + { set(data, mask, width, height, hotX, hotY); } + QImage * image() { return &cursorImage; } + QPoint hotspot() { return hot; } + void set(const uchar *data, const uchar *mask, int width, int height, int hotX, int hotY); + void set(const QImage &image, int hx, int hy); + void set(Qt::CursorShape); +private: + static void createSystemCursor(int id); + QImage cursorImage; + QPoint hot; +}; + +class QPlatformCursor; + +class QPlatformCursorPrivate { +public: + static QList<QWeakPointer<QPlatformCursor> > getInstances() { return instances; } + static QList<QWeakPointer<QPlatformCursor> > instances; +}; + +class Q_GUI_EXPORT QPlatformCursor : public QObject { +public: + QPlatformCursor(QPlatformScreen *); + + // input methods + virtual void pointerEvent(const QMouseEvent & event) { Q_UNUSED(event); } + virtual void changeCursor(QCursor * widgetCursor, QWidget * widget) = 0; + +protected: + QPlatformScreen* screen; // Where to request an update + +private: + Q_DECLARE_PRIVATE(QPlatformCursor); + friend void qt_qpa_set_cursor(QWidget * w, bool force); + friend class QApplicationPrivate; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGRAPHICSSYSTEMCURSOR_H diff --git a/src/gui/kernel/qplatformeventloopintegration_qpa.cpp b/src/gui/kernel/qplatformeventloopintegration_qpa.cpp new file mode 100644 index 0000000..a79b03e --- /dev/null +++ b/src/gui/kernel/qplatformeventloopintegration_qpa.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformeventloopintegration_qpa.h" + +#include <QtCore/QCoreApplication> + +#include <QtCore/QDebug> + +class QPlatformEventLoopIntegrationPrivate +{ +public: + QPlatformEventLoopIntegrationPrivate(); + qint64 nextTimerEvent; +}; + +QPlatformEventLoopIntegrationPrivate::QPlatformEventLoopIntegrationPrivate() + : nextTimerEvent(0) +{ +} + +QPlatformEventLoopIntegration::QPlatformEventLoopIntegration() + : d_ptr(new QPlatformEventLoopIntegrationPrivate) + +{ +} + +QPlatformEventLoopIntegration::~QPlatformEventLoopIntegration() +{ +} + +qint64 QPlatformEventLoopIntegration::nextTimerEvent() const +{ + Q_D(const QPlatformEventLoopIntegration); + return d->nextTimerEvent; +} + + +void QPlatformEventLoopIntegration::setNextTimerEvent(qint64 nextTimerEvent) +{ + Q_D(QPlatformEventLoopIntegration); + d->nextTimerEvent = nextTimerEvent; +} + +void QPlatformEventLoopIntegration::processEvents() +{ + QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); +} diff --git a/src/gui/kernel/qplatformeventloopintegration_qpa.h b/src/gui/kernel/qplatformeventloopintegration_qpa.h new file mode 100644 index 0000000..16a7cfd --- /dev/null +++ b/src/gui/kernel/qplatformeventloopintegration_qpa.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMEVENTLOOPINTEGRATION_QPA_H +#define QPLATFORMEVENTLOOPINTEGRATION_QPA_H + +#include <QtCore/qglobal.h> +#include <QtCore/QScopedPointer> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformEventLoopIntegrationPrivate; + +class Q_GUI_EXPORT QPlatformEventLoopIntegration +{ + Q_DECLARE_PRIVATE(QPlatformEventLoopIntegration); +public: + QPlatformEventLoopIntegration(); + virtual ~QPlatformEventLoopIntegration(); + + virtual void startEventLoop() = 0; + virtual void quitEventLoop() = 0; + virtual void qtNeedsToProcessEvents() = 0; + + qint64 nextTimerEvent() const; + void setNextTimerEvent(qint64 nextTimerEvent); + + static void processEvents(); +protected: + QScopedPointer<QPlatformEventLoopIntegrationPrivate> d_ptr; +private: + Q_DISABLE_COPY(QPlatformEventLoopIntegration); + friend class QEventDispatcherQPA; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMEVENTLOOPINTEGRATION_QPA_H diff --git a/src/gui/kernel/qplatformglcontext_qpa.cpp b/src/gui/kernel/qplatformglcontext_qpa.cpp new file mode 100644 index 0000000..5c56efb --- /dev/null +++ b/src/gui/kernel/qplatformglcontext_qpa.cpp @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformglcontext_qpa.h" + +#include <QtCore/QThreadStorage> +#include <QtCore/QThread> + +#include <QDebug> + +class QPlatformGLThreadContext +{ +public: + ~QPlatformGLThreadContext() { + if (context) + context->doneCurrent(); + } + QPlatformGLContext *context; +}; + +static QThreadStorage<QPlatformGLThreadContext *> qplatformgl_context_storage; + +class QPlatformGLContextPrivate +{ +public: + QPlatformGLContextPrivate() + :qGLContextHandle(0) + { + } + + virtual ~QPlatformGLContextPrivate() + { + //do not delete the QGLContext handle here as it is deleted in + //QWidgetPrivate::deleteTLSysExtra() + } + void *qGLContextHandle; + void (*qGLContextDeleteFunction)(void *handle); + static QPlatformGLContext *staticSharedContext; + + static void setCurrentContext(QPlatformGLContext *context); +}; + +QPlatformGLContext *QPlatformGLContextPrivate::staticSharedContext = 0; + +void QPlatformGLContextPrivate::setCurrentContext(QPlatformGLContext *context) +{ + QPlatformGLThreadContext *threadContext = qplatformgl_context_storage.localData(); + if (!threadContext) { + if (!QThread::currentThread()) { + qWarning("No QTLS available. currentContext wont work"); + return; + } + threadContext = new QPlatformGLThreadContext; + qplatformgl_context_storage.setLocalData(threadContext); + } + threadContext->context = context; +} + +/*! + Returns the last context which called makeCurrent. This function is thread aware. +*/ +const QPlatformGLContext* QPlatformGLContext::currentContext() +{ + QPlatformGLThreadContext *threadContext = qplatformgl_context_storage.localData(); + if(threadContext) { + return threadContext->context; + } + return 0; +} + +/*! + All subclasses needs to specify the platformWindow. It can be a null window. +*/ +QPlatformGLContext::QPlatformGLContext() + :d_ptr(new QPlatformGLContextPrivate()) +{ +} + +/*! + If this is the current context for the thread, doneCurrent is called +*/ +QPlatformGLContext::~QPlatformGLContext() +{ + if (QPlatformGLContext::currentContext() == this) { + doneCurrent(); + } + +} + +/*! + Reimplement in subclass to do makeCurrent on native GL context +*/ +void QPlatformGLContext::makeCurrent() +{ + QPlatformGLContextPrivate::setCurrentContext(this); +} + +/*! + Reimplement in subclass to release current context. + Typically this is calling makeCurrent with 0 "surface" +*/ +void QPlatformGLContext::doneCurrent() +{ + QPlatformGLContextPrivate::setCurrentContext(0); +} + +/* + internal: Needs to have a pointer to qGLContext. But since this is in QtGui we cant + have any type information. +*/ +void *QPlatformGLContext::qGLContextHandle() const +{ + Q_D(const QPlatformGLContext); + return d->qGLContextHandle; +} + +void QPlatformGLContext::setQGLContextHandle(void *handle,void (*qGLContextDeleteFunction)(void *)) +{ + Q_D(QPlatformGLContext); + d->qGLContextHandle = handle; + d->qGLContextDeleteFunction = qGLContextDeleteFunction; +} + +void QPlatformGLContext::deleteQGLContext() +{ + Q_D(QPlatformGLContext); + if (d->qGLContextDeleteFunction && d->qGLContextHandle) { + d->qGLContextDeleteFunction(d->qGLContextHandle); + d->qGLContextDeleteFunction = 0; + d->qGLContextHandle = 0; + } +} + +/*! + \class QPlatformGLContext + \since 4.8 + \internal + \preliminary + \ingroup qpa + + \brief The QPlatformGLContext class provides an abstraction for native GL contexts. + + In QPA the way to support OpenGL or OpenVG or other technologies that requires a native GL + context is through the QPlatformGLContext wrapper. + + There is no factory function for QPlatformGLContexts, but rather only one accessor function. + The only place to retrieve a QPlatformGLContext from is through a QPlatformWindow. + + The context which is current for a specific thread can be collected by the currentContext() + function. This is how QPlatformGLContext also makes it possible to use the QtOpenGL module + withhout using QGLWidget. When using QGLContext::currentContext(), it will ask + QPlatformGLContext for the currentContext. Then a corresponding QGLContext will be returned, + which maps to the QPlatformGLContext. +*/ + +/*! \fn void QPlatformGLContext::swapBuffers() + Reimplement in subclass to native swap buffers calls +*/ + +/*! \fn void *QPlatformGLContext::getProcAddress(const QString &procName) + Reimplement in subclass to native getProcAddr calls. + + Note: its convenient to use qPrintable(const QString &str) to get the const char * pointer +*/ + +/*! \fn QPlatformWindowFormat QPlatformGLContext::platformWindowFormat() const + QWidget has the function qplatformWindowFormat(). That function is for the application + programmer to request the format of the window and the context that he wants. + + Reimplement this function in a subclass to indicate what format the glContext actually has. +*/ diff --git a/src/gui/kernel/qplatformglcontext_qpa.h b/src/gui/kernel/qplatformglcontext_qpa.h new file mode 100644 index 0000000..1e64f4a --- /dev/null +++ b/src/gui/kernel/qplatformglcontext_qpa.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORM_GL_CONTEXT_H +#define QPLATFORM_GL_CONTEXT_H + +#include <QtCore/qnamespace.h> +#include <QtGui/QPlatformWindowFormat> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformGLContextPrivate; + +class Q_OPENGL_EXPORT QPlatformGLContext +{ +Q_DECLARE_PRIVATE(QPlatformGLContext); + +public: + explicit QPlatformGLContext(); + virtual ~QPlatformGLContext(); + + virtual void makeCurrent(); + virtual void doneCurrent(); + virtual void swapBuffers() = 0; + virtual void* getProcAddress(const QString& procName) = 0; + + virtual QPlatformWindowFormat platformWindowFormat() const = 0; + + const static QPlatformGLContext *currentContext(); + +protected: + QScopedPointer<QPlatformGLContextPrivate> d_ptr; + +private: + //hack to make it work with QGLContext::CurrentContext + friend class QGLContext; + friend class QWidgetPrivate; + void *qGLContextHandle() const; + void setQGLContextHandle(void *handle,void (*qGLContextDeleteFunction)(void *)); + void deleteQGLContext(); + Q_DISABLE_COPY(QPlatformGLContext); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif // QPLATFORM_GL_INTEGRATION_P_H diff --git a/src/gui/kernel/qplatformintegration_qpa.cpp b/src/gui/kernel/qplatformintegration_qpa.cpp new file mode 100644 index 0000000..29287fe --- /dev/null +++ b/src/gui/kernel/qplatformintegration_qpa.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformintegration_qpa.h" + +#include <QtGui/QPlatformFontDatabase> +#include <QtGui/QPlatformClipboard> + +QT_BEGIN_NAMESPACE + +QPixmap QPlatformIntegration::grabWindow(WId window, int x, int y, int width, int height) const +{ + Q_UNUSED(window); + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(width); + Q_UNUSED(height); + return QPixmap(); +} + +/*! + Factory function for the eventloop integration interface. + + Default implementation returns 0, which causes the eventloop to run in a single thread mode. + + \sa QPlatformEventLoopIntegration +*/ +QPlatformEventLoopIntegration *QPlatformIntegration::createEventLoopIntegration() const +{ + return 0; +} + +/*! + Accessor for the platform integrations fontdatabase. + + Default implementation returns a default QPlatformFontDatabase. + + \sa QPlatformFontDatabase +*/ +QPlatformFontDatabase *QPlatformIntegration::fontDatabase() const +{ + static QPlatformFontDatabase *db = 0; + if (!db) { + db = new QPlatformFontDatabase; + } + return db; +} + +/*! + Accessor for the platform integrations clipboard. + + Default implementation returns a default QPlatformClipboard. + + \sa QPlatformClipboard + +*/ + +#ifndef QT_NO_CLIPBOARD + +QPlatformClipboard *QPlatformIntegration::clipboard() const +{ + static QPlatformClipboard *clipboard = 0; + if (!clipboard) { + clipboard = new QPlatformClipboard; + } + return clipboard; +} + +#endif + +QPlatformNativeInterface * QPlatformIntegration::nativeInterface() const +{ + return 0; +} + +/*! + \class QPlatformIntegration + \since 4.8 + \internal + \preliminary + \ingroup qpa + \brief The QPlatformIntegration class is the entry for WindowSystem specific functionality. + + QPlatformIntegration is the single entry point for windowsystem specific functionality when + using the QPA platform. It has factory functions for creating platform specific pixmaps and + windows. The class also controls the font subsystem. + + QPlatformIntegration is a singelton class which gets instansiated in the QApplication + constructor. The QPlatformIntegration instance do not have ownership of objects it creates in + functions where the name starts with create. However, functions which don't have a name + starting with create acts as assessors to member variables. + + It is not trivial to create or build a platform plugin outside of the Qt source tree. Therefor + the recommended approach for making new platform plugin is to copy an existing plugin inside + the QTSRCTREE/src/plugins/platform and develop the plugin inside the source tree. + + The minimal platform integration is the smallest platform integration it is possible to make, + which makes it an ideal starting point for new plugins. For a slightly more advanced plugin, + consider reviewing the directfb plugin, or the testlite plugin. +*/ + +/*! + \fn QPixmapData *QPlatformIntegration::createPixmapData(QPixmapData::PixelType type) const + + Factory function for QPixmapData. PixelType can be either PixmapType or BitmapType. + \sa QPixmapData +*/ + +/*! + \fn QPlatformWindow *QPlatformIntegration::createPlatformWindow(QWidget *widget, WId winId = 0) const + + Factory function for QPlatformWindow. The widget parameter is a pointer to the top level + widget(tlw) which the QPlatformWindow is suppose to be created for. The WId handle is actually + never used, but there for future reference. Its purpose is if it is going to be possible to + create QPlatformWindows on existing WId. + + All tlw has to have a QPlatformWindow, and it will be created when the QPlatformWindow is set + to be visible for the first time. If the tlw's window flags are changed, or if the tlw's + QPlatformWindowFormat is changed, then the tlw's QPlatformWindow is deleted and a new one is + created. + + \sa QPlatformWindow, QPlatformWindowFormat + \sa createWindowSurface(QWidget *widget, WId winId) const +*/ + +/*! + \fn QWindowSurface *QPlatformIntegration::createWindowSurface(QWidget *widget, WId winId) const + + Factory function for QWindowSurface. The QWidget parameter is a pointer to the + top level widget(tlw) the window surface is created for. A QPlatformWindow is always created + before the QWindowSurface for tlw where the widget also requires a WindowSurface. It is + possible to create top level QWidgets without a QWindowSurface by specifying + QPlatformWindowFormat::setWindowSurface(false) for the tlw QPlatformWindowFormat. + + \sa QWindowSurface + \sa createPlatformWindow(QWidget *widget, WId winId = 0) const +*/ + +/*! + \fn void QPlatformIntegration::moveToScreen(QWidget *window, int screen) + + This function is called when a QWidget is displayed on screen, or the QWidget is to be + displayed on a new screen. The QWidget parameter is a pointer to the top level widget and + the int parameter is the index to the screen in QList<QPlatformScreen *> screens() const. + + Default implementation does nothing. + + \sa screens() const +*/ + +/*! + \fn QList<QPlatformScreen *> QPlatformIntegration::screens() const + + Accessor function to a list of all the screens on the current system. The screen with the + index == 0 is the default/main screen. +*/ + +/*! + \fn bool QPlatformIntegration::isVirtualDesktop() + + Returns if the current windowing system configuration defines all the screens to be one + desktop(virtual desktop), or if each screen is a desktop of its own. + + Default implementation returns false. +*/ + +/*! + \fn QPixmap QPlatformIntegration::grabWindow(WId window, int x, int y, int width, int height) const + + This function is called when Qt needs to be able to grab the content of a window. + + Returnes the content of the window specified with the WId handle within the boundaries of + QRect(x,y,width,height). +*/ + + +bool QPlatformIntegration::hasCapability(Capability cap) const +{ + Q_UNUSED(cap); + return false; +} + + + + + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformintegration_qpa.h b/src/gui/kernel/qplatformintegration_qpa.h new file mode 100644 index 0000000..74d2342 --- /dev/null +++ b/src/gui/kernel/qplatformintegration_qpa.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMINTEGRATION_H +#define QPLATFORMINTEGRATION_H + +#include <QtGui/qwindowdefs.h> +#include <QtGui/private/qwindowsurface_p.h> +#include <QtGui/private/qpixmapdata_p.h> +#include <QtGui/qplatformscreen_qpa.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformWindow; +class QWindowSurface; +class QBlittable; +class QWidget; +class QPlatformEventLoopIntegration; +class QPlatformFontDatabase; +class QPlatformClipboard; +class QPlatformNativeInterface; + +class Q_GUI_EXPORT QPlatformIntegration +{ +public: + enum Capability { + ThreadedPixmaps = 1, + OpenGL = 2 + }; + + virtual ~QPlatformIntegration() { } + + virtual bool hasCapability(Capability cap) const; + +// GraphicsSystem functions + virtual QPixmapData *createPixmapData(QPixmapData::PixelType type) const = 0; + virtual QPlatformWindow *createPlatformWindow(QWidget *widget, WId winId = 0) const = 0; + virtual QWindowSurface *createWindowSurface(QWidget *widget, WId winId) const = 0; + +// Window System functions + virtual QList<QPlatformScreen *> screens() const = 0; + virtual void moveToScreen(QWidget *window, int screen) {Q_UNUSED(window); Q_UNUSED(screen);} + virtual bool isVirtualDesktop() { return false; } + virtual QPixmap grabWindow(WId window, int x, int y, int width, int height) const; + +//Deeper window system integrations + virtual QPlatformFontDatabase *fontDatabase() const; +#ifndef QT_NO_CLIPBOARD + virtual QPlatformClipboard *clipboard() const; +#endif + +// Experimental in mainthread eventloop integration +// This should only be used if it is only possible to do window system event processing in +// the gui thread. All of the functions in QWindowSystemInterface are thread safe. + virtual QPlatformEventLoopIntegration *createEventLoopIntegration() const; + +// Access native handles. The window handle is already available from Wid; + virtual QPlatformNativeInterface *nativeInterface() const; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMINTEGRATION_H diff --git a/src/gui/kernel/qplatformintegrationfactory_qpa.cpp b/src/gui/kernel/qplatformintegrationfactory_qpa.cpp new file mode 100644 index 0000000..a2d1c36 --- /dev/null +++ b/src/gui/kernel/qplatformintegrationfactory_qpa.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformintegrationfactory_qpa_p.h" +#include <QPlatformIntegrationPlugin> +#include "private/qfactoryloader_p.h" +#include "qmutex.h" + +#include "qapplication.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QPlatformIntegrationFactoryInterface_iid, QLatin1String("/platforms"), Qt::CaseInsensitive)) +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader, + (QPlatformIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive)) +#endif + +QPlatformIntegration *QPlatformIntegrationFactory::create(const QString& key, const QString &platformPluginPath) +{ + QPlatformIntegration *ret = 0; + QStringList paramList = key.split(QLatin1Char(':')); + QString platform = paramList.takeFirst().toLower(); + +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + // Try loading the plugin from platformPluginPath first: + if (!platformPluginPath.isEmpty()) { + QCoreApplication::addLibraryPath(platformPluginPath); + if (QPlatformIntegrationFactoryInterface *factory = + qobject_cast<QPlatformIntegrationFactoryInterface*>(directLoader()->instance(platform))) + ret = factory->create(key, paramList); + + if (ret) + return ret; + } + if (QPlatformIntegrationFactoryInterface *factory = qobject_cast<QPlatformIntegrationFactoryInterface*>(loader()->instance(platform))) + ret = factory->create(platform, paramList); +#endif + + return ret; +} + +/*! + Returns the list of valid keys, i.e. the keys this factory can + create styles for. + + \sa create() +*/ +QStringList QPlatformIntegrationFactory::keys(const QString &platformPluginPath) +{ +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + QStringList list; + + if (!platformPluginPath.isEmpty()) { + QCoreApplication::addLibraryPath(platformPluginPath); + foreach (const QString &key, directLoader()->keys()) { + list += key + QString(QLatin1String(" (from %1)")).arg(platformPluginPath); + } + } + + list += loader()->keys(); +#else + QStringList list; +#endif + return list; +} + +QT_END_NAMESPACE + diff --git a/src/gui/kernel/qplatformintegrationfactory_qpa_p.h b/src/gui/kernel/qplatformintegrationfactory_qpa_p.h new file mode 100644 index 0000000..5ffdf85 --- /dev/null +++ b/src/gui/kernel/qplatformintegrationfactory_qpa_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMINTEGRATIONFACTORY_H +#define QPLATFORMINTEGRATIONFACTORY_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstringlist.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformIntegration; + +class QPlatformIntegrationFactory +{ +public: + static QStringList keys(const QString &platformPluginPath = QString()); + static QPlatformIntegration *create(const QString &key, const QString &platformPluginPath = QString()); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMINTEGRATIONFACTORY_H + diff --git a/src/gui/kernel/qplatformintegrationplugin_qpa.cpp b/src/gui/kernel/qplatformintegrationplugin_qpa.cpp new file mode 100644 index 0000000..0181b98 --- /dev/null +++ b/src/gui/kernel/qplatformintegrationplugin_qpa.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformintegrationplugin_qpa.h" + +QT_BEGIN_NAMESPACE + +QPlatformIntegrationPlugin::QPlatformIntegrationPlugin(QObject *parent) + : QObject(parent) +{ +} + +QPlatformIntegrationPlugin::~QPlatformIntegrationPlugin() +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformintegrationplugin_qpa.h b/src/gui/kernel/qplatformintegrationplugin_qpa.h new file mode 100644 index 0000000..b397f56 --- /dev/null +++ b/src/gui/kernel/qplatformintegrationplugin_qpa.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMINTEGRATIONPLUGIN_H +#define QPLATFORMINTEGRATIONPLUGIN_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformIntegration; + +struct QPlatformIntegrationFactoryInterface : public QFactoryInterface +{ + virtual QPlatformIntegration *create(const QString &key, const QStringList ¶mList) = 0; +}; + +#define QPlatformIntegrationFactoryInterface_iid "com.nokia.Qt.QPlatformIntegrationFactoryInterface" + +Q_DECLARE_INTERFACE(QPlatformIntegrationFactoryInterface, QPlatformIntegrationFactoryInterface_iid) + +class Q_GUI_EXPORT QPlatformIntegrationPlugin : public QObject, public QPlatformIntegrationFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QPlatformIntegrationFactoryInterface:QFactoryInterface) +public: + explicit QPlatformIntegrationPlugin(QObject *parent = 0); + ~QPlatformIntegrationPlugin(); + + virtual QStringList keys() const = 0; + virtual QPlatformIntegration *create(const QString &key, const QStringList ¶mList) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMINTEGRATIONPLUGIN_H diff --git a/src/gui/kernel/qplatformnativeinterface_qpa.cpp b/src/gui/kernel/qplatformnativeinterface_qpa.cpp new file mode 100644 index 0000000..32849d9 --- /dev/null +++ b/src/gui/kernel/qplatformnativeinterface_qpa.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformnativeinterface_qpa.h" + +QT_BEGIN_NAMESPACE + +void *QPlatformNativeInterface::nativeResourceForWidget(const QByteArray &resource, QWidget *widget) +{ + Q_UNUSED(resource); + Q_UNUSED(widget); + return 0; +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformnativeinterface_qpa.h b/src/gui/kernel/qplatformnativeinterface_qpa.h new file mode 100644 index 0000000..9f22337 --- /dev/null +++ b/src/gui/kernel/qplatformnativeinterface_qpa.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMNATIVEINTERFACE_QPA_H +#define QPLATFORMNATIVEINTERFACE_QPA_H + +#include <QtGui/qwindowdefs.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QWidget; + +class Q_GUI_EXPORT QPlatformNativeInterface +{ +public: + virtual void *nativeResourceForWidget(const QByteArray &resource, QWidget *widget); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMNATIVEINTERFACE_QPA_H diff --git a/src/gui/kernel/qplatformscreen_qpa.cpp b/src/gui/kernel/qplatformscreen_qpa.cpp new file mode 100644 index 0000000..2a1e7e4 --- /dev/null +++ b/src/gui/kernel/qplatformscreen_qpa.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformscreen_qpa.h" +#include <QtGui/qapplication.h> +#include <QtGui/private/qapplication_p.h> +#include <QtGui/qdesktopwidget.h> +#include <QtGui/qplatformintegration_qpa.h> +#include <QtGui/qwidget.h> +#include <QtGui/private/qwidget_p.h> + +/*! + Return the given top level widget for a given position. + + Default implementation retrieves a list of all top level widgets and finds the first widget + which contains point \a pos +*/ +QWidget *QPlatformScreen::topLevelAt(const QPoint & pos) const +{ + QWidgetList list = QApplication::topLevelWidgets(); + for (int i = list.size()-1; i >= 0; --i) { + QWidget *w = list[i]; + //### mask is ignored + if (w != QApplication::desktop() && w->isVisible() && w->geometry().contains(pos)) + return w; + } + + return 0; +} + +/*! + Reimplement this function in subclass to return the physical size of the + screen. This function is used by QFont to convert point sizes to pixel + sizes. + + The default implementation takes the pixel size of the screen, considers a + resolution of 100 dots per inch, and returns the calculated physical size. + A device with a screen that has different resolutions will need to be + supported by a suitable reimplementation of this function. +*/ +QSize QPlatformScreen::physicalSize() const +{ + static const int dpi = 100; + int width = geometry().width() / dpi * qreal(25.4) ; + int height = geometry().height() / dpi * qreal(25.4) ; + return QSize(width,height); +} + +Q_GUI_EXPORT extern QWidgetPrivate *qt_widget_private(QWidget *widget); +QPlatformScreen * QPlatformScreen::platformScreenForWidget(const QWidget *widget) +{ + QWidget *window = widget->window(); + QWidgetPrivate *windowPrivate = qt_widget_private(window); + QTLWExtra * topData = windowPrivate->topData(); + QPlatformIntegration *integration = + QApplicationPrivate::platformIntegration(); + return integration->screens()[topData->screenIndex]; +} + +/*! + \class QPlatformScreen + \since 4.8 + \internal + \preliminary + \ingroup qpa + + \brief The QPlatformScreen class provides an abstraction for visual displays. + + Many window systems has support for retrieving information on the attached displays. To be able + to query the display QPA uses QPlatformScreen. Qt its self is most dependent on the + physicalSize() function, since this is the function it uses to calculate the dpi to use when + converting point sizes to pixels sizes. However, this is unfortunate on some systems, as the + native system fakes its dpi size. + + QPlatformScreen is also used by the public api QDesktopWidget for information about the desktop. + */ + +/*! \fn QRect QPlatformScreen::geometry() const = 0 + Reimplement in subclass to return the pixel geometry of the screen +*/ + +/*! \fn QRect QPlatformScreen::availableGeometry() const + Reimplement in subclass to return the pixel geometry of the available space + This normally is the desktop screen minus the task manager, global menubar etc. +*/ + +/*! \fn int QPlatformScreen::depth() const = 0 + Reimplement in subclass to return current depth of the screen +*/ + +/*! \fn QImage::Format QPlatformScreen::format() const = 0 + Reimplement in subclass to return the image format which corresponds to the screen format +*/ + diff --git a/src/gui/kernel/qplatformscreen_qpa.h b/src/gui/kernel/qplatformscreen_qpa.h new file mode 100644 index 0000000..a9ce7c2 --- /dev/null +++ b/src/gui/kernel/qplatformscreen_qpa.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMSCREEN_H +#define QPLATFORMSCREEN_H + +#include <QtCore/qmetatype.h> +#include <QtCore/qnamespace.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qvariant.h> +#include <QtCore/qrect.h> +#include <QtCore/qobject.h> + +#include <QtGui/qcursor.h> +#include <QtGui/qimage.h> +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QPlatformScreen : public QObject +{ + Q_OBJECT +public: + virtual ~QPlatformScreen() { } + + virtual QRect geometry() const = 0; + virtual QRect availableGeometry() const {return geometry();} + virtual int depth() const = 0; + virtual QImage::Format format() const = 0; + virtual QSize physicalSize() const; + //jl: should setDirty be removed. + virtual void setDirty(const QRect &) {} + virtual QWidget *topLevelAt(const QPoint &point) const; + + //jl: should this function be in QPlatformIntegration + //jl: maybe screenForWidget is a better name? + static QPlatformScreen *platformScreenForWidget(const QWidget *widget); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMSCREEN_H diff --git a/src/gui/kernel/qplatformwindow_qpa.cpp b/src/gui/kernel/qplatformwindow_qpa.cpp new file mode 100644 index 0000000..b1c66ea --- /dev/null +++ b/src/gui/kernel/qplatformwindow_qpa.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformwindow_qpa.h" + +#include <QtGui/qwindowsysteminterface_qpa.h> +#include <QtGui/qwidget.h> + +class QPlatformWindowPrivate +{ + QWidget *tlw; + QRect rect; + Qt::WindowFlags flags; + friend class QPlatformWindow; +}; + +/*! + Constructs a platform window with the given top level widget. +*/ + +QPlatformWindow::QPlatformWindow(QWidget *tlw) + : d_ptr(new QPlatformWindowPrivate) +{ + Q_D(QPlatformWindow); + d->tlw = tlw; + tlw->setPlatformWindow(this); +} + +/*! + Virtual destructor does not delete its top level widget. +*/ +QPlatformWindow::~QPlatformWindow() +{ +} + +/*! + Returnes the widget which belongs to the QPlatformWindow +*/ +QWidget *QPlatformWindow::widget() const +{ + Q_D(const QPlatformWindow); + return d->tlw; +} + +/*! + This function is called by Qt whenever a window is moved or the window is resized. The resize + can happen programatically(from ie. user application) or by the window manager. This means that + there is no need to call this function specifically from the window manager callback, instead + call QWindowSystemInterface::handleGeometryChange(QWidget *w, const QRect &newRect); +*/ +void QPlatformWindow::setGeometry(const QRect &rect) +{ + Q_D(QPlatformWindow); + d->rect = rect; +} + +/*! + Returnes the current geometry of a window +*/ +QRect QPlatformWindow::geometry() const +{ + Q_D(const QPlatformWindow); + return d->rect; +} + +/*! + Reimplemented in subclasses to show the surface + if \a visible is \c true, and hide it if \a visible is \c false. +*/ +void QPlatformWindow::setVisible(bool visible) +{ + Q_UNUSED(visible); +} +/*! + Requests setting the window flags of this surface + to \a type. Returns the actual flags set. +*/ +Qt::WindowFlags QPlatformWindow::setWindowFlags(Qt::WindowFlags flags) +{ + Q_D(QPlatformWindow); + d->flags = flags; + return flags; +} + +/*! + Returns the effective window flags for this surface. +*/ +Qt::WindowFlags QPlatformWindow::windowFlags() const +{ + Q_D(const QPlatformWindow); + return d->flags; +} + +/*! + Reimplement in subclasses to return a handle to the native window +*/ +WId QPlatformWindow::winId() const { return WId(0); } + +/*! + This function is called to enable native child widgets in QPA. It is common not to support this + feature in Window systems, but can be faked. When this function is called all geometry of this + platform window will be relative to the parent. +*/ +//jl: It would be useful to have a property on the platform window which indicated if the sub-class +// supported the setParent. If not, then geometry would be in screen coordinates. +void QPlatformWindow::setParent(const QPlatformWindow *parent) +{ + Q_UNUSED(parent); + qWarning("This plugin does not support setParent!"); +} + +/*! + Reimplement to set the window title to \a title +*/ +void QPlatformWindow::setWindowTitle(const QString &) {} + +/*! + Reimplement to be able to let Qt rais windows to the top of the desktop +*/ +void QPlatformWindow::raise() { qWarning("This plugin does not support raise()"); } + +/*! + Reimplement to be able to let Qt lower windows to the bottom of the desktop +*/ +void QPlatformWindow::lower() { qWarning("This plugin does not support lower()"); } + +/*! + Reimplement to be able to let Qt set the opacity level of a window +*/ +void QPlatformWindow::setOpacity(qreal level) +{ + Q_UNUSED(level); + qWarning("This plugin does not support setting window opacity"); +} + +/*! + Reimplement to let Qt be able to request activation/focus for a window + + Some window systems will probably not have callbacks for this functionality, + and then calling QWindowSystemInterface::handleWindowActivated(QWidget *w) + would be sufficient. + + If the window system has some event handling/callbacks then call + QWindowSystemInterface::handleWindowActivated(QWidget *w) when the window system + gives the notification. + + Default implementation calls QWindowSystem::handleWindowActivated(QWidget *w) +*/ +void QPlatformWindow::requestActivateWindow() +{ + QWindowSystemInterface::handleWindowActivated(widget()); +} + +/*! + Reimplement to return the glContext associated with the window. +*/ +QPlatformGLContext *QPlatformWindow::glContext() const +{ + return 0; +} + +/*! + \class QPlatformWindow + \since 4.8 + \internal + \preliminary + \ingroup qpa + + \brief The QPlatformWindow class provides an abstraction for top-level windows. + + The QPlatformWindow abstraction is used by QWidget for all its top level widgets. It is being + created by calling the createPlatformWindow function in the loaded QPlatformIntegration + instance. + + QPlatformWindow is used to signal to the windowing system, how Qt persieves its frame. + However, it is not concerned with how Qt renders into the window it represents. + + Top level QWidgets(tlw) will always have a QPlatformWindow. However, it is not necessary for + all tlw to have a QWindowSurface. This is the case for QGLWidget. And could be the case for + widgets where some 3.party renders into it. + + The platform specific window handle can be retrieved by the winId function. + + QPlatformWindow is also the way QPA defines how native child windows should be supported + through the setParent function. + + The only way to retrieve a QPlatformGLContext in QPA is by calling the glContext() function + on QPlatformWindow. + + \sa QWindowSurface, QWidget +*/ diff --git a/src/gui/kernel/qplatformwindow_qpa.h b/src/gui/kernel/qplatformwindow_qpa.h new file mode 100644 index 0000000..78090de --- /dev/null +++ b/src/gui/kernel/qplatformwindow_qpa.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QPLATFORMWINDOW_H +#define QPLATFORMWINDOW_H + + +#include <QtCore/qscopedpointer.h> +#include <QtCore/qrect.h> +#include <QtCore/qstring.h> +#include <QtGui/qwindowdefs.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformWindowPrivate; +class QWidget; +class QPlatformGLContext; + +class Q_GUI_EXPORT QPlatformWindow +{ + Q_DECLARE_PRIVATE(QPlatformWindow) +public: + QPlatformWindow(QWidget *tlw); + virtual ~QPlatformWindow(); + + QWidget *widget() const; + virtual void setGeometry(const QRect &rect); + virtual QRect geometry() const; + + virtual void setVisible(bool visible); + virtual Qt::WindowFlags setWindowFlags(Qt::WindowFlags flags); + virtual Qt::WindowFlags windowFlags() const; + virtual WId winId() const; + virtual void setParent(const QPlatformWindow *window); + + virtual void setWindowTitle(const QString &title); + virtual void raise(); + virtual void lower(); + + virtual void setOpacity(qreal level); + virtual void requestActivateWindow(); + + virtual QPlatformGLContext *glContext() const; +protected: + QScopedPointer<QPlatformWindowPrivate> d_ptr; +private: + Q_DISABLE_COPY(QPlatformWindow) +}; + +QT_END_NAMESPACE + +QT_END_HEADER +#endif //QPLATFORMWINDOW_H diff --git a/src/gui/kernel/qplatformwindowformat_qpa.cpp b/src/gui/kernel/qplatformwindowformat_qpa.cpp new file mode 100644 index 0000000..4ab8dfd --- /dev/null +++ b/src/gui/kernel/qplatformwindowformat_qpa.cpp @@ -0,0 +1,998 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformwindowformat_qpa.h" + +#include <QtCore/QDebug> + +Q_GLOBAL_STATIC(QPlatformWindowFormat, q_platformwindow_default_format); + +class QPlatformWindowFormatPrivate +{ +public: + QPlatformWindowFormatPrivate() + : ref(1) + , opts(QPlatformWindowFormat::DoubleBuffer | QPlatformWindowFormat::DepthBuffer + | QPlatformWindowFormat::Rgba | QPlatformWindowFormat::DirectRendering + | QPlatformWindowFormat::StencilBuffer | QPlatformWindowFormat::DeprecatedFunctions + | QPlatformWindowFormat::HasWindowSurface) + , depthSize(-1) + , accumSize(-1) + , stencilSize(-1) + , redSize(-1) + , greenSize(-1) + , blueSize(-1) + , alphaSize(-1) + , numSamples(-1) + , swapInterval(-1) + , windowApi(QPlatformWindowFormat::Raster) + , sharedContext(0) + { + } + + QPlatformWindowFormatPrivate(const QPlatformWindowFormatPrivate *other) + : ref(1), + opts(other->opts), + depthSize(other->depthSize), + accumSize(other->accumSize), + stencilSize(other->stencilSize), + redSize(other->redSize), + greenSize(other->greenSize), + blueSize(other->blueSize), + alphaSize(other->alphaSize), + numSamples(other->numSamples), + swapInterval(other->swapInterval), + windowApi(other->windowApi), + sharedContext(other->sharedContext) + { + } + QAtomicInt ref; + QPlatformWindowFormat::FormatOptions opts; + int depthSize; + int accumSize; + int stencilSize; + int redSize; + int greenSize; + int blueSize; + int alphaSize; + int numSamples; + int swapInterval; + QPlatformWindowFormat::WindowApi windowApi; + QPlatformGLContext *sharedContext; +}; + +/*! + \class QPlatformWindowFormat + \ingroup painting + \since 4.8 + \brief The QPlatformWindowFormat class specifies the display format of an OpenGL + rendering context and if possible attributes of the corresponding QPlatformWindow. + + QWidget has a setter and getter function for QPlatformWindowFormat. These functions can be used + by the application programmer to signal what kind of format he wants to the window and glcontext + should have. However, it is not always possible to fulfill these requirements. The application + programmer should therefore check the resulting QPlatformWindowFormat from QPlatformGLContext + to see the format that was actually created. + + A display format has several characteristics: + \list + \i \link setDoubleBuffer() Double or single buffering.\endlink + \i \link setDepth() Depth buffer.\endlink + \i \link setRgba() RGBA or color index mode.\endlink + \i \link setAlpha() Alpha channel.\endlink + \i \link setAccum() Accumulation buffer.\endlink + \i \link setStencil() Stencil buffer.\endlink + \i \link setStereo() Stereo buffers.\endlink + \i \link setDirectRendering() Direct rendering.\endlink + \i \link setSampleBuffers() Multisample buffers.\endlink + \endlist + + You can also specify preferred bit depths for the color buffer, + depth buffer, alpha buffer, accumulation buffer and the stencil + buffer with the functions: setRedBufferSize(), setGreenBufferSize(), + setBlueBufferSize(), setDepthBufferSize(), setAlphaBufferSize(), + setAccumBufferSize() and setStencilBufferSize(). + + Note that even if you specify that you prefer a 32 bit depth + buffer (e.g. with setDepthBufferSize(32)), the format that is + chosen may not have a 32 bit depth buffer, even if there is a + format available with a 32 bit depth buffer. The main reason for + this is how the system dependant picking algorithms work on the + different platforms, and some format options may have higher + precedence than others. + + You create and tell a QPlatformWindowFormat object what rendering options you + want from an OpenGL rendering context. + + OpenGL drivers or accelerated hardware may or may not support + advanced features such as alpha channel or stereographic viewing. + If you request some features that the driver/hardware does not + provide when you create a QGLWidget, you will get a rendering + context with the nearest subset of features. + + There are different ways to define the display characteristics of + a rendering context. One is to create a QPlatformWindowFormat and make it the + default for the entire application: + \snippet doc/src/snippets/code/src_opengl_qgl.cpp 0 + + Or you can specify the desired format when creating an object of + your QGLWidget subclass: + \snippet doc/src/snippets/code/src_opengl_qgl.cpp 1 + + After the widget has been created, you can find out which of the + requested features the system was able to provide: + \snippet doc/src/snippets/code/src_opengl_qgl.cpp 2 + + \legalese + OpenGL is a trademark of Silicon Graphics, Inc. in the + United States and other countries. + \endlegalese + + \sa QPlatformGLContext, QWidget +*/ + +/*! + Constructs a QPlatformWindowFormat object with the following default settings: + \list + \i \link setDoubleBuffer() Double buffer:\endlink Enabled. + \i \link setDepth() Depth buffer:\endlink Enabled. + \i \link setRgba() RGBA:\endlink Enabled (i.e., color index disabled). + \i \link setAlpha() Alpha channel:\endlink Disabled. + \i \link setAccum() Accumulator buffer:\endlink Disabled. + \i \link setStencil() Stencil buffer:\endlink Enabled. + \i \link setStereo() Stereo:\endlink Disabled. + \i \link setDirectRendering() Direct rendering:\endlink Enabled. + \i \link setSampleBuffers() Multisample buffers:\endlink Disabled. + \endlist +*/ + +QPlatformWindowFormat::QPlatformWindowFormat() +{ + d = new QPlatformWindowFormatPrivate; +} + + +/*! + Creates a QPlatformWindowFormat object that is a copy of the current + defaultFormat(). + + If \a options is not 0, the default format is modified by the + specified format options. The \a options parameter should be + QGL::FormatOption values OR'ed together. + + This constructor makes it easy to specify a certain desired format + in classes derived from QGLWidget, for example: + \snippet doc/src/snippets/code/src_opengl_qgl.cpp 3 + + Note that there are QGL::FormatOption values to turn format settings + both on and off; e.g., QGL::DepthBuffer and QGL::NoDepthBuffer, + QGL::DirectRendering and QGL::IndirectRendering, etc. + + \sa defaultFormat(), setOption() +*/ + +QPlatformWindowFormat::QPlatformWindowFormat(QPlatformWindowFormat::FormatOptions options) +{ + d = new QPlatformWindowFormatPrivate; + QPlatformWindowFormat::FormatOptions newOpts = options; + d->opts = defaultFormat().d->opts; + d->opts |= (newOpts & 0xffff); + d->opts &= ~(newOpts >> 16); +} + +/*! + \internal +*/ +void QPlatformWindowFormat::detach() +{ + if (d->ref != 1) { + QPlatformWindowFormatPrivate *newd = new QPlatformWindowFormatPrivate(d); + if (!d->ref.deref()) + delete d; + d = newd; + } +} + +/*! + Constructs a copy of \a other. +*/ + +QPlatformWindowFormat::QPlatformWindowFormat(const QPlatformWindowFormat &other) +{ + d = other.d; + d->ref.ref(); +} + +/*! + Assigns \a other to this object. +*/ + +QPlatformWindowFormat &QPlatformWindowFormat::operator=(const QPlatformWindowFormat &other) +{ + if (d != other.d) { + other.d->ref.ref(); + if (!d->ref.deref()) + delete d; + d = other.d; + } + return *this; +} + +/*! + Destroys the QPlatformWindowFormat. +*/ +QPlatformWindowFormat::~QPlatformWindowFormat() +{ + if (!d->ref.deref()) + delete d; +} + +/*! + \fn bool QPlatformWindowFormat::doubleBuffer() const + + Returns true if double buffering is enabled; otherwise returns + false. Double buffering is enabled by default. + + \sa setDoubleBuffer() +*/ + +/*! + If \a enable is true sets double buffering; otherwise sets single + buffering. + + Double buffering is enabled by default. + + Double buffering is a technique where graphics are rendered on an + off-screen buffer and not directly to the screen. When the drawing + has been completed, the program calls a swapBuffers() function to + exchange the screen contents with the buffer. The result is + flicker-free drawing and often better performance. + + \sa doubleBuffer(), QGLContext::swapBuffers(), + QGLWidget::swapBuffers() +*/ + +void QPlatformWindowFormat::setDoubleBuffer(bool enable) +{ + setOption(enable ? QPlatformWindowFormat::DoubleBuffer : QPlatformWindowFormat::SingleBuffer); +} + + +/*! + \fn bool QPlatformWindowFormat::depth() const + + Returns true if the depth buffer is enabled; otherwise returns + false. The depth buffer is enabled by default. + + \sa setDepth(), setDepthBufferSize() +*/ + +/*! + If \a enable is true enables the depth buffer; otherwise disables + the depth buffer. + + The depth buffer is enabled by default. + + The purpose of a depth buffer (or Z-buffering) is to remove hidden + surfaces. Pixels are assigned Z values based on the distance to + the viewer. A pixel with a high Z value is closer to the viewer + than a pixel with a low Z value. This information is used to + decide whether to draw a pixel or not. + + \sa depth(), setDepthBufferSize() +*/ + +void QPlatformWindowFormat::setDepth(bool enable) +{ + setOption(enable ? QPlatformWindowFormat::DepthBuffer : QPlatformWindowFormat::NoDepthBuffer); +} + + +/*! + \fn bool QPlatformWindowFormat::rgba() const + + Returns true if RGBA color mode is set. Returns false if color + index mode is set. The default color mode is RGBA. + + \sa setRgba() +*/ + +/*! + If \a enable is true sets RGBA mode. If \a enable is false sets + color index mode. + + The default color mode is RGBA. + + RGBA is the preferred mode for most OpenGL applications. In RGBA + color mode you specify colors as red + green + blue + alpha + quadruplets. + + In color index mode you specify an index into a color lookup + table. + + \sa rgba() +*/ + +void QPlatformWindowFormat::setRgba(bool enable) +{ + setOption(enable ? QPlatformWindowFormat::Rgba : QPlatformWindowFormat::ColorIndex); +} + + +/*! + \fn bool QPlatformWindowFormat::alpha() const + + Returns true if the alpha buffer in the framebuffer is enabled; + otherwise returns false. The alpha buffer is disabled by default. + + \sa setAlpha(), setAlphaBufferSize() +*/ + +/*! + If \a enable is true enables the alpha buffer; otherwise disables + the alpha buffer. + + The alpha buffer is disabled by default. + + The alpha buffer is typically used for implementing transparency + or translucency. The A in RGBA specifies the transparency of a + pixel. + + \sa alpha(), setAlphaBufferSize() +*/ + +void QPlatformWindowFormat::setAlpha(bool enable) +{ + setOption(enable ? QPlatformWindowFormat::AlphaChannel : QPlatformWindowFormat::NoAlphaChannel); +} + + +/*! + \fn bool QPlatformWindowFormat::accum() const + + Returns true if the accumulation buffer is enabled; otherwise + returns false. The accumulation buffer is disabled by default. + + \sa setAccum(), setAccumBufferSize() +*/ + +/*! + If \a enable is true enables the accumulation buffer; otherwise + disables the accumulation buffer. + + The accumulation buffer is disabled by default. + + The accumulation buffer is used to create blur effects and + multiple exposures. + + \sa accum(), setAccumBufferSize() +*/ + +void QPlatformWindowFormat::setAccum(bool enable) +{ + setOption(enable ? QPlatformWindowFormat::AccumBuffer : QPlatformWindowFormat::NoAccumBuffer); +} + + +/*! + \fn bool QPlatformWindowFormat::stencil() const + + Returns true if the stencil buffer is enabled; otherwise returns + false. The stencil buffer is enabled by default. + + \sa setStencil(), setStencilBufferSize() +*/ + +/*! + If \a enable is true enables the stencil buffer; otherwise + disables the stencil buffer. + + The stencil buffer is enabled by default. + + The stencil buffer masks certain parts of the drawing area so that + masked parts are not drawn on. + + \sa stencil(), setStencilBufferSize() +*/ + +void QPlatformWindowFormat::setStencil(bool enable) +{ + setOption(enable ? QPlatformWindowFormat::StencilBuffer: QPlatformWindowFormat::NoStencilBuffer); +} + + +/*! + \fn bool QPlatformWindowFormat::stereo() const + + Returns true if stereo buffering is enabled; otherwise returns + false. Stereo buffering is disabled by default. + + \sa setStereo() +*/ + +/*! + If \a enable is true enables stereo buffering; otherwise disables + stereo buffering. + + Stereo buffering is disabled by default. + + Stereo buffering provides extra color buffers to generate left-eye + and right-eye images. + + \sa stereo() +*/ + +void QPlatformWindowFormat::setStereo(bool enable) +{ + setOption(enable ? QPlatformWindowFormat::StereoBuffers : QPlatformWindowFormat::NoStereoBuffers); +} + + +/*! + \fn bool QPlatformWindowFormat::directRendering() const + + Returns true if direct rendering is enabled; otherwise returns + false. + + Direct rendering is enabled by default. + + \sa setDirectRendering() +*/ + +/*! + If \a enable is true enables direct rendering; otherwise disables + direct rendering. + + Direct rendering is enabled by default. + + Enabling this option will make OpenGL bypass the underlying window + system and render directly from hardware to the screen, if this is + supported by the system. + + \sa directRendering() +*/ + +void QPlatformWindowFormat::setDirectRendering(bool enable) +{ + setOption(enable ? QPlatformWindowFormat::DirectRendering : QPlatformWindowFormat::IndirectRendering); +} + +/*! + \fn bool QPlatformWindowFormat::sampleBuffers() const + + Returns true if multisample buffer support is enabled; otherwise + returns false. + + The multisample buffer is disabled by default. + + \sa setSampleBuffers() +*/ + +/*! + If \a enable is true, a GL context with multisample buffer support + is picked; otherwise ignored. + + \sa sampleBuffers(), setSamples(), samples() +*/ +void QPlatformWindowFormat::setSampleBuffers(bool enable) +{ + setOption(enable ? QPlatformWindowFormat::SampleBuffers : QPlatformWindowFormat::NoSampleBuffers); +} + +/*! + Returns the number of samples per pixel when multisampling is + enabled. By default, the highest number of samples that is + available is used. + + \sa setSampleBuffers(), sampleBuffers(), setSamples() +*/ +int QPlatformWindowFormat::samples() const +{ + return d->numSamples; +} + +/*! + Set the preferred number of samples per pixel when multisampling + is enabled to \a numSamples. By default, the highest number of + samples available is used. + + \sa setSampleBuffers(), sampleBuffers(), samples() +*/ +void QPlatformWindowFormat::setSamples(int numSamples) +{ + detach(); + if (numSamples < 0) { + qWarning("QPlatformWindowFormat::setSamples: Cannot have negative number of samples per pixel %d", numSamples); + return; + } + d->numSamples = numSamples; + setSampleBuffers(numSamples > 0); +} + +/*! + \since 4.2 + + Set the preferred swap interval. This can be used to sync the GL + drawing into a system window to the vertical refresh of the screen. + Setting an \a interval value of 0 will turn the vertical refresh syncing + off, any value higher than 0 will turn the vertical syncing on. + + Under Windows and under X11, where the \c{WGL_EXT_swap_control} + and \c{GLX_SGI_video_sync} extensions are used, the \a interval + parameter can be used to set the minimum number of video frames + that are displayed before a buffer swap will occur. In effect, + setting the \a interval to 10, means there will be 10 vertical + retraces between every buffer swap. + + Under Windows the \c{WGL_EXT_swap_control} extension has to be present, + and under X11 the \c{GLX_SGI_video_sync} extension has to be present. +*/ +void QPlatformWindowFormat::setSwapInterval(int interval) +{ + detach(); + d->swapInterval = interval; +} + +/*! + \since 4.2 + + Returns the currently set swap interval. -1 is returned if setting + the swap interval isn't supported in the system GL implementation. +*/ +int QPlatformWindowFormat::swapInterval() const +{ + return d->swapInterval; +} + +void QPlatformWindowFormat::setWindowApi(QPlatformWindowFormat::WindowApi api) +{ + detach(); + d->windowApi = api; +} + +QPlatformWindowFormat::WindowApi QPlatformWindowFormat::windowApi() const +{ + return d->windowApi; +} + +void QPlatformWindowFormat::setSharedContext(QPlatformGLContext *context) +{ + d->sharedContext = context; +} + +QPlatformGLContext *QPlatformWindowFormat::sharedGLContext() const +{ + return d->sharedContext; +} + +/*! + \fn bool QPlatformWindowFormat::hasWindowSurface() const + + Returns true if the corresponding widget has an instance of QWindowSurface. + + Otherwise returns false. + + WindowSurface is enabled by default. +*/ + +/*! + If \a enable is true a top level QWidget will create a QWindowSurface at creation; + + otherwise the QWidget will only have a QPlatformWindow. + + This is useful for QGLWidget where the QPlatformGLContext controls the surface. +*/ + +void QPlatformWindowFormat::setWindowSurface(bool enable) +{ + setOption(enable ? QPlatformWindowFormat::HasWindowSurface : QPlatformWindowFormat::NoWindowSurface); +} + +/*! + Sets the format option to \a opt. + + \sa testOption() +*/ + +void QPlatformWindowFormat::setOption(QPlatformWindowFormat::FormatOptions opt) +{ + detach(); + if (opt & 0xffff) + d->opts |= opt; + else + d->opts &= ~(opt >> 16); +} + + + +/*! + Returns true if format option \a opt is set; otherwise returns false. + + \sa setOption() +*/ + +bool QPlatformWindowFormat::testOption(QPlatformWindowFormat::FormatOptions opt) const +{ + if (opt & 0xffff) + return (d->opts & opt) != 0; + else + return (d->opts & (opt >> 16)) == 0; +} + +/*! + Set the minimum depth buffer size to \a size. + + \sa depthBufferSize(), setDepth(), depth() +*/ +void QPlatformWindowFormat::setDepthBufferSize(int size) +{ + detach(); + if (size < 0) { + qWarning("QPlatformWindowFormat::setDepthBufferSize: Cannot set negative depth buffer size %d", size); + return; + } + d->depthSize = size; + setDepth(size > 0); +} + +/*! + Returns the depth buffer size. + + \sa depth(), setDepth(), setDepthBufferSize() +*/ +int QPlatformWindowFormat::depthBufferSize() const +{ + return d->depthSize; +} + +/*! + \since 4.2 + + Set the preferred red buffer size to \a size. + + \sa setGreenBufferSize(), setBlueBufferSize(), setAlphaBufferSize() +*/ +void QPlatformWindowFormat::setRedBufferSize(int size) +{ + detach(); + if (size < 0) { + qWarning("QPlatformWindowFormat::setRedBufferSize: Cannot set negative red buffer size %d", size); + return; + } + d->redSize = size; +} + +/*! + \since 4.2 + + Returns the red buffer size. + + \sa setRedBufferSize() +*/ +int QPlatformWindowFormat::redBufferSize() const +{ + return d->redSize; +} + +/*! + \since 4.2 + + Set the preferred green buffer size to \a size. + + \sa setRedBufferSize(), setBlueBufferSize(), setAlphaBufferSize() +*/ +void QPlatformWindowFormat::setGreenBufferSize(int size) +{ + detach(); + if (size < 0) { + qWarning("QPlatformWindowFormat::setGreenBufferSize: Cannot set negative green buffer size %d", size); + return; + } + d->greenSize = size; +} + +/*! + \since 4.2 + + Returns the green buffer size. + + \sa setGreenBufferSize() +*/ +int QPlatformWindowFormat::greenBufferSize() const +{ + return d->greenSize; +} + +/*! + \since 4.2 + + Set the preferred blue buffer size to \a size. + + \sa setRedBufferSize(), setGreenBufferSize(), setAlphaBufferSize() +*/ +void QPlatformWindowFormat::setBlueBufferSize(int size) +{ + detach(); + if (size < 0) { + qWarning("QPlatformWindowFormat::setBlueBufferSize: Cannot set negative blue buffer size %d", size); + return; + } + d->blueSize = size; +} + +/*! + \since 4.2 + + Returns the blue buffer size. + + \sa setBlueBufferSize() +*/ +int QPlatformWindowFormat::blueBufferSize() const +{ + return d->blueSize; +} + +/*! + Set the preferred alpha buffer size to \a size. + This function implicitly enables the alpha channel. + + \sa setRedBufferSize(), setGreenBufferSize(), alphaBufferSize() +*/ +void QPlatformWindowFormat::setAlphaBufferSize(int size) +{ + detach(); + if (size < 0) { + qWarning("QPlatformWindowFormat::setAlphaBufferSize: Cannot set negative alpha buffer size %d", size); + return; + } + d->alphaSize = size; + setAlpha(size > 0); +} + +/*! + Returns the alpha buffer size. + + \sa alpha(), setAlpha(), setAlphaBufferSize() +*/ +int QPlatformWindowFormat::alphaBufferSize() const +{ + return d->alphaSize; +} + +/*! + Set the preferred accumulation buffer size, where \a size is the + bit depth for each RGBA component. + + \sa accum(), setAccum(), accumBufferSize() +*/ +void QPlatformWindowFormat::setAccumBufferSize(int size) +{ + detach(); + if (size < 0) { + qWarning("QPlatformWindowFormat::setAccumBufferSize: Cannot set negative accumulate buffer size %d", size); + return; + } + d->accumSize = size; + setAccum(size > 0); +} + +/*! + Returns the accumulation buffer size. + + \sa setAccumBufferSize(), accum(), setAccum() +*/ +int QPlatformWindowFormat::accumBufferSize() const +{ + return d->accumSize; +} + +/*! + Set the preferred stencil buffer size to \a size. + + \sa stencilBufferSize(), setStencil(), stencil() +*/ +void QPlatformWindowFormat::setStencilBufferSize(int size) +{ + detach(); + if (size < 0) { + qWarning("QPlatformWindowFormat::setStencilBufferSize: Cannot set negative stencil buffer size %d", size); + return; + } + d->stencilSize = size; + setStencil(size > 0); +} + +/*! + Returns the stencil buffer size. + + \sa stencil(), setStencil(), setStencilBufferSize() +*/ +int QPlatformWindowFormat::stencilBufferSize() const +{ + return d->stencilSize; +} + +/*! + Returns the default QPlatformWindowFormat for the application. All QGLWidget + objects that are created use this format unless another format is + specified, e.g. when they are constructed. + + If no special default format has been set using + setDefaultFormat(), the default format is the same as that created + with QPlatformWindowFormat(). + + \sa setDefaultFormat() +*/ + +QPlatformWindowFormat QPlatformWindowFormat::defaultFormat() +{ + return *q_platformwindow_default_format(); +} + +/*! + Sets a new default QPlatformWindowFormat for the application to \a f. For + example, to set single buffering as the default instead of double + buffering, your main() might contain code like this: + \snippet doc/src/snippets/code/src_opengl_qgl.cpp 4 + + \sa defaultFormat() +*/ + +void QPlatformWindowFormat::setDefaultFormat(const QPlatformWindowFormat &f) +{ + *q_platformwindow_default_format() = f; +} + + +/* + Returns the default QPlatformWindowFormat for overlay contexts. + + The default overlay format is: + \list + \i \link setDoubleBuffer() Double buffer:\endlink Disabled. + \i \link setDepth() Depth buffer:\endlink Disabled. + \i \link setRgba() RGBA:\endlink Disabled (i.e., color index enabled). + \i \link setAlpha() Alpha channel:\endlink Disabled. + \i \link setAccum() Accumulator buffer:\endlink Disabled. + \i \link setStencil() Stencil buffer:\endlink Disabled. + \i \link setStereo() Stereo:\endlink Disabled. + \i \link setDirectRendering() Direct rendering:\endlink Enabled. + \i \link setSampleBuffers() Multisample buffers:\endlink Disabled. + \endlist + + \sa setDefaultFormat() +*/ + +//QPlatformWindowFormat QPlatformWindowFormat::defaultOverlayFormat() +//{ +// return *defaultOverlayFormatInstance(); +//} + +///*! +// Sets a new default QPlatformWindowFormat for overlay contexts to \a f. This +// format is used whenever a QGLWidget is created with a format that +// hasOverlay() enabled. + +// For example, to get a double buffered overlay context (if +// available), use code like this: + +// \snippet doc/src/snippets/code/src_opengl_qgl.cpp 5 + +// As usual, you can find out after widget creation whether the +// underlying OpenGL system was able to provide the requested +// specification: + +// \snippet doc/src/snippets/code/src_opengl_qgl.cpp 6 + +// \sa defaultOverlayFormat() +//*/ + +//void QPlatformWindowFormat::setDefaultOverlayFormat(const QPlatformWindowFormat &f) +//{ +// QPlatformWindowFormat *defaultFormat = defaultOverlayFormatInstance(); +// *defaultFormat = f; +// // Make sure the user doesn't request that the overlays themselves +// // have overlays, since it is unlikely that the system supports +// // infinitely many planes... +// defaultFormat->setOverlay(false); +//} + + +/*! + \since 4.8 + + Returns true if all the options of the two QPlatformWindowFormat objects + \a a and \a b are equal; otherwise returns false. + + \relates QPlatformWindowFormat +*/ + +bool operator==(const QPlatformWindowFormat& a, const QPlatformWindowFormat& b) +{ + return (a.d == b.d) || ((int) a.d->opts == (int) b.d->opts + && a.d->alphaSize == b.d->alphaSize + && a.d->accumSize == b.d->accumSize + && a.d->stencilSize == b.d->stencilSize + && a.d->depthSize == b.d->depthSize + && a.d->redSize == b.d->redSize + && a.d->greenSize == b.d->greenSize + && a.d->blueSize == b.d->blueSize + && a.d->numSamples == b.d->numSamples + && a.d->swapInterval == b.d->swapInterval + && a.d->windowApi == b.d->windowApi); +} + + +/*! + \since 4.8 + + Returns false if all the options of the two QPlatformWindowFormat objects + \a a and \a b are equal; otherwise returns true. + + \relates QPlatformWindowFormat +*/ + +bool operator!=(const QPlatformWindowFormat& a, const QPlatformWindowFormat& b) +{ + return !(a == b); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QPlatformWindowFormat &f) +{ + const QPlatformWindowFormatPrivate * const d = f.d; + + dbg.nospace() << "QGLFormat(" + << "options " << d->opts + << ", depthBufferSize " << d->depthSize + << ", accumBufferSize " << d->accumSize + << ", stencilBufferSize " << d->stencilSize + << ", redBufferSize " << d->redSize + << ", greenBufferSize " << d->greenSize + << ", blueBufferSize " << d->blueSize + << ", alphaBufferSize " << d->alphaSize + << ", samples " << d->numSamples + << ", swapInterval " << d->swapInterval + << ')'; + + return dbg.space(); +} +#endif diff --git a/src/gui/kernel/qplatformwindowformat_qpa.h b/src/gui/kernel/qplatformwindowformat_qpa.h new file mode 100644 index 0000000..e7dde79 --- /dev/null +++ b/src/gui/kernel/qplatformwindowformat_qpa.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QPLATFORMWINDOWFORMAT_QPA_H +#define QPLATFORMWINDOWFORMAT_QPA_H + +#include <QtGui/QPlatformWindow> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QPlatformWindowFormatPrivate; + +class Q_GUI_EXPORT QPlatformWindowFormat +{ +public: + enum FormatOption { + DoubleBuffer = 0x0001, + DepthBuffer = 0x0002, + Rgba = 0x0004, + AlphaChannel = 0x0008, + AccumBuffer = 0x0010, + StencilBuffer = 0x0020, + StereoBuffers = 0x0040, + DirectRendering = 0x0080, + HasOverlay = 0x0100, + SampleBuffers = 0x0200, + DeprecatedFunctions = 0x0400, + HasWindowSurface = 0x0800, + SingleBuffer = DoubleBuffer << 16, + NoDepthBuffer = DepthBuffer << 16, + ColorIndex = Rgba << 16, + NoAlphaChannel = AlphaChannel << 16, + NoAccumBuffer = AccumBuffer << 16, + NoStencilBuffer = StencilBuffer << 16, + NoStereoBuffers = StereoBuffers << 16, + IndirectRendering = DirectRendering << 16, + NoOverlay = HasOverlay << 16, + NoSampleBuffers = SampleBuffers << 16, + NoDeprecatedFunctions = DeprecatedFunctions << 16, + NoWindowSurface = HasWindowSurface << 16 + + }; + Q_DECLARE_FLAGS(FormatOptions, FormatOption) + + enum WindowApi { + Raster, + OpenGL, + OpenVG + }; + + QPlatformWindowFormat(); + QPlatformWindowFormat(FormatOptions options); + QPlatformWindowFormat(const QPlatformWindowFormat &other); + QPlatformWindowFormat &operator=(const QPlatformWindowFormat &other); + ~QPlatformWindowFormat(); + + void setDepthBufferSize(int size); + int depthBufferSize() const; + + void setAccumBufferSize(int size); + int accumBufferSize() const; + + void setRedBufferSize(int size); + int redBufferSize() const; + + void setGreenBufferSize(int size); + int greenBufferSize() const; + + void setBlueBufferSize(int size); + int blueBufferSize() const; + + void setAlphaBufferSize(int size); + int alphaBufferSize() const; + + void setStencilBufferSize(int size); + int stencilBufferSize() const; + + void setSampleBuffers(bool enable); + bool sampleBuffers() const; + + void setSamples(int numSamples); + int samples() const; + + void setSwapInterval(int interval); + int swapInterval() const; + + void setWindowApi(QPlatformWindowFormat::WindowApi api); + WindowApi windowApi() const; + + void setSharedContext(QPlatformGLContext *context); + QPlatformGLContext *sharedGLContext() const; + + bool doubleBuffer() const; + void setDoubleBuffer(bool enable); + bool depth() const; + void setDepth(bool enable); + bool rgba() const; + void setRgba(bool enable); + bool alpha() const; + void setAlpha(bool enable); + bool accum() const; + void setAccum(bool enable); + bool stencil() const; + void setStencil(bool enable); + bool stereo() const; + void setStereo(bool enable); + bool directRendering() const; + void setDirectRendering(bool enable); + bool hasWindowSurface() const; + void setWindowSurface(bool enable); + + void setOption(QPlatformWindowFormat::FormatOptions opt); + bool testOption(QPlatformWindowFormat::FormatOptions opt) const; + + static QPlatformWindowFormat defaultFormat(); + static void setDefaultFormat(const QPlatformWindowFormat& f); + +private: + QPlatformWindowFormatPrivate *d; + + void detach(); + + friend Q_GUI_EXPORT bool operator==(const QPlatformWindowFormat&, const QPlatformWindowFormat&); + friend Q_GUI_EXPORT bool operator!=(const QPlatformWindowFormat&, const QPlatformWindowFormat&); +#ifndef QT_NO_DEBUG_STREAM + friend Q_GUI_EXPORT QDebug operator<<(QDebug, const QPlatformWindowFormat &); +#endif +}; + +Q_GUI_EXPORT bool operator==(const QPlatformWindowFormat&, const QPlatformWindowFormat&); +Q_GUI_EXPORT bool operator!=(const QPlatformWindowFormat&, const QPlatformWindowFormat&); + +#ifndef QT_NO_DEBUG_STREAM +Q_OPENGL_EXPORT QDebug operator<<(QDebug, const QPlatformWindowFormat &); +#endif + +Q_DECLARE_OPERATORS_FOR_FLAGS(QPlatformWindowFormat::FormatOptions) + +inline bool QPlatformWindowFormat::doubleBuffer() const +{ + return testOption(QPlatformWindowFormat::DoubleBuffer); +} + +inline bool QPlatformWindowFormat::depth() const +{ + return testOption(QPlatformWindowFormat::DepthBuffer); +} + +inline bool QPlatformWindowFormat::rgba() const +{ + return testOption(QPlatformWindowFormat::Rgba); +} + +inline bool QPlatformWindowFormat::alpha() const +{ + return testOption(QPlatformWindowFormat::AlphaChannel); +} + +inline bool QPlatformWindowFormat::accum() const +{ + return testOption(QPlatformWindowFormat::AccumBuffer); +} + +inline bool QPlatformWindowFormat::stencil() const +{ + return testOption(QPlatformWindowFormat::StencilBuffer); +} + +inline bool QPlatformWindowFormat::stereo() const +{ + return testOption(QPlatformWindowFormat::StereoBuffers); +} + +inline bool QPlatformWindowFormat::directRendering() const +{ + return testOption(QPlatformWindowFormat::DirectRendering); +} + +inline bool QPlatformWindowFormat::hasWindowSurface() const +{ + return testOption(QPlatformWindowFormat::HasWindowSurface); +} + +inline bool QPlatformWindowFormat::sampleBuffers() const +{ + return testOption(QPlatformWindowFormat::SampleBuffers); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QPLATFORMWINDOWFORMAT_QPA_H diff --git a/src/gui/kernel/qsessionmanager_qpa.cpp b/src/gui/kernel/qsessionmanager_qpa.cpp new file mode 100644 index 0000000..bea0e12 --- /dev/null +++ b/src/gui/kernel/qsessionmanager_qpa.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qsessionmanager.h> + +#include <private/qobject_p.h> +#include <qapplication.h> + +#ifndef QT_NO_SESSIONMANAGER + +QT_BEGIN_NAMESPACE + +class QSessionManagerPrivate : public QObjectPrivate +{ +public: + QSessionManagerPrivate(QSessionManager *m, const QString &id, + const QString &key); + + QStringList restartCommand; + QStringList discardCommand; + const QString sessionId; + const QString sessionKey; + QSessionManager::RestartHint restartHint; +}; + +QSessionManagerPrivate::QSessionManagerPrivate(QSessionManager*, + const QString &id, + const QString &key) + : QObjectPrivate(), sessionId(id), sessionKey(key) +{ +} + +QSessionManager::QSessionManager(QApplication *app, QString &id, QString &key) + : QObject(*(new QSessionManagerPrivate(this, id, key)), app) +{ + Q_D(QSessionManager); + d->restartHint = RestartIfRunning; +} + +QSessionManager::~QSessionManager() +{ +} + +QString QSessionManager::sessionId() const +{ + Q_D(const QSessionManager); + return d->sessionId; +} + +QString QSessionManager::sessionKey() const +{ + Q_D(const QSessionManager); + return d->sessionKey; +} + + +bool QSessionManager::allowsInteraction() +{ + return false; +} + +bool QSessionManager::allowsErrorInteraction() +{ + return false; +} + +void QSessionManager::release() +{ +} + +void QSessionManager::cancel() +{ +} + +void QSessionManager::setRestartHint(QSessionManager::RestartHint hint) +{ + Q_D(QSessionManager); + d->restartHint = hint; +} + +QSessionManager::RestartHint QSessionManager::restartHint() const +{ + Q_D(const QSessionManager); + return d->restartHint; +} + +void QSessionManager::setRestartCommand(const QStringList &command) +{ + Q_D(QSessionManager); + d->restartCommand = command; +} + +QStringList QSessionManager::restartCommand() const +{ + Q_D(const QSessionManager); + return d->restartCommand; +} + +void QSessionManager::setDiscardCommand(const QStringList &command) +{ + Q_D(QSessionManager); + d->discardCommand = command; +} + +QStringList QSessionManager::discardCommand() const +{ + Q_D(const QSessionManager); + return d->discardCommand; +} + +void QSessionManager::setManagerProperty(const QString &name, + const QString &value) +{ + Q_UNUSED(name); + Q_UNUSED(value); +} + +void QSessionManager::setManagerProperty(const QString &name, + const QStringList &value) +{ + Q_UNUSED(name); + Q_UNUSED(value); +} + +bool QSessionManager::isPhase2() const +{ + return false; +} + +void QSessionManager::requestPhase2() +{ +} + +QT_END_NAMESPACE + +#endif // QT_NO_SESSIONMANAGER diff --git a/src/gui/kernel/qsessionmanager_qws.cpp b/src/gui/kernel/qsessionmanager_qws.cpp index efe688e..e437635 100644 --- a/src/gui/kernel/qsessionmanager_qws.cpp +++ b/src/gui/kernel/qsessionmanager_qws.cpp @@ -43,6 +43,9 @@ #ifndef QT_NO_SESSIONMANAGER +#include <qapplication.h> +#include <private/qobject_p.h> + QT_BEGIN_NAMESPACE class QSessionManagerPrivate : public QObjectPrivate diff --git a/src/gui/kernel/qsizepolicy.h b/src/gui/kernel/qsizepolicy.h index f06246b..bd92a91 100644 --- a/src/gui/kernel/qsizepolicy.h +++ b/src/gui/kernel/qsizepolicy.h @@ -62,12 +62,12 @@ private: HSize = 4, HMask = 0x0f, VMask = HMask << HSize, - CTShift = 9, - CTSize = 5, - WFHShift = CTShift + CTSize, - CTMask = ((0x1 << CTSize) - 1) << CTShift, - UnusedShift = CTShift + CTSize, - UnusedSize = 2 + CTShift = 9, + CTSize = 5, + CTMask = ((0x1 << CTSize) - 1) << CTShift, + WFHShift = CTShift + CTSize, + UnusedShift = WFHShift + 1, + UnusedSize = 1 }; public: @@ -134,6 +134,8 @@ public: void setHeightForWidth(bool b) { data = b ? (data | (1 << 2*HSize)) : (data & ~(1 << 2*HSize)); } bool hasHeightForWidth() const { return data & (1 << 2*HSize); } + void setWidthForHeight(bool b) { data = b ? (data | (1 << (WFHShift))) : (data & ~(1 << (WFHShift))); } + bool hasWidthForHeight() const { return data & (1 << (WFHShift)); } bool operator==(const QSizePolicy& s) const { return data == s.data; } bool operator!=(const QSizePolicy& s) const { return data != s.data; } @@ -200,15 +202,18 @@ private: QSizePolicy(int i) : data(i) { } quint32 data; -/* use bit flags instead, keep it here for improved readability for now +/* Qt5: Use bit flags instead, keep it here for improved readability for now. + We can maybe change it for Qt4, but we'd have to be careful, since the behaviour + is implementation defined. It usually varies between little- and big-endian compilers, but + it might also not vary. quint32 horzPolicy : 4; quint32 vertPolicy : 4; quint32 hfw : 1; quint32 ctype : 5; quint32 wfh : 1; quint32 padding : 1; // we cannot use the highest bit - quint32 horStretch : 8; quint32 verStretch : 8; + quint32 horStretch : 8; */ }; diff --git a/src/gui/kernel/qsizepolicy.qdoc b/src/gui/kernel/qsizepolicy.qdoc index 08a32be..7002ed3 100644 --- a/src/gui/kernel/qsizepolicy.qdoc +++ b/src/gui/kernel/qsizepolicy.qdoc @@ -249,7 +249,7 @@ Sets the flag determining whether the widget's preferred height depends on its width, to \a dependent. - \sa hasHeightForWidth() + \sa hasHeightForWidth(), setWidthForHeight() */ /*! @@ -262,6 +262,30 @@ */ /*! + \fn void QSizePolicy::setWidthForHeight(bool dependent) + \since 4.8 + + Sets the flag determining whether the widget's width + depends on its height, to \a dependent. + + This is only supported for QGraphicsLayout's subclasses. + It is not possible to have a layout with both height-for-width + and width-for-height constraints at the same time. + + \sa hasWidthForHeight(), setHeightForWidth() +*/ + +/*! + \fn bool QSizePolicy::hasWidthForHeight() const + \since 4.8 + + Returns true if the widget's width depends on its + height; otherwise returns false. + + \sa setWidthForHeight() +*/ + +/*! \fn bool QSizePolicy::operator==(const QSizePolicy &other) const Returns true if this policy is equal to \a other; otherwise diff --git a/src/gui/kernel/qsoftkeymanager.cpp b/src/gui/kernel/qsoftkeymanager.cpp index 80e6ec6..9caa37e 100644 --- a/src/gui/kernel/qsoftkeymanager.cpp +++ b/src/gui/kernel/qsoftkeymanager.cpp @@ -48,16 +48,13 @@ #ifdef Q_WS_S60 #include "private/qsoftkeymanager_s60_p.h" -#endif - -#if defined(Q_WS_S60) && !defined(SYMBIAN_VERSION_9_4) && !defined(SYMBIAN_VERSION_9_3) && !defined(SYMBIAN_VERSION_9_2) #include "private/qt_s60_p.h" #endif #ifndef QT_NO_SOFTKEYMANAGER QT_BEGIN_NAMESPACE -QSoftKeyManager *QSoftKeyManagerPrivate::self = 0; +QScopedPointer<QSoftKeyManager> QSoftKeyManagerPrivate::self(0); QString QSoftKeyManager::standardSoftKeyText(StandardSoftKey standardKey) { @@ -88,9 +85,9 @@ QString QSoftKeyManager::standardSoftKeyText(StandardSoftKey standardKey) QSoftKeyManager *QSoftKeyManager::instance() { if (!QSoftKeyManagerPrivate::self) - QSoftKeyManagerPrivate::self = new QSoftKeyManager; + QSoftKeyManagerPrivate::self.reset(new QSoftKeyManager); - return QSoftKeyManagerPrivate::self; + return QSoftKeyManagerPrivate::self.data(); } QSoftKeyManager::QSoftKeyManager() : @@ -203,6 +200,11 @@ void QSoftKeyManager::sendKeyEvent() void QSoftKeyManager::updateSoftKeys() { +#ifdef Q_WS_S60 + // Do not adjust softkeys if application is not the topmost one + if (S60->wsSession().GetFocusWindowGroup() != S60->windowGroup().WindowGroupId()) + return; +#endif QSoftKeyManager::instance()->d_func()->pendingUpdate = true; QEvent *event = new QEvent(QEvent::UpdateSoftKeys); QApplication::postEvent(QSoftKeyManager::instance(), event); diff --git a/src/gui/kernel/qsoftkeymanager_common_p.h b/src/gui/kernel/qsoftkeymanager_common_p.h index 81f551a..9a11eec 100644 --- a/src/gui/kernel/qsoftkeymanager_common_p.h +++ b/src/gui/kernel/qsoftkeymanager_common_p.h @@ -67,7 +67,7 @@ public: virtual void updateSoftKeys_sys() {}; protected: - static QSoftKeyManager *self; + static QScopedPointer<QSoftKeyManager> self; QHash<QAction*, Qt::Key> keyedActions; QMultiHash<int, QAction*> requestedSoftKeyActions; QWidget *initialSoftKeySource; diff --git a/src/gui/kernel/qsoftkeymanager_s60.cpp b/src/gui/kernel/qsoftkeymanager_s60.cpp index 9212a4b..999fccc 100644 --- a/src/gui/kernel/qsoftkeymanager_s60.cpp +++ b/src/gui/kernel/qsoftkeymanager_s60.cpp @@ -303,12 +303,6 @@ bool QSoftKeyManagerPrivateS60::setSoftkeyImage(CEikButtonGroupContainer *cba, EikSoftkeyImage::SetImage(cba, *myimage, left); // Takes myimage ownership cbaHasImage[position] = true; ret = true; - } else { - // Restore softkey to text based - if (cbaHasImage[position]) { - EikSoftkeyImage::SetLabel(cba, left); - cbaHasImage[position] = false; - } } } return ret; @@ -319,7 +313,7 @@ bool QSoftKeyManagerPrivateS60::setSoftkey(CEikButtonGroupContainer &cba, { QAction *action = highestPrioritySoftkey(role); if (action) { - setSoftkeyImage(&cba, *action, position); + bool hasImage = setSoftkeyImage(&cba, *action, position); QString text = softkeyText(*action); TPtrC nativeText = qt_QString2TPtrC(text); int command = S60_COMMAND_START + position; @@ -328,6 +322,11 @@ bool QSoftKeyManagerPrivateS60::setSoftkey(CEikButtonGroupContainer &cba, command = softKeyCommandActions.value(action); #endif setNativeSoftkey(cba, position, command, nativeText); + if (!hasImage && cbaHasImage[position]) { + EikSoftkeyImage::SetLabel(&cba, (position == LSK_POSITION)); + cbaHasImage[position] = false; + } + const bool dimmed = !action->isEnabled() && !QSoftKeyManager::isForceEnabledInSofkeys(action); cba.DimCommand(command, dimmed); realSoftKeyActions.insert(command, action); @@ -338,7 +337,18 @@ bool QSoftKeyManagerPrivateS60::setSoftkey(CEikButtonGroupContainer &cba, bool QSoftKeyManagerPrivateS60::setLeftSoftkey(CEikButtonGroupContainer &cba) { - return setSoftkey(cba, QAction::PositiveSoftKey, LSK_POSITION); + if (!setSoftkey(cba, QAction::PositiveSoftKey, LSK_POSITION)) { + if (cbaHasImage[LSK_POSITION]) { + // Clear any residual icon if LSK has no action. A real softkey + // is needed for SetLabel command to work, so do a temporary dummy + setNativeSoftkey(cba, LSK_POSITION, EAknSoftkeyExit, KNullDesC); + EikSoftkeyImage::SetLabel(&cba, true); + setNativeSoftkey(cba, LSK_POSITION, EAknSoftkeyEmpty, KNullDesC); + cbaHasImage[LSK_POSITION] = false; + } + return false; + } + return true; } bool QSoftKeyManagerPrivateS60::setMiddleSoftkey(CEikButtonGroupContainer &cba) @@ -357,16 +367,26 @@ bool QSoftKeyManagerPrivateS60::setRightSoftkey(CEikButtonGroupContainer &cba) if (windowType != Qt::Dialog && windowType != Qt::Popup) { QString text(QSoftKeyManager::tr("Exit")); TPtrC nativeText = qt_QString2TPtrC(text); + setNativeSoftkey(cba, RSK_POSITION, EAknSoftkeyExit, nativeText); if (cbaHasImage[RSK_POSITION]) { EikSoftkeyImage::SetLabel(&cba, false); cbaHasImage[RSK_POSITION] = false; } - setNativeSoftkey(cba, RSK_POSITION, EAknSoftkeyExit, nativeText); cba.DimCommand(EAknSoftkeyExit, false); return true; + } else { + if (cbaHasImage[RSK_POSITION]) { + // Clear any residual icon if RSK has no action. A real softkey + // is needed for SetLabel command to work, so do a temporary dummy + setNativeSoftkey(cba, RSK_POSITION, EAknSoftkeyExit, KNullDesC); + EikSoftkeyImage::SetLabel(&cba, false); + setNativeSoftkey(cba, RSK_POSITION, EAknSoftkeyEmpty, KNullDesC); + cbaHasImage[RSK_POSITION] = false; + } + return false; } } - return false; + return true; } void QSoftKeyManagerPrivateS60::setSoftkeys(CEikButtonGroupContainer &cba) diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm index 8e1159e..97ed7f7 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm @@ -79,6 +79,7 @@ #include <qdesktopwidget.h> #include <qevent.h> #include <qpixmapcache.h> +#include <qvarlengtharray.h> #include <private/qevent_p.h> #include <private/qt_cocoa_helpers_mac_p.h> #include <private/qt_mac_p.h> @@ -88,9 +89,16 @@ #include <private/qcocoaview_mac_p.h> #include <private/qkeymapper_p.h> #include <private/qwidget_p.h> +#include <private/qcocoawindow_mac_p.h> QT_BEGIN_NAMESPACE +#ifdef QT_MAC_USE_COCOA +// Cmd + left mousebutton should produce a right button +// press (mainly for mac users with one-button mice): +static bool qt_leftButtonIsRightButton = false; +#endif + Q_GLOBAL_STATIC(QMacWindowFader, macwindowFader); QMacWindowFader::QMacWindowFader() @@ -140,6 +148,9 @@ void QMacWindowFader::performFade() extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // qapplication.cpp; extern QWidget * mac_mouse_grabber; extern QWidget *qt_button_down; //qapplication_mac.cpp +extern QPointer<QWidget> qt_last_mouse_receiver; +extern OSViewRef qt_mac_effectiveview_for(const QWidget *w); +extern void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse); // qcursor_mac.mm void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds) { @@ -166,6 +177,70 @@ void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds) } } } +struct dndenum_mapper +{ + NSDragOperation mac_code; + Qt::DropAction qt_code; + bool Qt2Mac; +}; + +#if defined(QT_MAC_USE_COCOA) && defined(__OBJC__) + +static dndenum_mapper dnd_enums[] = { + { NSDragOperationLink, Qt::LinkAction, true }, + { NSDragOperationMove, Qt::MoveAction, true }, + { NSDragOperationCopy, Qt::CopyAction, true }, + { NSDragOperationGeneric, Qt::CopyAction, false }, + { NSDragOperationEvery, Qt::ActionMask, false }, + { NSDragOperationNone, Qt::IgnoreAction, false } +}; + +NSDragOperation qt_mac_mapDropAction(Qt::DropAction action) +{ + for (int i=0; dnd_enums[i].qt_code; i++) { + if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) { + return dnd_enums[i].mac_code; + } + } + return NSDragOperationNone; +} + +NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions) +{ + NSDragOperation nsActions = NSDragOperationNone; + for (int i=0; dnd_enums[i].qt_code; i++) { + if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code)) + nsActions |= dnd_enums[i].mac_code; + } + return nsActions; +} + +Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions) +{ + Qt::DropAction action = Qt::IgnoreAction; + for (int i=0; dnd_enums[i].mac_code; i++) { + if (nsActions & dnd_enums[i].mac_code) + return dnd_enums[i].qt_code; + } + return action; +} + +Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions) +{ + Qt::DropActions actions = Qt::IgnoreAction; + for (int i=0; dnd_enums[i].mac_code; i++) { + if (nsActions & dnd_enums[i].mac_code) + actions |= dnd_enums[i].qt_code; + } + return actions; +} + +Q_GLOBAL_STATIC(DnDParams, currentDnDParameters); +DnDParams *macCurrentDnDParameters() +{ + return currentDnDParameters(); +} +#endif bool macWindowIsTextured( void * /*OSWindowRef*/ window ) { @@ -300,7 +375,7 @@ bool qt_mac_checkForNativeSizeGrip(const QWidget *widget) HIViewFindByID(HIViewGetRoot(HIViewGetWindow(HIViewRef(widget->winId()))), kHIViewWindowGrowBoxID, &nativeSizeGrip); return (nativeSizeGrip != 0); #else - return [[reinterpret_cast<NSView *>(widget->winId()) window] showsResizeIndicator]; + return [[reinterpret_cast<NSView *>(widget->effectiveWinId()) window] showsResizeIndicator]; #endif } struct qt_mac_enum_mapper @@ -470,7 +545,6 @@ void qt_dispatchTabletProximityEvent(const ::TabletProximityRec &proxRec) qt_sendSpontaneousEvent(qApp, &qtabletProximity); } -#ifdef QT_MAC_USE_COCOA // Use this method to keep all the information in the TextSegment. As long as it is ordered // we are in OK shape, and we can influence that ourselves. struct KeyPair @@ -494,69 +568,111 @@ bool operator<(QChar qchar, const KeyPair &entry) return qchar < entry.cocoaKey; } +bool operator<(const Qt::Key &key, const KeyPair &entry) +{ + return key < entry.qtKey; +} + +bool operator<(const KeyPair &entry, const Qt::Key &key) +{ + return entry.qtKey < key; +} + +static bool qtKey2CocoaKeySortLessThan(const KeyPair &entry1, const KeyPair &entry2) +{ + return entry1.qtKey < entry2.qtKey; +} + +static const int NumEntries = 59; +static const KeyPair entries[NumEntries] = { + { NSEnterCharacter, Qt::Key_Enter }, + { NSBackspaceCharacter, Qt::Key_Backspace }, + { NSTabCharacter, Qt::Key_Tab }, + { NSNewlineCharacter, Qt::Key_Return }, + { NSCarriageReturnCharacter, Qt::Key_Return }, + { NSBackTabCharacter, Qt::Key_Backtab }, + { kEscapeCharCode, Qt::Key_Escape }, + // Cocoa sends us delete when pressing backspace! + // (NB when we reverse this list in qtKey2CocoaKey, there + // will be two indices of Qt::Key_Backspace. But is seems to work + // ok for menu shortcuts (which uses that function): + { NSDeleteCharacter, Qt::Key_Backspace }, + { NSUpArrowFunctionKey, Qt::Key_Up }, + { NSDownArrowFunctionKey, Qt::Key_Down }, + { NSLeftArrowFunctionKey, Qt::Key_Left }, + { NSRightArrowFunctionKey, Qt::Key_Right }, + { NSF1FunctionKey, Qt::Key_F1 }, + { NSF2FunctionKey, Qt::Key_F2 }, + { NSF3FunctionKey, Qt::Key_F3 }, + { NSF4FunctionKey, Qt::Key_F4 }, + { NSF5FunctionKey, Qt::Key_F5 }, + { NSF6FunctionKey, Qt::Key_F6 }, + { NSF7FunctionKey, Qt::Key_F7 }, + { NSF8FunctionKey, Qt::Key_F8 }, + { NSF9FunctionKey, Qt::Key_F8 }, + { NSF10FunctionKey, Qt::Key_F10 }, + { NSF11FunctionKey, Qt::Key_F11 }, + { NSF12FunctionKey, Qt::Key_F12 }, + { NSF13FunctionKey, Qt::Key_F13 }, + { NSF14FunctionKey, Qt::Key_F14 }, + { NSF15FunctionKey, Qt::Key_F15 }, + { NSF16FunctionKey, Qt::Key_F16 }, + { NSF17FunctionKey, Qt::Key_F17 }, + { NSF18FunctionKey, Qt::Key_F18 }, + { NSF19FunctionKey, Qt::Key_F19 }, + { NSF20FunctionKey, Qt::Key_F20 }, + { NSF21FunctionKey, Qt::Key_F21 }, + { NSF22FunctionKey, Qt::Key_F22 }, + { NSF23FunctionKey, Qt::Key_F23 }, + { NSF24FunctionKey, Qt::Key_F24 }, + { NSF25FunctionKey, Qt::Key_F25 }, + { NSF26FunctionKey, Qt::Key_F26 }, + { NSF27FunctionKey, Qt::Key_F27 }, + { NSF28FunctionKey, Qt::Key_F28 }, + { NSF29FunctionKey, Qt::Key_F29 }, + { NSF30FunctionKey, Qt::Key_F30 }, + { NSF31FunctionKey, Qt::Key_F31 }, + { NSF32FunctionKey, Qt::Key_F32 }, + { NSF33FunctionKey, Qt::Key_F33 }, + { NSF34FunctionKey, Qt::Key_F34 }, + { NSF35FunctionKey, Qt::Key_F35 }, + { NSInsertFunctionKey, Qt::Key_Insert }, + { NSDeleteFunctionKey, Qt::Key_Delete }, + { NSHomeFunctionKey, Qt::Key_Home }, + { NSEndFunctionKey, Qt::Key_End }, + { NSPageUpFunctionKey, Qt::Key_PageUp }, + { NSPageDownFunctionKey, Qt::Key_PageDown }, + { NSPrintScreenFunctionKey, Qt::Key_Print }, + { NSScrollLockFunctionKey, Qt::Key_ScrollLock }, + { NSPauseFunctionKey, Qt::Key_Pause }, + { NSSysReqFunctionKey, Qt::Key_SysReq }, + { NSMenuFunctionKey, Qt::Key_Menu }, + { NSHelpFunctionKey, Qt::Key_Help }, +}; +static const KeyPair * const end = entries + NumEntries; + +QChar qtKey2CocoaKey(Qt::Key key) +{ + // The first time this function is called, create a reverse + // looup table sorted on Qt Key rather than Cocoa key: + static QVector<KeyPair> rev_entries(NumEntries); + static bool mustInit = true; + if (mustInit){ + mustInit = false; + for (int i=0; i<NumEntries; ++i) + rev_entries[i] = entries[i]; + qSort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan); + } + const QVector<KeyPair>::iterator i + = qBinaryFind(rev_entries.begin(), rev_entries.end(), key); + if (i == rev_entries.end()) + return QChar(); + return i->cocoaKey; +} + +#ifdef QT_MAC_USE_COCOA static Qt::Key cocoaKey2QtKey(QChar keyCode) { - static const int NumEntries = 57; - static const KeyPair entries[NumEntries] = { - { NSEnterCharacter, Qt::Key_Enter }, - { NSTabCharacter, Qt::Key_Tab }, - { NSCarriageReturnCharacter, Qt::Key_Return }, - { NSBackTabCharacter, Qt::Key_Backtab }, - { kEscapeCharCode, Qt::Key_Escape }, - { NSDeleteCharacter, Qt::Key_Backspace }, - { NSUpArrowFunctionKey, Qt::Key_Up }, - { NSDownArrowFunctionKey, Qt::Key_Down }, - { NSLeftArrowFunctionKey, Qt::Key_Left }, - { NSRightArrowFunctionKey, Qt::Key_Right }, - { NSF1FunctionKey, Qt::Key_F1 }, - { NSF2FunctionKey, Qt::Key_F2 }, - { NSF3FunctionKey, Qt::Key_F3 }, - { NSF4FunctionKey, Qt::Key_F4 }, - { NSF5FunctionKey, Qt::Key_F5 }, - { NSF6FunctionKey, Qt::Key_F6 }, - { NSF7FunctionKey, Qt::Key_F7 }, - { NSF8FunctionKey, Qt::Key_F8 }, - { NSF9FunctionKey, Qt::Key_F8 }, - { NSF10FunctionKey, Qt::Key_F10 }, - { NSF11FunctionKey, Qt::Key_F11 }, - { NSF12FunctionKey, Qt::Key_F12 }, - { NSF13FunctionKey, Qt::Key_F13 }, - { NSF14FunctionKey, Qt::Key_F14 }, - { NSF15FunctionKey, Qt::Key_F15 }, - { NSF16FunctionKey, Qt::Key_F16 }, - { NSF17FunctionKey, Qt::Key_F17 }, - { NSF18FunctionKey, Qt::Key_F18 }, - { NSF19FunctionKey, Qt::Key_F19 }, - { NSF20FunctionKey, Qt::Key_F20 }, - { NSF21FunctionKey, Qt::Key_F21 }, - { NSF22FunctionKey, Qt::Key_F22 }, - { NSF23FunctionKey, Qt::Key_F23 }, - { NSF24FunctionKey, Qt::Key_F24 }, - { NSF25FunctionKey, Qt::Key_F25 }, - { NSF26FunctionKey, Qt::Key_F26 }, - { NSF27FunctionKey, Qt::Key_F27 }, - { NSF28FunctionKey, Qt::Key_F28 }, - { NSF29FunctionKey, Qt::Key_F29 }, - { NSF30FunctionKey, Qt::Key_F30 }, - { NSF31FunctionKey, Qt::Key_F31 }, - { NSF32FunctionKey, Qt::Key_F32 }, - { NSF33FunctionKey, Qt::Key_F33 }, - { NSF34FunctionKey, Qt::Key_F34 }, - { NSF35FunctionKey, Qt::Key_F35 }, - { NSInsertFunctionKey, Qt::Key_Insert }, - { NSDeleteFunctionKey, Qt::Key_Delete }, - { NSHomeFunctionKey, Qt::Key_Home }, - { NSEndFunctionKey, Qt::Key_End }, - { NSPageUpFunctionKey, Qt::Key_PageUp }, - { NSPageDownFunctionKey, Qt::Key_PageDown }, - { NSPrintScreenFunctionKey, Qt::Key_Print }, - { NSScrollLockFunctionKey, Qt::Key_ScrollLock }, - { NSPauseFunctionKey, Qt::Key_Pause }, - { NSSysReqFunctionKey, Qt::Key_SysReq }, - { NSMenuFunctionKey, Qt::Key_Menu }, - { NSHelpFunctionKey, Qt::Key_Help }, - }; - static const KeyPair * const end = entries + NumEntries; const KeyPair *i = qBinaryFind(entries, end, keyCode); if (i == end) return Qt::Key(keyCode.unicode()); @@ -579,6 +695,27 @@ Qt::KeyboardModifiers qt_cocoaModifiers2QtModifiers(ulong modifierFlags) return qtMods; } +NSString *qt_mac_removePrivateUnicode(NSString* string) +{ + int len = [string length]; + if (len) { + QVarLengthArray <unichar, 10> characters(len); + bool changed = false; + for (int i = 0; i<len; i++) { + characters[i] = [string characterAtIndex:i]; + // check if they belong to key codes in private unicode range + // currently we need to handle only the NSDeleteFunctionKey + if (characters[i] == NSDeleteFunctionKey) { + characters[i] = NSDeleteCharacter; + changed = true; + } + } + if (changed) + return [NSString stringWithCharacters:characters.data() length:len]; + } + return string; +} + Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations) { Qt::KeyboardModifiers qtMods =Qt::NoModifier; @@ -672,7 +809,6 @@ Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum) return Qt::NoButton; } -// Helper to share code between QCocoaWindow and QCocoaView bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent) { #ifndef QT_MAC_USE_COCOA @@ -684,6 +820,7 @@ bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEve EventRef key_event = static_cast<EventRef>(const_cast<void *>([event eventRef])); Q_ASSERT(key_event); unsigned int info = 0; + if ([event type] == NSKeyDown) { NSString *characters = [event characters]; if ([characters length]) { @@ -693,19 +830,12 @@ bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEve } } - // Redirect keys to alien widgets. - if (widgetToGetEvent->testAttribute(Qt::WA_NativeWindow) == false) { - widgetToGetEvent = qApp->focusWidget(); - } - - if (widgetToGetEvent == 0) - return false; - if (qt_mac_sendMacEventToWidget(widgetToGetEvent, key_event)) return true; if (mustUseCocoaKeyEvent()) return qt_dispatchKeyEventWithCocoa(keyEvent, widgetToGetEvent); + bool consumed = qt_keymapper_private()->translateKeyEvent(widgetToGetEvent, 0, key_event, &info, true); return consumed && (info != 0); #endif @@ -729,7 +859,6 @@ void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget #endif } - QPointF flipPoint(const NSPoint &p) { return QPointF(p.x, flipYCoordinate(p.y)); @@ -745,21 +874,15 @@ NSPoint flipPoint(const QPointF &p) return NSMakePoint(p.x(), flipYCoordinate(p.y())); } -void qt_mac_dispatchNCMouseMessage(void * /* NSWindow* */eventWindow, void * /* NSEvent* */mouseEvent, - QWidget *widgetToGetEvent, bool &leftButtonIsRightButton) +#if QT_MAC_USE_COCOA && __OBJC__ + +void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event) { -#ifndef QT_MAC_USE_COCOA - Q_UNUSED(eventWindow); - Q_UNUSED(mouseEvent); - Q_UNUSED(widgetToGetEvent); - Q_UNUSED(leftButtonIsRightButton); -#else + QWidget *widgetToGetEvent = [window QT_MANGLE_NAMESPACE(qt_qwidget)]; if (widgetToGetEvent == 0) return; - NSWindow *window = static_cast<NSWindow *>(eventWindow); - NSEvent *event = static_cast<NSEvent *>(mouseEvent); - NSEventType evtType = [event type]; + NSEventType evtType = [event type]; QPoint qlocalPoint; QPoint qglobalPoint; bool processThisEvent = false; @@ -877,12 +1000,12 @@ void qt_mac_dispatchNCMouseMessage(void * /* NSWindow* */eventWindow, void * /* : QEvent::MouseButtonDblClick; if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) { button = Qt::RightButton; - leftButtonIsRightButton = true; + qt_leftButtonIsRightButton = true; } } else if (eventType == QEvent::NonClientAreaMouseButtonRelease || eventType == QEvent::MouseButtonRelease) { - if (button == Qt::LeftButton && leftButtonIsRightButton) { + if (button == Qt::LeftButton && qt_leftButtonIsRightButton) { button = Qt::RightButton; - leftButtonIsRightButton = false; + qt_leftButtonIsRightButton = false; } } @@ -902,155 +1025,265 @@ void qt_mac_dispatchNCMouseMessage(void * /* NSWindow* */eventWindow, void * /* // However we might need to unset it if the event is Release. if (eventType == QEvent::MouseButtonRelease) qt_button_down = 0; -#endif } -bool qt_mac_handleMouseEvent(void * /* NSView * */view, void * /* NSEvent * */event, QEvent::Type eventType, Qt::MouseButton button) +QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent) { -#ifndef QT_MAC_USE_COCOA - Q_UNUSED(view); - Q_UNUSED(event); - Q_UNUSED(eventType); - Q_UNUSED(button); - return false; -#else - QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view); - NSEvent *theEvent = static_cast<NSEvent *>(event); + if (QWidget *popup = QApplication::activePopupWidget()) { + QWidget *focusInPopup = popup->focusWidget(); + return focusInPopup ? focusInPopup : popup; + } - // Give the Input Manager a chance to process the mouse events. - NSInputManager *currentIManager = [NSInputManager currentInputManager]; - if (currentIManager && [currentIManager wantsToHandleMouseEvents]) { - [currentIManager handleMouseEvent:theEvent]; - } + QWidget *widgetToGetKey = qApp->focusWidget(); + if (!widgetToGetKey) + widgetToGetKey = widgetThatReceivedEvent; - // Handle tablet events (if any) first. - if (qt_mac_handleTabletEvent(theView, theEvent)) { - // Tablet event was handled. In Qt we aren't supposed to send the mouse event. - return true; + return widgetToGetKey; +} + +// This function will find the widget that should receive the +// mouse event. Because of explicit/implicit mouse grabs, popups, +// etc, this might not end up being the same as the widget under +// the mouse (which is more interresting when handling enter/leave +// events +QWidget *qt_mac_getTargetForMouseEvent( + // You can call this function without providing an event. + NSEvent *event, + QEvent::Type eventType, + QPoint &returnLocalPoint, + QPoint &returnGlobalPoint, + QWidget *nativeWidget, + QWidget **returnWidgetUnderMouse) +{ + Q_UNUSED(event); + NSPoint nsglobalpoint = event ? [[event window] convertBaseToScreen:[event locationInWindow]] : [NSEvent mouseLocation]; + returnGlobalPoint = flipPoint(nsglobalpoint).toPoint(); + QWidget *mouseGrabber = QWidget::mouseGrabber(); + bool buttonDownNotBlockedByModal = qt_button_down && !QApplicationPrivate::isBlockedByModal(qt_button_down); + QWidget *popup = QApplication::activePopupWidget(); + + // Resolve the widget under the mouse: + QWidget *widgetUnderMouse = 0; + if (popup || qt_button_down || !nativeWidget || !nativeWidget->isVisible()) { + // Using QApplication::widgetAt for finding the widget under the mouse + // is most safe, since it ignores cocoas own mouse down redirections (which + // we need to be prepared for when using nativeWidget as starting point). + // (the only exception is for QMacNativeWidget, where QApplication::widgetAt fails). + // But it is also slower (I guess), so we try to avoid it and use nativeWidget if we can: + widgetUnderMouse = QApplication::widgetAt(returnGlobalPoint); } - NSPoint windowPoint = [theEvent locationInWindow]; - NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint]; + if (!widgetUnderMouse && nativeWidget) { + // Entering here should be the common case. We + // also handle the QMacNativeWidget fallback case. + QPoint p = nativeWidget->mapFromGlobal(returnGlobalPoint); + widgetUnderMouse = nativeWidget->childAt(p); + if (!widgetUnderMouse && nativeWidget->rect().contains(p)) + widgetUnderMouse = nativeWidget; + } - // Find the widget that *should* get the event (e.g., maybe it was a pop-up, - // they always get the mouse event). - QWidget *qwidget = [theView qt_qwidget]; - QWidget *widgetToGetMouse = 0; - NSView *tmpView = 0; - QWidget *popup = qAppInstance()->activePopupWidget(); - QPoint qglobalPoint(flipPoint(globalPoint).toPoint()); + if (widgetUnderMouse) { + // Check if widgetUnderMouse is blocked by a modal + // window, or the mouse if over the frame strut: + if (widgetUnderMouse == qt_button_down) { + // Small optimization to avoid an extra call to isBlockedByModal: + if (buttonDownNotBlockedByModal == false) + widgetUnderMouse = 0; + } else if (QApplicationPrivate::isBlockedByModal(widgetUnderMouse)) { + widgetUnderMouse = 0; + } - if (popup) { - widgetToGetMouse = popup; - tmpView = qt_mac_nativeview_for(popup); - windowPoint = [[tmpView window] convertScreenToBase:globalPoint]; - - QPoint qWindowPoint(windowPoint.x, windowPoint.y); - if (widgetToGetMouse->rect().contains(qWindowPoint)) { - // Keeping the mouse pressed on a combobox button will make - // the popup pop in front of the mouse. But all mouse events - // will be sendt to the button. Since we want mouse events - // to be sendt to widgets inside the popup, we search for the - // widget in front of the mouse: - tmpView = [tmpView hitTest:windowPoint]; - if (!tmpView) - return false; - widgetToGetMouse = - [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(tmpView) qt_qwidget]; + if (widgetUnderMouse && widgetUnderMouse->isWindow()) { + // Exclude the titlebar (and frame strut) when finding widget under mouse: + QPoint p = widgetUnderMouse->mapFromGlobal(returnGlobalPoint); + if (!widgetUnderMouse->rect().contains(p)) + widgetUnderMouse = 0; + } + } + if (returnWidgetUnderMouse) + *returnWidgetUnderMouse = widgetUnderMouse; + + // Resolve the target for the mouse event. Default will be + // widgetUnderMouse, except if there is a grab (popup/mouse/button-down): + if (popup && !mouseGrabber) { + // We special case handling of popups, since they have an implicitt mouse grab. + QWidget *candidate = buttonDownNotBlockedByModal ? qt_button_down : widgetUnderMouse; + if (!popup->isAncestorOf(candidate)) { + // INVARIANT: we have a popup, but the candidate is not + // in it. But the popup will grab the mouse anyway, + // except if the user scrolls: + if (eventType == QEvent::Wheel) + return 0; + returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint); + return popup; + } else if (popup == candidate) { + // INVARIANT: The candidate is the popup itself, and not a child: + returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint); + return popup; + } else { + // INVARIANT: The candidate is a child inside the popup: + returnLocalPoint = candidate->mapFromGlobal(returnGlobalPoint); + return candidate; } + } + + QWidget *target = mouseGrabber; + if (!target && buttonDownNotBlockedByModal) + target = qt_button_down; + if (!target) + target = widgetUnderMouse; + if (!target) + return 0; + + returnLocalPoint = target->mapFromGlobal(returnGlobalPoint); + return target; +} + +QPointer<QWidget> qt_last_native_mouse_receiver = 0; + +static inline void qt_mac_checkEnterLeaveForNativeWidgets(QWidget *maybeEnterWidget) +{ + // Dispatch enter/leave for the cases where QApplicationPrivate::sendMouseEvent do + // not. This will in general be the cases when alien widgets are not involved: + // 1. from a native widget to another native widget or + // 2. from a native widget to no widget + // 3. from no widget to a native or alien widget + + if (qt_button_down || QWidget::mouseGrabber()) + return; + + if ((maybeEnterWidget == qt_last_native_mouse_receiver) && qt_last_native_mouse_receiver) + return; + if (maybeEnterWidget) { + if (!qt_last_native_mouse_receiver) { + // case 3 + QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, 0); + qt_last_native_mouse_receiver = maybeEnterWidget->internalWinId() ? maybeEnterWidget : maybeEnterWidget->nativeParentWidget(); + } else if (maybeEnterWidget->internalWinId()) { + // case 1 + QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, qt_last_native_mouse_receiver); + qt_last_native_mouse_receiver = maybeEnterWidget->internalWinId() ? maybeEnterWidget : maybeEnterWidget->nativeParentWidget(); + } // else at lest one of the widgets are alien, so enter/leave will be handled in QApplicationPrivate } else { - extern QWidget * qt_button_down; //qapplication_mac.cpp - QPoint pos; - widgetToGetMouse = QApplicationPrivate::pickMouseReceiver(qwidget, qglobalPoint, - pos, eventType, - button, qt_button_down, 0); - if (widgetToGetMouse) - tmpView = qt_mac_nativeview_for(widgetToGetMouse); + if (qt_last_native_mouse_receiver) { + // case 2 + QApplicationPrivate::dispatchEnterLeave(0, qt_last_native_mouse_receiver); + qt_last_mouse_receiver = 0; + qt_last_native_mouse_receiver = 0; + } } +} + +bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget) +{ + // Give the Input Manager a chance to process the mouse events. + NSInputManager *currentIManager = [NSInputManager currentInputManager]; + if (currentIManager && [currentIManager wantsToHandleMouseEvents]) { + [currentIManager handleMouseEvent:event]; + } + + // Find the widget that should receive the event, and the widget under the mouse. Those + // can differ if an implicit or explicit mouse grab is active: + QWidget *widgetUnderMouse = 0; + QPoint localPoint, globalPoint; + QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(event, eventType, localPoint, globalPoint, nativeWidget, &widgetUnderMouse); if (!widgetToGetMouse) return false; - NSPoint localPoint = [tmpView convertPoint:windowPoint fromView:nil]; - QPoint qlocalPoint = QPoint(localPoint.x, localPoint.y); + // From here on, we let nativeWidget actually be the native widget under widgetUnderMouse. The reason + // for this, is that qt_mac_getTargetForMouseEvent will set cocoa's mouse event redirection aside when + // determining which widget is under the mouse (in other words, it will usually ignore nativeWidget). + // nativeWidget will be used in QApplicationPrivate::sendMouseEvent to correctly dispatch enter/leave events. + if (widgetUnderMouse) + nativeWidget = widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget(); + if (!nativeWidget) + return false; + NSView *view = qt_mac_effectiveview_for(nativeWidget); - // Search for alien child widgets (either on this qwidget or on the popup) - if (widgetToGetMouse->testAttribute(Qt::WA_NativeWindow) == false || qt_widget_private(widgetToGetMouse)->hasAlienChildren) { - QPoint qScreenPoint = flipPoint(globalPoint).toPoint(); -#ifdef ALIEN_DEBUG - qDebug() << "alien mouse event" << qScreenPoint << possibleAlien; -#endif - QWidget *possibleAlien = widgetToGetMouse->childAt(qlocalPoint); - if (possibleAlien) { - qlocalPoint = possibleAlien->mapFromGlobal(widgetToGetMouse->mapToGlobal(qlocalPoint)); - widgetToGetMouse = possibleAlien; - } + // Handle tablet events (if any) first. + if (qt_mac_handleTabletEvent(view, event)) { + // Tablet event was handled. In Qt we aren't supposed to send the mouse event. + return true; } - EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([theEvent eventRef])); + EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([event eventRef])); if (qt_mac_sendMacEventToWidget(widgetToGetMouse, carbonEvent)) return true; - // Yay! All the special cases are handled, it really is just a normal mouse event. - Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]); - NSInteger clickCount = [theEvent clickCount]; - Qt::MouseButtons buttons = 0; + // Keep previousButton to make sure we don't send double click + // events when the user double clicks using two different buttons: static Qt::MouseButton previousButton = Qt::NoButton; + + Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]); + NSInteger clickCount = [event clickCount]; + Qt::MouseButtons buttons = 0; { UInt32 mac_buttons; if (GetEventParameter(carbonEvent, kEventParamMouseChord, typeUInt32, 0, sizeof(mac_buttons), 0, &mac_buttons) == noErr) buttons = qt_mac_get_buttons(mac_buttons); } + + // Send enter/leave events for the cases when QApplicationPrivate::sendMouseEvent do not: + qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse); + switch (eventType) { default: qWarning("not handled! %d", eventType); break; case QEvent::MouseMove: + if (button == Qt::LeftButton && qt_leftButtonIsRightButton) + button = Qt::RightButton; break; case QEvent::MouseButtonPress: - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->view = theView; - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->theEvent = theEvent; -#ifndef QT_NAMESPACE - Q_ASSERT(clickCount > 0); -#endif + qt_button_down = widgetUnderMouse; if (clickCount % 2 == 0 && (previousButton == Qt::NoButton || previousButton == button)) eventType = QEvent::MouseButtonDblClick; if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) { button = Qt::RightButton; - [theView qt_setLeftButtonIsRightButton: true]; + qt_leftButtonIsRightButton = true; } break; case QEvent::MouseButtonRelease: - if (button == Qt::LeftButton && [theView qt_leftButtonIsRightButton]) { + if (button == Qt::LeftButton && qt_leftButtonIsRightButton) { button = Qt::RightButton; - [theView qt_setLeftButtonIsRightButton: false]; + qt_leftButtonIsRightButton = false; } qt_button_down = 0; break; } - [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->localPoint = localPoint; - QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, buttons, keyMods); -#ifdef ALIEN_DEBUG - qDebug() << "sending mouse event to" << widgetToGetMouse; -#endif - extern QWidget *qt_button_down; - extern QPointer<QWidget> qt_last_mouse_receiver; + qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse); - if (qwidget->testAttribute(Qt::WA_NativeWindow) && qt_widget_private(qwidget)->hasAlienChildren == false) - qt_sendSpontaneousEvent(widgetToGetMouse, &qme); - else - QApplicationPrivate::sendMouseEvent(widgetToGetMouse, &qme, widgetToGetMouse, qwidget, &qt_button_down, - qt_last_mouse_receiver); + DnDParams *dndParams = currentDnDParameters(); + dndParams->view = view; + dndParams->theEvent = event; + dndParams->globalPoint = globalPoint; + + // Send the mouse event: + QMouseEvent qme(eventType, localPoint, globalPoint, button, buttons, keyMods); + QApplicationPrivate::sendMouseEvent( + widgetToGetMouse, &qme, widgetUnderMouse, nativeWidget, + &qt_button_down, qt_last_mouse_receiver, true); if (eventType == QEvent::MouseButtonPress && button == Qt::RightButton) { - QContextMenuEvent qcme(QContextMenuEvent::Mouse, qlocalPoint, qglobalPoint, keyMods); + QContextMenuEvent qcme(QContextMenuEvent::Mouse, localPoint, globalPoint, keyMods); qt_sendSpontaneousEvent(widgetToGetMouse, &qcme); } + + if (eventType == QEvent::MouseButtonRelease) { + // A mouse button was released, which means that the implicit grab was + // released. We therefore need to re-check if should send (delayed) enter leave events: + // qt_button_down has now become NULL since the call at the top of the function. Also, since + // the relase might have closed a window, we dont give the nativeWidget hint + qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, nativeWidget, &widgetUnderMouse); + qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse); + } + previousButton = button; return true; -#endif } +#endif bool qt_mac_handleTabletEvent(void * /*QCocoaView * */view, void * /*NSEvent * */tabletEvent) { @@ -1207,7 +1440,7 @@ void qt_mac_replaceDrawRect(void * /*OSWindowRef */window, QWidgetPrivate *widge // We have the original method here. Proceed and swap the methods. method_exchangeImplementations(m1, m0); widget->originalDrawMethod = false; - [window display]; + [theWindow display]; } } @@ -1230,21 +1463,21 @@ void qt_mac_replaceDrawRectOriginal(void * /*OSWindowRef */window, QWidgetPrivat } method_exchangeImplementations(m1, m0); widget->originalDrawMethod = true; - [window display]; + [theWindow display]; } #endif // QT_MAC_USE_COCOA +#if QT_MAC_USE_COCOA void qt_mac_showBaseLineSeparator(void * /*OSWindowRef */window, bool show) { if(!window) return; -#if QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; OSWindowRef theWindow = static_cast<OSWindowRef>(window); NSToolbar *macToolbar = [theWindow toolbar]; [macToolbar setShowsBaselineSeparator:show]; -#endif // QT_MAC_USE_COCOA } +#endif // QT_MAC_USE_COCOA QStringList qt_mac_NSArrayToQStringList(void *nsarray) { @@ -1264,6 +1497,7 @@ void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list) return result; } +#if QT_MAC_USE_COCOA void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow) { if (!widgetForWindow) @@ -1288,6 +1522,7 @@ void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow) [window setShowsToolbarButton:uint(flags & Qt::MacWindowToolBarButtonHint) != 0]; } +#endif // QT_MAC_USE_COCOA // Carbon: Make sure you call QDEndContext on the context when done with it. CGContextRef qt_mac_graphicsContextFor(QWidget *widget) @@ -1300,7 +1535,7 @@ CGContextRef qt_mac_graphicsContextFor(QWidget *widget) CGrafPtr port = GetWindowPort(qt_mac_window_for(widget)); QDBeginCGContext(port, &context); #else - CGContextRef context = reinterpret_cast<CGContextRef>([[qt_mac_window_for(widget) graphicsContext] graphicsPort]); + CGContextRef context = (CGContextRef)[[NSGraphicsContext graphicsContextWithWindow:qt_mac_window_for(widget)] graphicsPort]; #endif return context; } @@ -1330,11 +1565,11 @@ QString qt_mac_get_pasteboardString(OSPasteboardRef paste) QMacCocoaAutoReleasePool pool; NSPasteboard *pb = nil; CFStringRef pbname; - if (PasteboardCopyName (paste, &pbname)) { - pb = [NSPasteboard generalPasteboard]; + if (PasteboardCopyName(paste, &pbname) == noErr) { + pb = [NSPasteboard pasteboardWithName:const_cast<NSString *>(reinterpret_cast<const NSString *>(pbname))]; + CFRelease(pbname); } else { - pb = [NSPasteboard pasteboardWithName:reinterpret_cast<const NSString *>(pbname)]; - CFRelease (pbname); + pb = [NSPasteboard generalPasteboard]; } if (pb) { NSString *text = [pb stringForType:NSStringPboardType]; @@ -1389,6 +1624,7 @@ void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayI } } +#ifdef QT_MAC_USE_COCOA void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse) { QMacCocoaAutoReleasePool pool; @@ -1423,47 +1659,46 @@ void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse) } } -#ifdef QT_MAC_USE_COCOA -void qt_cocoaChangeOverrideCursor(const QCursor &cursor) -{ - QMacCocoaAutoReleasePool pool; - [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) set]; -} - -// WARNING: If Qt did not create NSApplication (e.g. in case it is -// used as a plugin), and at the same time, there is no window on -// screen (or the window that the event is sendt to becomes hidden etc -// before the event gets delivered), the message will not be performed. -bool qt_cocoaPostMessage(id target, SEL selector) -{ - if (!target) - return false; +class CocoaPostMessageAfterEventLoopExitHelp : public QObject +{ + id target; + SEL selector; + int argCount; + id arg1; + id arg2; +public: + CocoaPostMessageAfterEventLoopExitHelp(id target, SEL selector, int argCount, id arg1, id arg2) + : target(target), selector(selector), argCount(argCount), arg1(arg1), arg2(arg2){ + deleteLater(); + } - NSInteger windowNumber = 0; - if (![NSApp isMemberOfClass:[QNSApplication class]]) { - // INVARIANT: Cocoa is not using our NSApplication subclass. That means - // we don't control the main event handler either. So target the event - // for one of the windows on screen: - NSWindow *nswin = [NSApp mainWindow]; - if (!nswin) { - nswin = [NSApp keyWindow]; - if (!nswin) - return false; - } - windowNumber = [nswin windowNumber]; + ~CocoaPostMessageAfterEventLoopExitHelp() + { + qt_cocoaPostMessage(target, selector, argCount, arg1, arg2); } +}; - // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5! +void qt_cocoaPostMessage(id target, SEL selector, int argCount, id arg1, id arg2) +{ + // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5! // That is why we need to split the address in two parts: - QCocoaPostMessageArgs *args = new QCocoaPostMessageArgs(target, selector); + QCocoaPostMessageArgs *args = new QCocoaPostMessageArgs(target, selector, argCount, arg1, arg2); quint32 lower = quintptr(args); quint32 upper = quintptr(args) >> 32; NSEvent *e = [NSEvent otherEventWithType:NSApplicationDefined - location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:windowNumber + location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:0 context:nil subtype:QtCocoaEventSubTypePostMessage data1:lower data2:upper]; [NSApp postEvent:e atStart:NO]; - return true; } + +void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount, id arg1, id arg2) +{ + if (QApplicationPrivate::instance()->threadData->eventLoops.size() <= 1) + qt_cocoaPostMessage(target, selector, argCount, arg1, arg2); + else + new CocoaPostMessageAfterEventLoopExitHelp(target, selector, argCount, arg1, arg2); +} + #endif QMacCocoaAutoReleasePool::QMacCocoaAutoReleasePool() @@ -1487,6 +1722,12 @@ void qt_mac_post_retranslateAppMenu() #endif } +QWidgetPrivate *QMacScrollOptimization::_target = 0; +bool QMacScrollOptimization::_inWheelEvent = false; +int QMacScrollOptimization::_dx = 0; +int QMacScrollOptimization::_dy = 0; +QRect QMacScrollOptimization::_scrollRect = QRect(0, 0, -1, -1); + #ifdef QT_MAC_USE_COCOA // This method implements the magic for the drawRectSpecial method. // We draw a line at the upper edge of the content view in order to @@ -1501,7 +1742,7 @@ void macDrawRectOnTop(void * /*OSWindowRef */window) NSRect contentRect = [contentView frame]; // Draw a line on top of the already drawn line. // We need to check if we are active or not to use the proper color. - if([window isKeyWindow] || [window isMainWindow]) { + if([theWindow isKeyWindow] || [theWindow isMainWindow]) { [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0] set]; } else { [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0] set]; @@ -1521,7 +1762,7 @@ void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window) { OSWindowRef theWindow = static_cast<OSWindowRef>(window); NSApplication *application = [NSApplication sharedApplication]; - NSToolbar *toolbar = [window toolbar]; + NSToolbar *toolbar = [theWindow toolbar]; if([application isActive]) { // Launched from finder [toolbar setShowsBaselineSeparator:NO]; @@ -1549,6 +1790,35 @@ void qt_cocoaStackChildWindowOnTopOfOtherChildren(QWidget *childWidget) } } +void qt_mac_display(QWidget *widget) +{ + NSView *theNSView = qt_mac_nativeview_for(widget); + [theNSView display]; +} + +void qt_mac_setNeedsDisplay(QWidget *widget) +{ + NSView *theNSView = qt_mac_nativeview_for(widget); + [theNSView setNeedsDisplay:YES]; +} + +void qt_mac_setNeedsDisplayInRect(QWidget *widget, QRegion region) +{ + NSView *theNSView = qt_mac_nativeview_for(widget); + if (region.isEmpty()) { + [theNSView setNeedsDisplay:YES]; + return; + } + + QVector<QRect> rects = region.rects(); + for (int i = 0; i < rects.count(); ++i) { + const QRect &rect = rects.at(i); + NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); + [theNSView setNeedsDisplayInRect:nsrect]; + } + +} + #endif // QT_MAC_USE_COCOA QT_END_NAMESPACE diff --git a/src/gui/kernel/qt_cocoa_helpers_mac_p.h b/src/gui/kernel/qt_cocoa_helpers_mac_p.h index 29dac85..14be29e 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac_p.h +++ b/src/gui/kernel/qt_cocoa_helpers_mac_p.h @@ -103,6 +103,7 @@ #include <qtimer.h> #include <qtooltip.h> #include <private/qeffects_p.h> +#include <private/qwidget_p.h> #include <qtextdocument.h> #include <qdebug.h> #include <qpoint.h> @@ -144,18 +145,14 @@ bool qt_mac_checkForNativeSizeGrip(const QWidget *widget); void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent); #ifdef QT_MAC_USE_COCOA bool qt_dispatchKeyEventWithCocoa(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); -void qt_cocoaChangeOverrideCursor(const QCursor &cursor); // These methods exists only for supporting unified mode. void macDrawRectOnTop(void * /*OSWindowRef */ window); void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window); void qt_cocoaStackChildWindowOnTopOfOtherChildren(QWidget *widget); -#endif void qt_mac_menu_collapseSeparators(void * /*NSMenu */ menu, bool collapse); +#endif bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget *widgetToGetEvent); -void qt_mac_dispatchNCMouseMessage(void */* NSWindow* */eventWindow, void */* NSEvent* */mouseEvent, - QWidget *widgetToGetEvent, bool &leftButtonIsRightButton); -bool qt_mac_handleMouseEvent(void * /*QCocoaView * */view, void * /*NSEvent * */event, QEvent::Type eventType, Qt::MouseButton button); bool qt_mac_handleTabletEvent(void * /*QCocoaView * */view, void * /*NSEvent * */event); inline QApplication *qAppInstance() { return static_cast<QApplication *>(QCoreApplication::instance()); } struct ::TabletProximityRec; @@ -165,6 +162,29 @@ Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations); QPixmap qt_mac_convert_iconref(const IconRef icon, int width, int height); void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayIcon, QIcon *retIcon, QStyle::StandardPixmap standardIcon = QStyle::SP_CustomBase); + +#if QT_MAC_USE_COCOA && __OBJC__ +struct DnDParams +{ + NSView *view; + NSEvent *theEvent; + QPoint globalPoint; + NSDragOperation performedAction; +}; + +DnDParams *macCurrentDnDParameters(); +NSDragOperation qt_mac_mapDropAction(Qt::DropAction action); +NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions); +Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions); +Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions); + +QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent); +QWidget *qt_mac_getTargetForMouseEvent(NSEvent *event, QEvent::Type eventType, + QPoint &returnLocalPoint, QPoint &returnGlobalPoint, QWidget *nativeWidget, QWidget **returnWidgetUnderMouse); +bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget); +void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event); +#endif + inline int flipYCoordinate(int y) { return QApplication::desktop()->screenGeometry(0).height() - y; @@ -202,23 +222,119 @@ class QCocoaPostMessageArgs { public: id target; SEL selector; - QCocoaPostMessageArgs(id target, SEL selector) : target(target), selector(selector) + int argCount; + id arg1; + id arg2; + QCocoaPostMessageArgs(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0) + : target(target), selector(selector), argCount(argCount), arg1(arg1), arg2(arg2) { [target retain]; + [arg1 retain]; + [arg2 retain]; } ~QCocoaPostMessageArgs() { + [arg2 release]; + [arg1 release]; [target release]; } }; -bool qt_cocoaPostMessage(id target, SEL selector); +void qt_cocoaPostMessage(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0); +void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0); #endif #endif +class QMacScrollOptimization { + // This class is made to optimize for the case when the user + // scrolls both horizontally and vertically at the same + // time. This will result in two QWheelEvents (one for each + // direction), which will typically result in two calls to + // QWidget::_scroll_sys. Rather than copying pixels twize on + // screen because of this, we add this helper class to try to + // get away with only one blit. + static QWidgetPrivate *_target; + static bool _inWheelEvent; + static int _dx; + static int _dy; + static QRect _scrollRect; + +public: + static void initDelayedScroll() + { + _inWheelEvent = true; + } + + static bool delayScroll(QWidgetPrivate *target, int dx, int dy, const QRect &scrollRect) + { + if (!_inWheelEvent) + return false; + if (_target && _target != target) + return false; + if (_scrollRect.width() != -1 && _scrollRect != scrollRect) + return false; + + _target = target; + _dx += dx; + _dy += dy; + _scrollRect = scrollRect; + return true; + } + + static void performDelayedScroll() + { + if (!_inWheelEvent) + return; + _inWheelEvent = false; + if (!_target) + return; + + _target->scroll_sys(_dx, _dy, _scrollRect); + + _target = 0; + _dx = 0; + _dy = 0; + _scrollRect = QRect(0, 0, -1, -1); + } +}; + void qt_mac_post_retranslateAppMenu(); +#ifdef QT_MAC_USE_COCOA +void qt_mac_display(QWidget *widget); +void qt_mac_setNeedsDisplay(QWidget *widget); +void qt_mac_setNeedsDisplayInRect(QWidget *widget, QRegion region); +#endif // QT_MAC_USE_COCOA + + +// Utility functions to ease the use of Core Graphics contexts. + +inline void qt_mac_retain_graphics_context(CGContextRef context) +{ + CGContextRetain(context); + CGContextSaveGState(context); +} + +inline void qt_mac_release_graphics_context(CGContextRef context) +{ + CGContextRestoreGState(context); + CGContextRelease(context); +} + +inline void qt_mac_draw_image(CGContextRef context, CGContextRef imageContext, CGRect area, CGRect drawingArea) +{ + CGImageRef image = CGBitmapContextCreateImage(imageContext); + CGImageRef subImage = CGImageCreateWithImageInRect(image, area); + + CGContextTranslateCTM (context, 0, drawingArea.origin.y + CGRectGetMaxY(drawingArea)); + CGContextScaleCTM(context, 1, -1); + CGContextDrawImage(context, drawingArea, subImage); + + CGImageRelease(subImage); + CGImageRelease(image); +} + QT_END_NAMESPACE #endif // QT_COCOA_HELPERS_MAC_P_H diff --git a/src/gui/kernel/qt_mac.cpp b/src/gui/kernel/qt_mac.cpp index 339bc82..69f0034 100644 --- a/src/gui/kernel/qt_mac.cpp +++ b/src/gui/kernel/qt_mac.cpp @@ -61,7 +61,6 @@ QFont qfontForThemeFont(ThemeFontID themeID) SInt16 f_size; Style f_style; GetThemeFont(themeID, Script, f_name, &f_size, &f_style); - extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp return QFont(qt_mac_from_pascal_string(f_name), f_size, (f_style & ::bold) ? QFont::Bold : QFont::Normal, (bool)(f_style & ::italic)); diff --git a/src/gui/kernel/qt_s60_p.h b/src/gui/kernel/qt_s60_p.h index 3e6a293..96b8141 100644 --- a/src/gui/kernel/qt_s60_p.h +++ b/src/gui/kernel/qt_s60_p.h @@ -77,6 +77,7 @@ #include <akncontext.h> // CAknContextPane #include <eikspane.h> // CEikStatusPane #include <AknPopupFader.h> // MAknFadedComponent and TAknPopupFader +#include <bitstd.h> // EGraphicsOrientation constants #ifdef QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H #include <gfxtranseffect/gfxtranseffect.h> // BeginFullScreen #include <akntranseffect.h> // BeginFullScreen @@ -213,6 +214,14 @@ public: int nativeScreenWidthInPixels; int nativeScreenHeightInPixels; + enum ScreenRotation { + ScreenRotation0, // portrait (or the native orientation) + ScreenRotation90, // typically DisplayLeftUp landscape + ScreenRotation180, // not used + ScreenRotation270 // DisplayRightUp landscape when 3-way orientation is supported + }; + ScreenRotation screenRotation; + int beginFullScreenCalled : 1; int endFullScreenCalled : 1; }; @@ -303,6 +312,7 @@ private: void translateAdvancedPointerEvent(const TAdvancedPointerEvent *event); #endif bool isSplitViewWidget(QWidget *widget); + bool hasFocusedAndVisibleChild(QWidget *parentWidget); public: void handleClientAreaChange(); @@ -383,6 +393,24 @@ inline void QS60Data::updateScreenSize() inches = S60->screenWidthInTwips / (TReal)KTwipsPerInch; S60->defaultDpiX = S60->screenWidthInPixels / inches; + switch (params.iRotation) { + case CFbsBitGc::EGraphicsOrientationNormal: + S60->screenRotation = ScreenRotation0; + break; + case CFbsBitGc::EGraphicsOrientationRotated90: + S60->screenRotation = ScreenRotation90; + break; + case CFbsBitGc::EGraphicsOrientationRotated180: + S60->screenRotation = ScreenRotation180; + break; + case CFbsBitGc::EGraphicsOrientationRotated270: + S60->screenRotation = ScreenRotation270; + break; + default: + S60->screenRotation = ScreenRotation0; + break; + } + int screens = S60->screenCount(); for (int i = 0; i < screens; ++i) { CWsScreenDevice *dev = S60->screenDevice(i); diff --git a/src/gui/kernel/qtooltip.cpp b/src/gui/kernel/qtooltip.cpp index b7b6765..f880243 100644 --- a/src/gui/kernel/qtooltip.cpp +++ b/src/gui/kernel/qtooltip.cpp @@ -353,7 +353,7 @@ void QTipLabel::placeTip(const QPoint &pos, QWidget *w) #ifndef QT_NO_STYLE_STYLESHEET if (testAttribute(Qt::WA_StyleSheet) || (w && qobject_cast<QStyleSheetStyle *>(w->style()))) { //the stylesheet need to know the real parent - QTipLabel::instance->setProperty("_q_stylesheet_parent", qVariantFromValue(w)); + QTipLabel::instance->setProperty("_q_stylesheet_parent", QVariant::fromValue(w)); //we force the style to be the QStyleSheetStyle, and force to clear the cache as well. QTipLabel::instance->setStyleSheet(QLatin1String("/* */")); diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 8e8266c..0aa1dfa 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -76,6 +76,9 @@ # include "qpaintengine.h" // for PorterDuff # include "private/qwindowsurface_qws_p.h" #endif +#if defined(Q_WS_QPA) +#include "qplatformwindow_qpa.h" +#endif #include "qpainter.h" #include "qtooltip.h" #include "qwhatsthis.h" @@ -145,6 +148,10 @@ Q_GUI_EXPORT void qt_x11_set_global_double_buffer(bool enable) } #endif +#if defined(QT_MAC_USE_COCOA) +bool qt_mac_clearDirtyOnWidgetInsideDrawWidget = false; +#endif + static inline bool qRectIntersects(const QRect &r1, const QRect &r2) { return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right()) && @@ -302,7 +309,6 @@ QWidgetPrivate::QWidgetPrivate(int version) #endif #elif defined(Q_WS_MAC) , needWindowChange(0) - , hasAlienChildren(0) , window_event(0) , qd_hd(0) #elif defined(Q_OS_SYMBIAN) @@ -324,6 +330,11 @@ QWidgetPrivate::QWidgetPrivate(int version) drawRectOriginalAdded = false; originalDrawMethod = true; changeMethods = false; + isInUnifiedToolbar = false; + unifiedSurface = 0; + toolbar_ancestor = 0; + flushRequested = false; + touchEventsEnabled = false; #endif // QT_MAC_USE_COCOA #ifdef QWIDGET_EXTRA_DEBUG static int count = 0; @@ -362,9 +373,12 @@ QWindowSurface *QWidgetPrivate::createDefaultWindowSurface() Q_Q(QWidget); QWindowSurface *surface; +#ifndef QT_NO_PROPERTIES if (q->property("_q_DummyWindowSurface").toBool()) { surface = new QDummyWindowSurface(q); - } else { + } else +#endif + { if (QApplicationPrivate::graphicsSystem()) surface = QApplicationPrivate::graphicsSystem()->createWindowSurface(q); else @@ -1295,6 +1309,12 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) if (desktopWidget) { symbianScreenNumber = qt_widget_private(desktopWidget)->symbianScreenNumber; } +#elif defined(Q_WS_QPA) + if (desktopWidget) { + int screen = desktopWidget->d_func()->topData()->screenIndex; + QPlatformIntegration *platform = QApplicationPrivate::platformIntegration(); + platform->moveToScreen(q, screen); + } #else Q_UNUSED(desktopWidget); #endif @@ -1319,9 +1339,9 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) if (f & Qt::MSWindowsOwnDC) q->setAttribute(Qt::WA_NativeWindow); -#ifdef Q_WS_MAC - q->setAttribute(Qt::WA_NativeWindow); -#endif +//#ifdef Q_WS_MAC +// q->setAttribute(Qt::WA_NativeWindow); +//#endif q->setAttribute(Qt::WA_QuitOnClose); // might be cleared in adjustQuitOnCloseAttribute() adjustQuitOnCloseAttribute(); @@ -1426,11 +1446,8 @@ void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) flags |= Qt::Window; } +#ifndef Q_WS_QPA if (QWidget *parent = parentWidget()) { -#ifdef Q_WS_MAC - if (testAttribute(Qt::WA_NativeWindow) == false) - parent->d_func()->hasAlienChildren = true; -#endif if (type & Qt::Window) { if (!parent->testAttribute(Qt::WA_WState_Created)) parent->createWinId(); @@ -1446,6 +1463,7 @@ void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) return; } } +#endif //Q_WS_QPA #ifdef QT3_SUPPORT if (flags & Qt::WStaticContents) @@ -1565,6 +1583,7 @@ QWidget::~QWidget() // delete layout while we still are a valid widget delete d->layout; + d->layout = 0; // Remove myself from focus list Q_ASSERT(d->focus_next->d_func()->focus_prev == this); @@ -1604,13 +1623,13 @@ QWidget::~QWidget() } } -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) +#if defined(Q_WS_WIN) || defined(Q_WS_X11)|| defined(Q_WS_MAC) else if (!internalWinId() && isVisible()) { qApp->d_func()->sendSyntheticEnterLeave(this); -#ifdef Q_WS_QWS - } else if (isVisible()) { + } +#elif defined(Q_WS_QWS) || defined(Q_WS_QPA) + else if (isVisible()) { qApp->d_func()->sendSyntheticEnterLeave(this); -#endif } #endif @@ -1635,7 +1654,8 @@ QWidget::~QWidget() d->needsFlush = 0; // set all QPointers for this object to zero - QObjectPrivate::clearGuards(this); + if (d->hasGuards) + QObjectPrivate::clearGuards(this); if (d->declarativeData) { QAbstractDeclarativeData::destroyed(d->declarativeData, this); @@ -1654,6 +1674,10 @@ QWidget::~QWidget() if (!d->children.isEmpty()) d->deleteChildren(); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::ObjectDestroyed); +#endif + QApplication::removePostedEvents(this); QT_TRY { @@ -1737,6 +1761,11 @@ void QWidgetPrivate::createTLExtra() static int count = 0; qDebug() << "tlextra" << ++count; #endif +#if defined(Q_WS_QPA) + x->platformWindow = 0; + x->platformWindowFormat = QPlatformWindowFormat::defaultFormat(); + x->screenIndex = 0; +#endif } } @@ -1858,13 +1887,7 @@ void QWidgetPrivate::syncBackingStore() repaint_sys(dirty); dirty = QRegion(); } else if (QWidgetBackingStore *bs = maybeBackingStore()) { -#ifdef QT_MAC_USE_COCOA - Q_UNUSED(bs); - void qt_mac_set_needs_display(QWidget *, QRegion); - qt_mac_set_needs_display(q_func(), QRegion()); -#else bs->sync(); -#endif } } @@ -1873,13 +1896,7 @@ void QWidgetPrivate::syncBackingStore(const QRegion ®ion) if (paintOnScreen()) repaint_sys(region); else if (QWidgetBackingStore *bs = maybeBackingStore()) { -#ifdef QT_MAC_USE_COCOA - Q_UNUSED(bs); - void qt_mac_set_needs_display(QWidget *, QRegion); - qt_mac_set_needs_display(q_func(), region); -#else bs->sync(q_func(), region); -#endif } } @@ -2096,6 +2113,11 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt if (disableSubtractOpaqueSiblings || q->isWindow()) return; +#ifdef QT_MAC_USE_COCOA + if (q->d_func()->isInUnifiedToolbar) + return; +#endif // QT_MAC_USE_COCOA + QRect clipBoundingRect; bool dirtyClipBoundingRect = true; @@ -2483,7 +2505,9 @@ WId QWidget::winId() const qDebug() << "QWidget::winId: creating native window for" << this; #endif QWidget *that = const_cast<QWidget*>(this); +#ifndef Q_WS_QPA that->setAttribute(Qt::WA_NativeWindow); +#endif that->d_func()->createWinId(); return that->data->winid; } @@ -2500,6 +2524,7 @@ void QWidgetPrivate::createWinId(WId winid) #endif const bool forceNativeWindow = q->testAttribute(Qt::WA_NativeWindow); if (!q->testAttribute(Qt::WA_WState_Created) || (forceNativeWindow && !q->internalWinId())) { +#ifndef Q_WS_QPA if (!q->isWindow()) { QWidget *parent = q->parentWidget(); QWidgetPrivate *pd = parent->d_func(); @@ -2527,6 +2552,11 @@ void QWidgetPrivate::createWinId(WId winid) } else { q->create(); } +#else + Q_UNUSED(winid); + q->create(); +#endif //Q_WS_QPA + } } @@ -5426,6 +5456,17 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP if (rgn.isEmpty()) return; +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + if (qt_mac_clearDirtyOnWidgetInsideDrawWidget) + dirtyOnWidget = QRegion(); + + // We disable the rendering of QToolBar in the backingStore if + // it's supposed to be in the unified toolbar on Mac OS X. + if (backingStore && isInUnifiedToolbar) + return; +#endif // Q_WS_MAC && QT_MAC_USE_COCOA + + Q_Q(QWidget); #ifndef QT_NO_GRAPHICSEFFECT if (graphicsEffect && graphicsEffect->isEnabled()) { @@ -5488,6 +5529,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP QPaintEngine *paintEngine = pdev->paintEngine(); if (paintEngine) { setRedirected(pdev, -offset); + #ifdef Q_WS_MAC // (Alien support) Special case for Mac when redirecting: If the paint device // is of the Widget type we need to set WA_WState_InPaintEvent since painting @@ -5505,7 +5547,6 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP //paint the background if ((asRoot || q->autoFillBackground() || onScreen || q->testAttribute(Qt::WA_StyledBackground)) && !q->testAttribute(Qt::WA_OpaquePaintEvent) && !q->testAttribute(Qt::WA_NoSystemBackground)) { - QPainter p(q); paintBackground(&p, toBePainted, (asRoot || onScreen) ? flags | DrawAsRoot : 0); } @@ -5530,7 +5571,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP //actually send the paint event QPaintEvent e(toBePainted); QCoreApplication::sendSpontaneousEvent(q, &e); -#if !defined(Q_WS_MAC) && !defined(Q_WS_QWS) +#if !defined(Q_WS_QWS) && !defined(Q_WS_QPA) if (backingStore && !onScreen && !asRoot && (q->internalWinId() || !q->nativeParentWidget()->isWindow())) backingStore->markDirtyOnScreen(toBePainted, q, offset); #endif @@ -5687,10 +5728,12 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis QRect boundingRect; bool dirtyBoundingRect = true; const bool exludeOpaqueChildren = (flags & DontDrawOpaqueChildren); + const bool excludeNativeChildren = (flags & DontDrawNativeChildren); do { QWidget *x = qobject_cast<QWidget*>(siblings.at(index)); - if (x && !(exludeOpaqueChildren && x->d_func()->isOpaque) && !x->isHidden() && !x->isWindow()) { + if (x && !(exludeOpaqueChildren && x->d_func()->isOpaque) && !x->isHidden() && !x->isWindow() + && !(excludeNativeChildren && x->internalWinId())) { if (dirtyBoundingRect) { boundingRect = rgn.boundingRect(); dirtyBoundingRect = false; @@ -6399,6 +6442,10 @@ void QWidget::setFocus(Qt::FocusReason reason) if (!(testAttribute(Qt::WA_WState_Created) && window()->windowType() != Qt::Popup && internalWinId())) //setFocusWidget will already post a focus event for us (that the AT client receives) on Windows # endif +# ifdef Q_OS_UNIX + // menus update the focus manually and this would create bogus events + if (!(f->inherits("QMenuBar") || f->inherits("QMenu") || f->inherits("QMenuItem"))) +# endif QAccessible::updateAccessibility(f, 0, QAccessible::Focus); #endif #ifndef QT_NO_GRAPHICSVIEW @@ -6677,7 +6724,7 @@ void QWidget::setTabOrder(QWidget* first, QWidget *second) // that can take keyboard focus so that second is inserted after // that last child, and the focus order within first is (more // likely to be) preserved. - QList<QWidget *> l = qFindChildren<QWidget *>(first); + QList<QWidget *> l = first->findChildren<QWidget *>(); for (int i = l.size()-1; i >= 0; --i) { QWidget * next = l.at(i); if (next->window() == fp->window()) { @@ -7066,7 +7113,11 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) if (maximized || fullScreen) { // set geomerty before setting the window state to make // sure the window is maximized to the right screen. + // Skip on windows: the window is restored into a broken + // half-maximized state. +#ifndef Q_WS_WIN setGeometry(restoredNormalGeometry); +#endif Qt::WindowStates ws = windowState(); if (maximized) ws |= Qt::WindowMaximized; @@ -7568,7 +7619,7 @@ void QWidgetPrivate::hide_helper() // next bit tries to move the focus if the focus widget is now // hidden. if (wasVisible) { -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) || defined(Q_WS_QPA) qApp->d_func()->sendSyntheticEnterLeave(q); #endif @@ -7700,7 +7751,7 @@ void QWidget::setVisible(bool visible) d->show_helper(); -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) || defined(Q_WS_QPA) qApp->d_func()->sendSyntheticEnterLeave(this); #endif } @@ -7832,7 +7883,7 @@ void QWidgetPrivate::hideChildren(bool spontaneous) widget->d_func()->hide_sys(); } } -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) || defined(Q_WS_QPA) qApp->d_func()->sendSyntheticEnterLeave(widget); #endif #ifndef QT_NO_ACCESSIBILITY @@ -9841,6 +9892,23 @@ int QWidget::heightForWidth(int w) const return -1; } + +/*! + \internal + + *virtual private* + + This is a bit hackish, but ideally we would have created a virtual function + in the public API (however, too late...) so that subclasses could reimplement + their own function. + Instead we add a virtual function to QWidgetPrivate. + ### Qt5: move to public class and make virtual +*/ +bool QWidgetPrivate::hasHeightForWidth() const +{ + return layout ? layout->hasHeightForWidth() : size_policy.hasHeightForWidth(); +} + /*! \fn QWidget *QWidget::childAt(int x, int y) const @@ -10074,7 +10142,13 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) #if defined(Q_WS_X11) || defined(Q_WS_WIN) || defined(Q_WS_MAC) || defined(Q_OS_SYMBIAN) if (newParent && parent && !desktopWidget) { - if (testAttribute(Qt::WA_NativeWindow) && !qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings)) + if (testAttribute(Qt::WA_NativeWindow) && !qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + // On Mac, toolbars inside the unified title bar will never overlap with + // siblings in the content view. So we skip enforce native siblings in that case + && !d->isInUnifiedToolbar && parentWidget() && parentWidget()->isWindow() +#endif // Q_WS_MAC && QT_MAC_USE_COCOA + ) parent->d_func()->enforceNativeChildren(); else if (parent->d_func()->nativeChildrenForced() || parent->testAttribute(Qt::WA_PaintOnScreen)) setAttribute(Qt::WA_NativeWindow); @@ -10359,6 +10433,12 @@ void QWidget::repaint(const QRect &rect) return; if (hasBackingStoreSupport()) { +#ifdef QT_MAC_USE_COCOA + if (qt_widget_private(this)->isInUnifiedToolbar) { + qt_widget_private(this)->unifiedSurface->renderToolbar(this, true); + return; + } +#endif // QT_MAC_USE_COCOA QTLWExtra *tlwExtra = window()->d_func()->maybeTopData(); if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) { tlwExtra->inRepaint = true; @@ -10388,6 +10468,12 @@ void QWidget::repaint(const QRegion &rgn) return; if (hasBackingStoreSupport()) { +#ifdef QT_MAC_USE_COCOA + if (qt_widget_private(this)->isInUnifiedToolbar) { + qt_widget_private(this)->unifiedSurface->renderToolbar(this, true); + return; + } +#endif // QT_MAC_USE_COCOA QTLWExtra *tlwExtra = window()->d_func()->maybeTopData(); if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) { tlwExtra->inRepaint = true; @@ -10445,6 +10531,12 @@ void QWidget::update(const QRect &rect) } if (hasBackingStoreSupport()) { +#ifdef QT_MAC_USE_COCOA + if (qt_widget_private(this)->isInUnifiedToolbar) { + qt_widget_private(this)->unifiedSurface->renderToolbar(this, true); + return; + } +#endif // QT_MAC_USE_COCOA QTLWExtra *tlwExtra = window()->d_func()->maybeTopData(); if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) tlwExtra->backingStore->markDirty(rect, this); @@ -10469,6 +10561,12 @@ void QWidget::update(const QRegion &rgn) } if (hasBackingStoreSupport()) { +#ifdef QT_MAC_USE_COCOA + if (qt_widget_private(this)->isInUnifiedToolbar) { + qt_widget_private(this)->unifiedSurface->renderToolbar(this, true); + return; + } +#endif // QT_MAC_USE_COCOA QTLWExtra *tlwExtra = window()->d_func()->maybeTopData(); if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) tlwExtra->backingStore->markDirty(rgn, this); @@ -10596,7 +10694,6 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) Q_ASSERT_X(sizeof(d->high_attributes)*8 >= (Qt::WA_AttributeCount - sizeof(uint)*8), "QWidget::setAttribute(WidgetAttribute, bool)", "QWidgetPrivate::high_attributes[] too small to contain all attributes in WidgetAttribute"); - #ifdef Q_WS_WIN // ### Don't use PaintOnScreen+paintEngine() to do native painting in 5.0 if (attribute == Qt::WA_PaintOnScreen && on && !inherits("QGLWidget")) { @@ -10724,7 +10821,13 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) ic->setFocusWidget(0); } } - if (!qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) && parentWidget()) + if (!qApp->testAttribute(Qt::AA_DontCreateNativeWidgetSiblings) && parentWidget() +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + // On Mac, toolbars inside the unified title bar will never overlap with + // siblings in the content view. So we skip enforce native siblings in that case + && !d->isInUnifiedToolbar && parentWidget()->isWindow() +#endif // Q_WS_MAC && QT_MAC_USE_COCOA + ) parentWidget()->d_func()->enforceNativeChildren(); if (on && !internalWinId() && testAttribute(Qt::WA_WState_Created)) d->createWinId(); @@ -11125,6 +11228,7 @@ void QWidget::setAccessibleName(const QString &name) { Q_D(QWidget); d->accessibleName = name; + QAccessible::updateAccessibility(this, 0, QAccessible::NameChanged); } QString QWidget::accessibleName() const @@ -11146,6 +11250,7 @@ void QWidget::setAccessibleDescription(const QString &description) { Q_D(QWidget); d->accessibleDescription = description; + QAccessible::updateAccessibility(this, 0, QAccessible::DescriptionChanged); } QString QWidget::accessibleDescription() const @@ -11261,8 +11366,10 @@ void QWidget::updateMicroFocus() } #endif #ifndef QT_NO_ACCESSIBILITY - // ##### is this correct - QAccessible::updateAccessibility(this, 0, QAccessible::StateChanged); + if (isVisible()) { + // ##### is this correct + QAccessible::updateAccessibility(this, 0, QAccessible::StateChanged); + } #endif } diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h index 2f8545e..8306ed4 100644 --- a/src/gui/kernel/qwidget.h +++ b/src/gui/kernel/qwidget.h @@ -56,6 +56,10 @@ #include <QtGui/qcursor.h> #include <QtGui/qkeysequence.h> +#ifdef Q_WS_QPA //should this go somewhere else? +#include <QtGui/qplatformwindowformat_qpa.h> +#endif + #ifdef QT_INCLUDE_COMPAT #include <QtGui/qevent.h> #endif @@ -94,9 +98,12 @@ class QHideEvent; class QInputContext; class QIcon; class QWindowSurface; +class QPlatformWindow; class QLocale; class QGraphicsProxyWidget; class QGraphicsEffect; +class QRasterWindowSurface; +class QUnifiedToolbarSurface; #if defined(Q_WS_X11) class QX11Info; #endif @@ -627,6 +634,16 @@ public: void setWindowSurface(QWindowSurface *surface); QWindowSurface *windowSurface() const; +#if defined(Q_WS_QPA) + void setPlatformWindow(QPlatformWindow *window); + QPlatformWindow *platformWindow() const; + + void setPlatformWindowFormat(const QPlatformWindowFormat &format); + QPlatformWindowFormat platformWindowFormat() const; + + friend class QDesktopScreenWidget; +#endif + Q_SIGNALS: void customContextMenuRequested(const QPoint &pos); @@ -758,6 +775,8 @@ private: friend OSViewRef qt_mac_nativeview_for(const QWidget *w); friend void qt_event_request_window_change(QWidget *widget); friend bool qt_mac_sendMacEventToWidget(QWidget *widget, EventRef ref); + friend class QRasterWindowSurface; + friend class QUnifiedToolbarSurface; #endif #ifdef Q_WS_QWS friend class QWSBackingStore; @@ -898,13 +917,6 @@ protected: Q_DECLARE_OPERATORS_FOR_FLAGS(QWidget::RenderFlags) -#if defined Q_CC_MSVC && _MSC_VER < 1300 -template <> inline QWidget *qobject_cast_helper<QWidget*>(QObject *o, QWidget *) -{ - if (!o || !o->isWidgetType()) return 0; - return (QWidget*)(o); -} -#else template <> inline QWidget *qobject_cast<QWidget*>(QObject *o) { if (!o || !o->isWidgetType()) return 0; @@ -915,7 +927,6 @@ template <> inline const QWidget *qobject_cast<const QWidget*>(const QObject *o) if (!o || !o->isWidgetType()) return 0; return static_cast<const QWidget*>(o); } -#endif inline QWidget *QWidget::childAt(int ax, int ay) const { return childAt(QPoint(ax, ay)); } diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index 9481a85..632df78 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -155,7 +155,6 @@ static bool qt_mac_raise_process = true; static OSWindowRef qt_root_win = 0; QWidget *mac_mouse_grabber = 0; QWidget *mac_keyboard_grabber = 0; -extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp #ifndef QT_MAC_USE_COCOA #ifdef QT_NAMESPACE @@ -179,13 +178,15 @@ static CFStringRef kObjectQWidget = CFSTR("com.trolltech.qt.widget"); /***************************************************************************** Externals *****************************************************************************/ +extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp extern QWidget *qt_mac_modal_blocked(QWidget *); //qapplication_mac.mm extern void qt_event_request_activate(QWidget *); //qapplication_mac.mm extern bool qt_event_remove_activate(); //qapplication_mac.mm extern void qt_mac_event_release(QWidget *w); //qapplication_mac.mm extern void qt_event_request_showsheet(QWidget *); //qapplication_mac.mm extern void qt_event_request_window_change(QWidget *); //qapplication_mac.mm -extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm +extern QPointer<QWidget> qt_last_mouse_receiver; //qapplication_mac.mm +extern QPointer<QWidget> qt_last_native_mouse_receiver; //qt_cocoa_helpers_mac.mm extern IconRef qt_mac_create_iconref(const QPixmap &); //qpixmap_mac.cpp extern void qt_mac_set_cursor(const QCursor *, const QPoint &); //qcursor_mac.mm extern void qt_mac_update_cursor(); //qcursor_mac.mm @@ -193,7 +194,8 @@ extern bool qt_nograb(); extern CGImageRef qt_mac_create_cgimage(const QPixmap &, bool); //qpixmap_mac.cpp extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp extern QRegion qt_mac_convert_mac_region(RgnHandle rgn); //qregion_mac.cpp - +extern void qt_mac_setMouseGrabCursor(bool set, QCursor *cursor = 0); // qcursor_mac.mm +extern QPointer<QWidget> topLevelAt_cache; // qapplication_mac.mm /***************************************************************************** QWidget utility functions *****************************************************************************/ @@ -217,20 +219,13 @@ static QSize qt_mac_desktopSize() #ifdef QT_MAC_USE_COCOA static NSDrawer *qt_mac_drawer_for(const QWidget *widget) { - // This only goes one level below the content view so start with the window. - // This works fine for straight Qt stuff, but runs into problems if we are - // embedding, but if that's the case, they probably want to be using - // NSDrawer directly. - NSView *widgetView = reinterpret_cast<NSView *>(widget->window()->winId()); + NSView *widgetView = reinterpret_cast<NSView *>(widget->window()->effectiveWinId()); NSArray *windows = [NSApp windows]; for (NSWindow *window in windows) { NSArray *drawers = [window drawers]; for (NSDrawer *drawer in drawers) { - NSArray *views = [[drawer contentView] subviews]; - for (NSView *view in views) { - if (view == widgetView) - return drawer; - } + if ([drawer contentView] == widgetView) + return drawer; } } return 0; @@ -240,6 +235,9 @@ static NSDrawer *qt_mac_drawer_for(const QWidget *widget) static void qt_mac_destructView(OSViewRef view) { #ifdef QT_MAC_USE_COCOA + NSWindow *window = [view window]; + if ([window contentView] == view) + [window setContentView:[[NSView alloc] initWithFrame:[view bounds]]]; [view removeFromSuperview]; [view release]; #else @@ -309,7 +307,7 @@ bool qt_mac_is_macdrawer(const QWidget *w) bool qt_mac_insideKeyWindow(const QWidget *w) { #ifdef QT_MAC_USE_COCOA - return [[reinterpret_cast<NSView *>(w->winId()) window] isKeyWindow]; + return [[reinterpret_cast<NSView *>(w->effectiveWinId()) window] isKeyWindow]; #else Q_UNUSED(w); #endif @@ -416,7 +414,14 @@ inline static void qt_mac_set_fullscreen_mode(bool b) Q_GUI_EXPORT OSViewRef qt_mac_nativeview_for(const QWidget *w) { - return reinterpret_cast<OSViewRef>(w->data->winid); + return reinterpret_cast<OSViewRef>(w->internalWinId()); +} + +Q_GUI_EXPORT OSViewRef qt_mac_effectiveview_for(const QWidget *w) +{ + // Get the first non-alien (parent) widget for + // w, and return its NSView (if it has one): + return reinterpret_cast<OSViewRef>(w->effectiveWinId()); } Q_GUI_EXPORT OSViewRef qt_mac_get_contentview_for(OSWindowRef w) @@ -474,11 +479,12 @@ bool qt_isGenuineQWidget(const QWidget *window) Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w) { - OSViewRef hiview = qt_mac_nativeview_for(w); - if (hiview){ + if (OSViewRef hiview = qt_mac_effectiveview_for(w)) { OSWindowRef window = qt_mac_window_for(hiview); - if (!window && qt_isGenuineQWidget(hiview)) { - QWidget *myWindow = w->window(); + if (window) + return window; + + if (qt_isGenuineQWidget(hiview)) { // This is a workaround for NSToolbar. When a widget is hidden // by clicking the toolbar button, Cocoa reparents the widgets // to another window (but Qt doesn't know about it). @@ -486,18 +492,22 @@ Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w) // but at this point it's window is nil, but the window it's being brought // into (the Qt one) is for sure created. // This stops the hierarchy moving under our feet. - if (myWindow != w && qt_mac_window_for(qt_mac_nativeview_for(myWindow))) - return qt_mac_window_for(qt_mac_nativeview_for(myWindow)); + QWidget *toplevel = w->window(); + if (toplevel != w) { + hiview = qt_mac_nativeview_for(toplevel); + if (OSWindowRef w = qt_mac_window_for(hiview)) + return w; + } - myWindow->d_func()->createWindow_sys(); - // Reget the hiview since the "create window could potentially move the view (I guess). - hiview = qt_mac_nativeview_for(w); - window = qt_mac_window_for(hiview); + toplevel->d_func()->createWindow_sys(); + // Reget the hiview since "create window" could potentially move the view (I guess). + hiview = qt_mac_nativeview_for(toplevel); + return qt_mac_window_for(hiview); } - return window; } return 0; } + #ifndef QT_MAC_USE_COCOA /* Checks if the current group is a 'stay on top' group. If so, the group gets removed from the hash table */ @@ -575,25 +585,6 @@ inline static void qt_mac_set_window_group_to_popup(OSWindowRef window) } #endif -#ifdef QT_MAC_USE_COCOA -void qt_mac_set_needs_display(QWidget *widget, QRegion region) -{ - NSView *theNSView = qt_mac_nativeview_for(widget); - if (region.isEmpty()) { - [theNSView setNeedsDisplay:YES]; - return; - } - - QVector<QRect> rects = region.rects(); - for (int i = 0; i<rects.count(); ++i) { - const QRect &rect = rects.at(i); - NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); - [theNSView setNeedsDisplayInRect:nsrect]; - } - -} -#endif - inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const QRect &rect) { if (!widget) @@ -631,6 +622,51 @@ inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const return false; } +void QWidgetPrivate::macSetNeedsDisplay(QRegion region) +{ + Q_Q(QWidget); +#ifndef QT_MAC_USE_COCOA + if (region.isEmpty()) + HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); + else if (RgnHandle rgnHandle = region.toQDRgnForUpdate_sys()) + HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgnHandle), true); + else + HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); // do a complete repaint on overflow. +#else + if (NSView *nativeView = qt_mac_nativeview_for(q)) { + // INVARIANT: q is _not_ alien. So we can optimize a little: + if (region.isEmpty()) { + [nativeView setNeedsDisplay:YES]; + } else { + QVector<QRect> rects = region.rects(); + for (int i = 0; i<rects.count(); ++i) { + const QRect &rect = rects.at(i); + NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); + [nativeView setNeedsDisplayInRect:nsrect]; + } + } + } else if (QWidget *effectiveWidget = q->nativeParentWidget()) { + // INVARIANT: q is alien, and effectiveWidget is native. + if (NSView *effectiveView = qt_mac_nativeview_for(effectiveWidget)) { + if (region.isEmpty()) { + const QRect &rect = q->rect(); + QPoint p = q->mapTo(effectiveWidget, rect.topLeft()); + NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height()); + [effectiveView setNeedsDisplayInRect:nsrect]; + } else { + QVector<QRect> rects = region.rects(); + for (int i = 0; i<rects.count(); ++i) { + const QRect &rect = rects.at(i); + QPoint p = q->mapTo(effectiveWidget, rect.topLeft()); + NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height()); + [effectiveView setNeedsDisplayInRect:nsrect]; + } + } + } + } +#endif +} + void QWidgetPrivate::macUpdateIsOpaque() { Q_Q(QWidget); @@ -1564,6 +1600,11 @@ OSViewRef qt_mac_create_widget(QWidget *widget, QWidgetPrivate *widgetPrivate, O #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; QT_MANGLE_NAMESPACE(QCocoaView) *view = [[QT_MANGLE_NAMESPACE(QCocoaView) alloc] initWithQWidget:widget widgetPrivate:widgetPrivate]; + +#ifdef ALIEN_DEBUG + qDebug() << "Creating NSView for" << widget; +#endif + if (view && parent) [parent addSubview:view]; return view; @@ -1630,7 +1671,7 @@ bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up) // to happen, prevent that here (you really want the thing hidden). if (up >= 0 || topData->resizer != 0) topData->resizer += up; - OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->winId())); + OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->effectiveWinId())); { #ifndef QT_MAC_USE_COCOA WindowClass wclass; @@ -1665,6 +1706,7 @@ bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up) void QWidgetPrivate::qt_clean_root_win() { #ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; [qt_root_win release]; #else if(!qt_root_win) @@ -2289,33 +2331,28 @@ void QWidgetPrivate::finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ voidWin } else { [windowRef setHidesOnDeactivate:NO]; } - [windowRef setHasShadow:YES]; + if (q->testAttribute(Qt::WA_MacNoShadow)) + [windowRef setHasShadow:NO]; + else + [windowRef setHasShadow:YES]; Q_UNUSED(parentWidget); Q_UNUSED(dialog); data.fstrut_dirty = true; // when we create a toplevel widget, the frame strut should be dirty + OSViewRef nsview = (OSViewRef)data.winid; - OSViewRef window_contentview = qt_mac_get_contentview_for(windowRef); if (!nsview) { - nsview = qt_mac_create_widget(q, this, window_contentview); + nsview = qt_mac_create_widget(q, this, 0); setWinId(WId(nsview)); - } else { - [window_contentview addSubview:nsview]; } - if (nsview) { - NSRect bounds = [window_contentview bounds]; - [nsview setFrame:bounds]; - [nsview setHidden:NO]; - if (q->testAttribute(Qt::WA_DropSiteRegistered)) - registerDropSite(true); - transferChildren(); + [windowRef setContentView:nsview]; + [nsview setHidden:NO]; + transferChildren(); - // Tell Cocoa explicit that we wan't the view to receive key events - // (regardless of focus policy) because this is how it works on other - // platforms (and in the carbon port): - if (!qApp->focusWidget()) - [windowRef makeFirstResponder:nsview]; - } + // Tell Cocoa explicit that we wan't the view to receive key events + // (regardless of focus policy) because this is how it works on other + // platforms (and in the carbon port): + [windowRef makeFirstResponder:nsview]; if (topExtra->posFromMove) { updateFrameStrut(); @@ -2346,8 +2383,9 @@ void QWidgetPrivate::finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ voidWin q->setAttribute(Qt::WA_WState_WindowOpacitySet, false); } - if (qApp->overrideCursor()) - [windowRef disableCursorRects]; + // Its more performant to handle the mouse cursor + // ourselves, expecially when using alien widgets: + [windowRef disableCursorRects]; setWindowLevel(); macUpdateHideOnSuspend(); @@ -2438,6 +2476,8 @@ void QWidgetPrivate::createWindow_sys() void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow) { Q_Q(QWidget); + QMacCocoaAutoReleasePool pool; + OSViewRef destroyid = 0; #ifndef QT_MAC_USE_COCOA window_event = 0; @@ -2465,8 +2505,6 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO data.crect.setRect(0, 0, desktopSize.width(), desktopSize.height()); dialog = popup = false; // force these flags off } else { - q->setAttribute(Qt::WA_WState_Visible, false); - if (topLevel && (type != Qt::Drawer)) { if (QDesktopWidget *dsk = QApplication::desktop()) { // calc pos/size from screen const bool wasResized = q->testAttribute(Qt::WA_Resized); @@ -2556,6 +2594,8 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO transfer = true; } else if (parentWidget) { // I need to be added to my parent, therefore my parent needs an NSView + // Alien note: a 'window' was supplied as argument, meaning this widget + // is not alien. So therefore the parent cannot be alien either. parentWidget->createWinId(); parent = qt_mac_nativeview_for(parentWidget); } @@ -2627,11 +2667,8 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO data.fstrut_dirty = false; // non-toplevel widgets don't have a frame, so no need to update the strut #ifdef QT_MAC_USE_COCOA - if (q->testAttribute(Qt::WA_NativeWindow) == false || - q->internalWinId() != 0) { -#ifdef ALIEN_DEBUG - qDebug() << "Skipping native widget creation for" << this; -#endif + if (q->testAttribute(Qt::WA_NativeWindow) == false || q->internalWinId() != 0) { + // INVARIANT: q is Alien, and we should not create an NSView to back it up. } else #endif if (OSViewRef osview = qt_mac_create_widget(q, this, qt_mac_nativeview_for(parentWidget))) { @@ -2643,21 +2680,26 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO NSRect bounds = NSMakeRect(data.crect.x(), data.crect.y(), data.crect.width(), data.crect.height()); [osview setFrame:bounds]; setWinId((WId)osview); + if (q->isVisible()) { + // If q were Alien before, but now became native (e.g. if a call to + // winId was done from somewhere), we need to show the view immidiatly: + QMacCocoaAutoReleasePool pool; + [osview setHidden:NO]; + } #endif - if (q->testAttribute(Qt::WA_DropSiteRegistered)) - registerDropSite(true); } } updateIsOpaque(); + + if (q->testAttribute(Qt::WA_DropSiteRegistered)) + registerDropSite(true); if (q->hasFocus()) setFocus_sys(); if (!topLevel && initializeWindow) setWSGeometry(); if (destroyid) qt_mac_destructView(destroyid); - if (q->testAttribute(Qt::WA_AcceptTouchEvents)) - registerTouchWindow(); } /*! @@ -2691,16 +2733,29 @@ QWidget::macCGHandle() const return handle(); } +void qt_mac_updateParentUnderAlienWidget(QWidget *alienWidget) +{ + QWidget *nativeParent = alienWidget->nativeParentWidget(); + if (!nativeParent) + return; + + QPoint globalPos = alienWidget->mapToGlobal(QPoint(0, 0)); + QRect dirtyRect = QRect(nativeParent->mapFromGlobal(globalPos), alienWidget->size()); + nativeParent->update(dirtyRect); +} + void QWidget::destroy(bool destroyWindow, bool destroySubWindows) { Q_D(QWidget); + QMacCocoaAutoReleasePool pool; d->aboutToDestroy(); if (!isWindow() && parentWidget()) parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); + if (!internalWinId()) + qt_mac_updateParentUnderAlienWidget(this); d->deactivateWidgetCleanup(); qt_mac_event_release(this); if(testAttribute(Qt::WA_WState_Created)) { - QMacCocoaAutoReleasePool pool; setAttribute(Qt::WA_WState_Created, false); QObjectList chldrn = children(); for(int i = 0; i < chldrn.size(); i++) { // destroy all widget children @@ -2756,7 +2811,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) void QWidgetPrivate::transferChildren() { Q_Q(QWidget); - if (!q->testAttribute(Qt::WA_WState_Created)) + if (!q->internalWinId()) return; // Can't add any views anyway QObjectList chlist = q->children(); @@ -2768,7 +2823,7 @@ void QWidgetPrivate::transferChildren() // This seems weird, no need to call it in a loop right? if (!topData()->caption.isEmpty()) setWindowTitle_helper(extra->topextra->caption); - if (w->testAttribute(Qt::WA_WState_Created)) { + if (w->internalWinId()) { #ifndef QT_MAC_USE_COCOA HIViewAddSubview(qt_mac_nativeview_for(q), qt_mac_nativeview_for(w)); #else @@ -2909,11 +2964,11 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) #ifndef QT_MAC_USE_COCOA old_window_event = window_event; #else - OSWindowRef oldWindow = qt_mac_window_for(old_id); if (qt_mac_is_macdrawer(q)) { oldDrawer = qt_mac_drawer_for(q); } if (wasWindow) { + OSWindowRef oldWindow = qt_mac_window_for(old_id); oldToolbar = [oldWindow toolbar]; if (oldToolbar) { [oldToolbar retain]; @@ -2951,7 +3006,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) // unless this is an alien widget. ) const bool nonWindowWithCreatedParent = !q->isWindow() && parent->testAttribute(Qt::WA_WState_Created); const bool nativeWidget = q->internalWinId() != 0; - if (wasCreated || nativeWidget && nonWindowWithCreatedParent) { + if (wasCreated || (nativeWidget && nonWindowWithCreatedParent)) { createWinId(); if (q->isWindow()) { #ifndef QT_MAC_USE_COCOA @@ -2977,6 +3032,16 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) q->setAttribute(Qt::WA_WState_Hidden); q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden); +#ifdef QT_MAC_USE_COCOA + // If we add a child to the unified toolbar, we have to redirect the painting. + if (parent && parent->d_func() && parent->d_func()->isInUnifiedToolbar) { + if (parent->d_func()->unifiedSurface) { + QWidget *toolbar = parent->d_func()->toolbar_ancestor; + parent->d_func()->unifiedSurface->recursiveRedirect(toolbar, toolbar, toolbar->d_func()->toolbar_offset); + } + } +#endif // QT_MAC_USE_COCOA + if (wasCreated) { transferChildren(); #ifndef QT_MAC_USE_COCOA @@ -3035,7 +3100,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) QPoint QWidget::mapToGlobal(const QPoint &pos) const { Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { + if (!internalWinId()) { QPoint p = pos + data->crect.topLeft(); return isWindow() ? p : parentWidget()->mapToGlobal(p); } @@ -3062,7 +3127,7 @@ QPoint QWidget::mapToGlobal(const QPoint &pos) const QPoint QWidget::mapFromGlobal(const QPoint &pos) const { Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { + if (!internalWinId()) { QPoint p = isWindow() ? pos : parentWidget()->mapFromGlobal(pos); return p - data->crect.topLeft(); } @@ -3089,28 +3154,12 @@ void QWidgetPrivate::updateSystemBackground() void QWidgetPrivate::setCursor_sys(const QCursor &) { -#ifndef QT_MAC_USE_COCOA qt_mac_update_cursor(); -#else - Q_Q(QWidget); - if (q->testAttribute(Qt::WA_WState_Created)) { - QMacCocoaAutoReleasePool pool; - [qt_mac_window_for(q) invalidateCursorRectsForView:qt_mac_nativeview_for(q)]; - } -#endif } void QWidgetPrivate::unsetCursor_sys() { -#ifndef QT_MAC_USE_COCOA qt_mac_update_cursor(); -#else - Q_Q(QWidget); - if (q->testAttribute(Qt::WA_WState_Created)) { - QMacCocoaAutoReleasePool pool; - [qt_mac_window_for(q) invalidateCursorRectsForView:qt_mac_nativeview_for(q)]; - } -#endif } void QWidgetPrivate::setWindowTitle_sys(const QString &caption) @@ -3214,11 +3263,13 @@ void QWidgetPrivate::setWindowIcon_sys(bool forceReset) ReleaseIconRef(previousIcon); #else QMacCocoaAutoReleasePool pool; + if (icon.isNull()) + return; NSButton *iconButton = [qt_mac_window_for(q) standardWindowButton:NSWindowDocumentIconButton]; if (iconButton == nil) { QCFString string(q->windowTitle()); const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string); - [qt_mac_window_for(q) setRepresentedURL:[NSURL fileURLWithPath:tmpString]]; + [qt_mac_window_for(q) setRepresentedURL:[NSURL fileURLWithPath:const_cast<NSString *>(tmpString)]]; iconButton = [qt_mac_window_for(q) standardWindowButton:NSWindowDocumentIconButton]; } if (icon.isNull()) { @@ -3252,24 +3303,28 @@ void QWidget::grabMouse() if(mac_mouse_grabber) mac_mouse_grabber->releaseMouse(); mac_mouse_grabber=this; + qt_mac_setMouseGrabCursor(true); } } #ifndef QT_NO_CURSOR -void QWidget::grabMouse(const QCursor &) +void QWidget::grabMouse(const QCursor &cursor) { if(isVisible() && !qt_nograb()) { if(mac_mouse_grabber) mac_mouse_grabber->releaseMouse(); mac_mouse_grabber=this; + qt_mac_setMouseGrabCursor(true, const_cast<QCursor *>(&cursor)); } } #endif void QWidget::releaseMouse() { - if(!qt_nograb() && mac_mouse_grabber == this) + if(!qt_nograb() && mac_mouse_grabber == this) { mac_mouse_grabber = 0; + qt_mac_setMouseGrabCursor(false); + } } void QWidget::grabKeyboard() @@ -3354,35 +3409,10 @@ QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys() void QWidgetPrivate::update_sys(const QRect &r) { Q_Q(QWidget); - if (r == q->rect()) { - if (updateRedirectedToGraphicsProxyWidget(q, r)) - return; - dirtyOnWidget += r; -#ifndef QT_MAC_USE_COCOA - HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); -#else - qt_mac_set_needs_display(q, QRegion()); -#endif + if (updateRedirectedToGraphicsProxyWidget(q, r)) return; - } - - int x = r.x(), y = r.y(), w = r.width(), h = r.height(); - if (w < 0) - w = q->data->crect.width() - x; - if (h < 0) - h = q->data->crect.height() - y; - if (w && h) { - const QRect updateRect = QRect(x, y, w, h); - if (updateRedirectedToGraphicsProxyWidget(q, updateRect)) - return; -#ifndef QT_MAC_USE_COCOA - dirtyOnWidget += updateRect; - HIRect r = CGRectMake(x, y, w, h); - HIViewSetNeedsDisplayInRect(qt_mac_nativeview_for(q), &r, true); -#else - [qt_mac_nativeview_for(q) setNeedsDisplayInRect:NSMakeRect(x, y, w, h)]; -#endif - } + dirtyOnWidget += r; + macSetNeedsDisplay(r != q->rect() ? r : QRegion()); } void QWidgetPrivate::update_sys(const QRegion &rgn) @@ -3391,33 +3421,7 @@ void QWidgetPrivate::update_sys(const QRegion &rgn) if (updateRedirectedToGraphicsProxyWidget(q, rgn)) return; dirtyOnWidget += rgn; -#ifndef QT_MAC_USE_COCOA - RgnHandle rgnHandle = rgn.toQDRgnForUpdate_sys(); - if (rgnHandle) - HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgnHandle), true); - else { - HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); // do a complete repaint on overflow. - } -#else - // Alien support: get the first native ancestor widget (will be q itself in the non-alien case), - // map the coordinates from q space to NSView space and invalidate the rect. - QWidget *nativeParent = q->internalWinId() ? q : q->nativeParentWidget(); - if (nativeParent == 0) - return; - - QVector<QRect> rects = rgn.rects(); - for (int i = 0; i < rects.count(); ++i) { - const QRect &rect = rects.at(i); - - const QRect nativeBoundingRect = QRect( - QPoint(q->mapTo(nativeParent, rect.topLeft())), - QSize(rect.size())); - - [qt_mac_nativeview_for(nativeParent) setNeedsDisplayInRect:NSMakeRect(nativeBoundingRect.x(), - nativeBoundingRect.y(), nativeBoundingRect.width(), - nativeBoundingRect.height())]; - } -#endif + macSetNeedsDisplay(rgn); } bool QWidgetPrivate::isRealWindow() const @@ -3456,7 +3460,6 @@ void QWidgetPrivate::show_sys() data.fstrut_dirty = true; if (realWindow) { - // Delegates can change window state, so record some things earlier. bool isCurrentlyMinimized = (q->windowState() & Qt::WindowMinimized); setModal_sys(); OSWindowRef window = qt_mac_window_for(q); @@ -3487,7 +3490,10 @@ void QWidgetPrivate::show_sys() QWidget *top = 0; if (QApplicationPrivate::tryModalHelper(q, &top)) { - [window makeKeyAndOrderFront:window]; + if (q->testAttribute(Qt::WA_ShowWithoutActivating)) + [window orderFront:window]; + else + [window makeKeyAndOrderFront:window]; // If this window is app modal, we need to start spinning // a modal session for it. Interrupting // the event dispatcher will make this happend: @@ -3503,9 +3509,11 @@ void QWidgetPrivate::show_sys() } } setSubWindowStacking(true); + qt_mac_update_cursor(); #endif if (q->windowType() == Qt::Popup) { - if (q->focusWidget()) + qt_button_down = 0; + if (q->focusWidget()) q->focusWidget()->d_func()->setFocus_sys(); else setFocus_sys(); @@ -3527,21 +3535,34 @@ void QWidgetPrivate::show_sys() #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), true); #else - [qt_mac_nativeview_for(q) setHidden:NO]; - + if (NSView *view = qt_mac_nativeview_for(q)) { + // INVARIANT: q is native. Just show the view: + [view setHidden:NO]; + } else { + // INVARIANT: q is alien. Update q instead: + q->update(); + } #endif } - if (!QWidget::mouseGrabber()){ - QWidget *enterWidget = QApplication::widgetAt(QCursor::pos()); - QApplicationPrivate::dispatchEnterLeave(enterWidget, qt_mouseover); - qt_mouseover = enterWidget; +#ifdef QT_MAC_USE_COCOA + if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_mouse_receiver); + qt_last_mouse_receiver = widgetUnderMouse; + qt_last_native_mouse_receiver = widgetUnderMouse ? + (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0; } +#endif + topLevelAt_cache = 0; qt_event_request_window_change(q); } - QPoint qt_mac_nativeMapFromParent(const QWidget *child, const QPoint &pt) { #ifndef QT_MAC_USE_COCOA @@ -3622,6 +3643,7 @@ void QWidgetPrivate::hide_sys() } #endif toggleDrawers(false); + qt_mac_update_cursor(); #ifndef QT_MAC_USE_COCOA // Clear modality (because it seems something that we've always done). if (data.window_modality != Qt::NonModal) { @@ -3669,18 +3691,31 @@ void QWidgetPrivate::hide_sys() #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), false); #else - [qt_mac_nativeview_for(q) setHidden:YES]; + if (NSView *view = qt_mac_nativeview_for(q)) { + // INVARIANT: q is native. Just hide the view: + [view setHidden:YES]; + } else { + // INVARIANT: q is alien. Repaint where q is placed instead: + qt_mac_updateParentUnderAlienWidget(q); + } #endif } - if (!QWidget::mouseGrabber()){ - QWidget *enterWidget = QApplication::widgetAt(QCursor::pos()); - if (enterWidget && enterWidget->data->in_destructor) - enterWidget = 0; - QApplicationPrivate::dispatchEnterLeave(enterWidget, qt_mouseover); - qt_mouseover = enterWidget; - } - +#ifdef QT_MAC_USE_COCOA + if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Leave, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_native_mouse_receiver); + qt_last_mouse_receiver = widgetUnderMouse; + qt_last_native_mouse_receiver = widgetUnderMouse ? + (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0; + } +#endif + + topLevelAt_cache = 0; qt_event_request_window_change(q); deactivateWidgetCleanup(); qt_mac_event_release(q); @@ -3903,12 +3938,14 @@ void QWidgetPrivate::raise_sys() QWidget *parentWidget = q->parentWidget(); if(parentWidget) { OSWindowRef parentWindow = qt_mac_window_for(parentWidget); - if(parentWindow && [parentWindow isOnActiveSpace]) { - // The window was created in a different space. Therefore if we want - // to show it in the current space we need to recreate it in the new - // space. - recreateMacWindow(); - window = qt_mac_window_for(q); + if(parentWindow && [parentWindow respondsToSelector:@selector(isOnActiveSpace)]) { + if ([parentWindow performSelector:@selector(isOnActiveSpace)]) { + // The window was created in a different space. Therefore if we want + // to show it in the current space we need to recreate it in the new + // space. + recreateMacWindow(); + window = qt_mac_window_for(q); + } } } } @@ -3925,6 +3962,7 @@ void QWidgetPrivate::raise_sys() NSView *parentView = [view superview]; [parentView sortSubviewsUsingFunction:compareViews2Raise context:reinterpret_cast<void *>(view)]; } + topLevelAt_cache = 0; #else if(q->isWindow()) { //raise this window @@ -3965,6 +4003,7 @@ void QWidgetPrivate::lower_sys() NSView *parentView = [view superview]; [parentView sortSubviewsUsingFunction:compareViews2Lower context:reinterpret_cast<void *>(view)]; } + topLevelAt_cache = 0; #else if(q->isWindow()) { SendBehind(qt_mac_window_for(q), 0); @@ -4021,6 +4060,7 @@ void QWidgetPrivate::stackUnder_sys(QWidget *w) #endif } +#ifndef QT_MAC_USE_COCOA /* Modifies the bounds for a widgets backing HIView during moves and resizes. Also updates the widget, either by scrolling its contents or repainting, depending on the WA_StaticContents @@ -4028,7 +4068,6 @@ void QWidgetPrivate::stackUnder_sys(QWidget *w) */ static void qt_mac_update_widget_position(QWidget *q, QRect oldRect, QRect newRect) { -#ifndef QT_MAC_USE_COCOA HIRect bounds = CGRectMake(newRect.x(), newRect.y(), newRect.width(), newRect.height()); @@ -4120,13 +4159,8 @@ static void qt_mac_update_widget_position(QWidget *q, QRect oldRect, QRect newRe HIViewSetNeedsDisplayInRect(view, &verticalSlice, true); const HIRect horizontalSlice = CGRectMake(0, starty, startx, stopy); HIViewSetNeedsDisplayInRect(view, &horizontalSlice, true); -#else - Q_UNUSED(oldRect); - NSRect bounds = NSMakeRect(newRect.x(), newRect.y(), - newRect.width(), newRect.height()); - [qt_mac_nativeview_for(q) setFrame:bounds]; -#endif } +#endif /* Helper function for non-toplevel widgets. Helps to map Qt's 32bit @@ -4147,7 +4181,15 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) { Q_Q(QWidget); Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - Q_UNUSED(oldRect); + + if (!q->internalWinId() && QApplicationPrivate::graphicsSystem() != 0) { + // We have no view to move, and no paint engine that + // we can update dirty regions on. So just return: + return; + } + + QMacCocoaAutoReleasePool pool; + /* There are up to four different coordinate systems here: Qt coordinate system for this widget. @@ -4155,13 +4197,31 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) Qt coordinate system for parent X coordinate system for parent (relative to parent's wrect). */ + + // wrect is the same as crect, except that it is + // clipped to fit inside parent (and screen): QRect wrect; - //xrect is the X geometry of my X widget. (starts out in parent's Qt coord sys, and ends up in parent's X coord sys) - QRect xrect = data.crect; + // wrectInParentCoordSys will be the same as wrect, except that it is + // originated in q's parent rather than q itself. It starts out in + // parent's Qt coord system, and ends up in parent's coordinate system: + QRect wrectInParentCoordSys = data.crect; + + // If q's parent has been clipped, parentWRect will + // be filled with the parents clipped crect: QRect parentWRect; + + // Embedded have different meaning on each platform, and on + // Mac, it means that q is a QMacNativeWidget. bool isEmbeddedWindow = (q->isWindow() && topData()->embedded); - if (isEmbeddedWindow) { +#ifdef QT_MAC_USE_COCOA + NSView *nsview = qt_mac_nativeview_for(q); +#endif + if (!isEmbeddedWindow) { + parentWRect = q->parentWidget()->data->wrect; + } else { + // INVARIANT: q's parent view is not owned by Qt. So we need to + // do some extra calls to get the clipped rect of the parent view: #ifndef QT_MAC_USE_COCOA HIViewRef parentView = HIViewGetSuperview(qt_mac_nativeview_for(q)); #else @@ -4180,43 +4240,57 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) const QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX); parentWRect = wrectRange; } - } else { - parentWRect = q->parentWidget()->data->wrect; } if (parentWRect.isValid()) { - // parent is clipped, and we have to clip to the same limit as parent - if (!parentWRect.contains(xrect) && !isEmbeddedWindow) { - xrect &= parentWRect; - wrect = xrect; - //translate from parent's to my Qt coord sys + // INVARIANT: q's parent has been clipped. + // So we fit our own wrects inside it: + if (!parentWRect.contains(wrectInParentCoordSys) && !isEmbeddedWindow) { + wrectInParentCoordSys &= parentWRect; + wrect = wrectInParentCoordSys; + // Make sure wrect is originated in q's coordinate system: wrect.translate(-data.crect.topLeft()); } - //translate from parent's Qt coords to parent's X coords - xrect.translate(-parentWRect.topLeft()); - + // // Make sure wrectInParentCoordSys originated in q's parent coordinate system: + wrectInParentCoordSys.translate(-parentWRect.topLeft()); } else { - // parent is not clipped, we may or may not have to clip + // INVARIANT: we dont know yet the clipping rect of q's parent. + // So we may or may not have to adjust our wrects: if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) { - // This is where the main optimization is: we are already - // clipped, and if our clip is still valid, we can just - // move our window, and do not need to move or clip - // children + // This is where the main optimization is: we have an old wrect from an earlier + // setGeometry call, and the new crect is smaller than it. If the final wrect is + // also inside the old wrect, we can just move q and its children to the new + // location without any clipping: + + // vrect will be the part of q that's will be visible inside + // q's parent. If it inside the old wrect, then we can just move: + QRect vrect = wrectInParentCoordSys & q->parentWidget()->rect(); + vrect.translate(-data.crect.topLeft()); - QRect vrect = xrect & q->parentWidget()->rect(); - vrect.translate(-data.crect.topLeft()); //the part of me that's visible through parent, in my Qt coords if (data.wrect.contains(vrect)) { - xrect = data.wrect; - xrect.translate(data.crect.topLeft()); + wrectInParentCoordSys = data.wrect; + wrectInParentCoordSys.translate(data.crect.topLeft()); #ifndef QT_MAC_USE_COCOA - HIRect bounds = CGRectMake(xrect.x(), xrect.y(), - xrect.width(), xrect.height()); + HIRect bounds = CGRectMake(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); HIViewSetFrame(qt_mac_nativeview_for(q), &bounds); #else - NSRect bounds = NSMakeRect(xrect.x(), xrect.y(), - xrect.width(), xrect.height()); - [qt_mac_nativeview_for(q) setFrame:bounds]; + if (nsview) { + // INVARIANT: q is native. Set view frame: + NSRect bounds = NSMakeRect(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); + [nsview setFrame:bounds]; + } else { + // INVARIANT: q is alien. Repaint wrect instead (includes old and new wrect): + QWidget *parent = q->parentWidget(); + QPoint globalPosWRect = parent->mapToGlobal(data.wrect.topLeft()); + + QWidget *nativeParent = q->nativeParentWidget(); + QRect dirtyWRect = QRect(nativeParent->mapFromGlobal(globalPosWRect), data.wrect.size()); + + nativeParent->update(dirtyWRect); + } #endif if (q->testAttribute(Qt::WA_OutsideWSRange)) { q->setAttribute(Qt::WA_OutsideWSRange, false); @@ -4225,7 +4299,8 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), true); #else - [qt_mac_nativeview_for(q) setHidden:NO]; + // If q is Alien, the following call does nothing: + [nsview setHidden:NO]; #endif } } @@ -4233,9 +4308,10 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) } } +#ifndef QT_MAC_USE_COCOA const QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX); - if (!validRange.contains(xrect)) { - // we are too big, and must clip + if (!validRange.contains(wrectInParentCoordSys)) { + // We're too big, and must clip: QPoint screenOffset(0, 0); // offset of the part being on screen const QWidget *parentWidget = q->parentWidget(); while (parentWidget && !parentWidget->isWindow()) { @@ -4247,14 +4323,15 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) 2*WRECT_MAX, 2*WRECT_MAX); - xrect &=cropRect; - wrect = xrect; - wrect.translate(-data.crect.topLeft()); // translate wrect in my Qt coordinates + wrectInParentCoordSys &=cropRect; + wrect = wrectInParentCoordSys; + wrect.translate(-data.crect.topLeft()); } +#endif //QT_MAC_USE_COCOA } // unmap if we are outside the valid window system coord system - bool outsideRange = !xrect.isValid(); + bool outsideRange = !wrectInParentCoordSys.isValid(); bool mapWindow = false; if (q->testAttribute(Qt::WA_OutsideWSRange) != outsideRange) { q->setAttribute(Qt::WA_OutsideWSRange, outsideRange); @@ -4262,7 +4339,8 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), false); #else - [qt_mac_nativeview_for(q) setHidden:YES]; + // If q is Alien, the following call does nothing: + [nsview setHidden:YES]; #endif q->setAttribute(Qt::WA_Mapped, false); } else if (!q->isHidden()) { @@ -4273,10 +4351,10 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) if (outsideRange) return; + // Store the new clipped rect: bool jump = (data.wrect != wrect); data.wrect = wrect; - // and now recursively for all children... // ### can be optimized for (int i = 0; i < children.size(); ++i) { @@ -4288,17 +4366,56 @@ void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) } } - qt_mac_update_widget_position(q, oldRect, xrect); - - if (jump) +#ifndef QT_MAC_USE_COCOA + // Move the actual HIView: + qt_mac_update_widget_position(q, oldRect, wrectInParentCoordSys); + if (jump) q->update(); +#else + if (nsview) { + // INVARIANT: q is native. Move the actual NSView: + NSRect bounds = NSMakeRect( + wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); + [nsview setFrame:bounds]; + if (jump) + q->update(); + } else if (QApplicationPrivate::graphicsSystem() == 0){ + // INVARIANT: q is alien and we use native paint engine. + // Schedule updates where q is moved from and to: + const QWidget *parent = q->parentWidget(); + const QPoint globalPosOldWRect = parent->mapToGlobal(oldRect.topLeft()); + const QPoint globalPosNewWRect = parent->mapToGlobal(wrectInParentCoordSys.topLeft()); + + QWidget *nativeParent = q->nativeParentWidget(); + const QRegion dirtyOldWRect = QRect(nativeParent->mapFromGlobal(globalPosOldWRect), oldRect.size()); + const QRegion dirtyNewWRect = QRect(nativeParent->mapFromGlobal(globalPosNewWRect), wrectInParentCoordSys.size()); + + const bool sizeUnchanged = oldRect.size() == wrectInParentCoordSys.size(); + const bool posUnchanged = oldRect.topLeft() == wrectInParentCoordSys.topLeft(); + + // Resolve/minimize the region that needs to update: + if (sizeUnchanged && q->testAttribute(Qt::WA_OpaquePaintEvent)) { + // INVARIANT: q is opaque, and is only moved (not resized). So in theory we only + // need to blit pixels, and skip a repaint. But we can only make this work if we + // had access to the backbuffer, so we need to update all: + nativeParent->update(dirtyOldWRect | dirtyNewWRect); + } else if (posUnchanged && q->testAttribute(Qt::WA_StaticContents)) { + // We only need to redraw exposed areas: + nativeParent->update(dirtyNewWRect - dirtyOldWRect); + } else { + nativeParent->update(dirtyOldWRect | dirtyNewWRect); + } + } +#endif if (mapWindow && !dontShow) { q->setAttribute(Qt::WA_Mapped); #ifndef QT_MAC_USE_COCOA HIViewSetVisible(qt_mac_nativeview_for(q), true); #else - [qt_mac_nativeview_for(q) setHidden:NO]; + // If q is Alien, the following call does nothing: + [nsview setHidden:NO]; #endif } } @@ -4333,6 +4450,8 @@ void QWidgetPrivate::adjustWithinMaxAndMinSize(int &w, int &h) void QWidgetPrivate::applyMaxAndMinSizeOnWindow() { Q_Q(QWidget); + QMacCocoaAutoReleasePool pool; + const float max_f(20000); #ifndef QT_MAC_USE_COCOA #define SF(x) ((x > max_f) ? max_f : x) @@ -4360,7 +4479,6 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) QMacCocoaAutoReleasePool pool; bool realWindow = isRealWindow(); - BOOL needDisplay = realWindow ? YES : NO; if (realWindow && !q->testAttribute(Qt::WA_DontShowOnScreen)){ adjustWithinMaxAndMinSize(w, h); @@ -4408,7 +4526,7 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) if (currTopLeft.x() == x && currTopLeft.y() == y && cocoaFrameRect.size.width != 0 && cocoaFrameRect.size.height != 0) { - [window setFrame:cocoaFrameRect display:needDisplay]; + [window setFrame:cocoaFrameRect display:realWindow]; } else { // The window is moved and resized (or resized to zero). // Since Cocoa usually only sends us a resize callback after @@ -4417,7 +4535,7 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) // would have the same origin as the setFrame call) we shift the // window back and forth inbetween. cocoaFrameRect.origin.y += 1; - [window setFrame:cocoaFrameRect display:needDisplay]; + [window setFrame:cocoaFrameRect display:realWindow]; cocoaFrameRect.origin.y -= 1; [window setFrameOrigin:cocoaFrameRect.origin]; } @@ -4425,6 +4543,8 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) } else { setGeometry_sys_helper(x, y, w, h, isMove); } + + topLevelAt_cache = 0; } void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isMove) @@ -4473,17 +4593,11 @@ void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isM const QRect oldRect(oldp, olds); if (!isResize && QApplicationPrivate::graphicsSystem()) moveRect(oldRect, x - oldp.x(), y - oldp.y()); + setWSGeometry(false, oldRect); - if (isResize && QApplicationPrivate::graphicsSystem()) { - invalidateBuffer(q->rect()); - if (extra && !graphicsEffect && !extra->mask.isEmpty()) { - QRegion oldRegion(extra->mask.translated(oldp)); - oldRegion &= oldRect; - q->parentWidget()->d_func()->invalidateBuffer(oldRegion); - } else { - q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(oldRect)); - } - } + + if (isResize && QApplicationPrivate::graphicsSystem()) + invalidateBuffer_resizeHelper(oldp, olds); } if(isMove || isResize) { @@ -4563,6 +4677,7 @@ void QWidgetPrivate::updateMaximizeButton_sys() void QWidgetPrivate::scroll_sys(int dx, int dy) { if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) { + // INVARIANT: Alien paint engine scrollChildren(dx, dy); scrollRect(q_func()->rect(), dx, dy); } else { @@ -4570,37 +4685,81 @@ void QWidgetPrivate::scroll_sys(int dx, int dy) } } -void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) +void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &qscrollRect) { - Q_Q(QWidget); + if (QMacScrollOptimization::delayScroll(this, dx, dy, qscrollRect)) + return; + Q_Q(QWidget); if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) { - scrollRect(r, dx, dy); + // INVARIANT: Alien paint engine + scrollRect(qscrollRect, dx, dy); return; } - const bool valid_rect = r.isValid(); - if (!q->updatesEnabled() && (valid_rect || q->children().isEmpty())) - return; + static int accelEnv = -1; + if (accelEnv == -1) { + accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0; + } - qt_event_request_window_change(q); + // Scroll the whole widget if qscrollRect is not valid: + QRect validScrollRect = qscrollRect.isValid() ? qscrollRect : q->rect(); + validScrollRect &= clipRect(); + + // If q is overlapped by other widgets, we cannot just blit pixels since + // this will move overlapping widgets as well. In case we just update: + const bool overlapped = isOverlapped(validScrollRect.translated(data.crect.topLeft())); + const bool accelerateScroll = accelEnv && isOpaque && !overlapped; + const bool isAlien = (q->internalWinId() == 0); + const QPoint scrollDelta(dx, dy); + + // If qscrollRect is valid, we are _not_ supposed to scroll q's children (as documented). + // But we do scroll children (and the whole of q) if qscrollRect is invalid. This case is + // documented as undefined, but we exploit it to help factor our code into one function. + const bool scrollChildren = !qscrollRect.isValid(); + + if (!q->updatesEnabled()) { + // We are told not to update anything on q at this point. So unless + // we are supposed to scroll children, we bail out early: + if (!scrollChildren || q->children().isEmpty()) + return; + } + + if (!accelerateScroll) { + if (overlapped) { + QRegion region(validScrollRect); + subtractOpaqueSiblings(region); + update_sys(region); + }else { + update_sys(qscrollRect); + } + return; + } #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; +#else + Q_UNUSED(isAlien); + // We're not sure what the following call is supposed to achive + // but until we see what it breaks, we don't bring it into the + // Cocoa port: + qt_event_request_window_change(q); #endif - if(!valid_rect) { // scroll children - QPoint pd(dx, dy); - QWidgetList moved; - QObjectList chldrn = q->children(); - for(int i = 0; i < chldrn.size(); i++) { //first move all children - QObject *obj = chldrn.at(i); - if(obj->isWidgetType()) { - QWidget *w = (QWidget*)obj; - if(!w->isWindow()) { - w->data->crect = QRect(w->pos() + pd, w->size()); - if (w->testAttribute(Qt::WA_WState_Created)) { + // First move all native children. Alien children will indirectly be + // moved when the parent is scrolled. All directly or indirectly moved + // children will receive a move event before the function call returns. + QWidgetList movedChildren; + if (scrollChildren) { + QObjectList children = q->children(); + + for (int i=0; i<children.size(); i++) { + QObject *obj = children.at(i); + if (QWidget *w = qobject_cast<QWidget*>(obj)) { + if (!w->isWindow()) { + w->data->crect = QRect(w->pos() + scrollDelta, w->size()); #ifndef QT_MAC_USE_COCOA + if (w->testAttribute(Qt::WA_WState_Created)) { HIRect bounds = CGRectMake(w->data->crect.x(), w->data->crect.y(), w->data->crect.width(), w->data->crect.height()); HIViewRef hiview = qt_mac_nativeview_for(w); @@ -4611,83 +4770,118 @@ void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) HIViewSetFrame(hiview, &bounds); if (opaque) HIViewSetDrawingEnabled(hiview, true); + } #else - [qt_mac_nativeview_for(w) - setFrame:NSMakeRect(w->data->crect.x(), w->data->crect.y(), - w->data->crect.width(), w->data->crect.height())]; -#endif + if (NSView *view = qt_mac_nativeview_for(w)) { + // INVARIANT: w is not alien + [view setFrame:NSMakeRect( + w->data->crect.x(), w->data->crect.y(), + w->data->crect.width(), w->data->crect.height())]; } - moved.append(w); +#endif + movedChildren.append(w); } } } - //now send move events (do not do this in the above loop, breaks QAquaFocusWidget) - for(int i = 0; i < moved.size(); i++) { - QWidget *w = moved.at(i); - QMoveEvent e(w->pos(), w->pos() - pd); - QApplication::sendEvent(w, &e); - } } - if (!q->testAttribute(Qt::WA_WState_Created) || !q->isVisible()) - return; + if (q->testAttribute(Qt::WA_WState_Created) && q->isVisible()) { + // Scroll q itself according to the qscrollRect, and + // call update on any exposed areas so that they get redrawn: - OSViewRef view = qt_mac_nativeview_for(q); #ifndef QT_MAC_USE_COCOA - HIRect scrollrect = CGRectMake(r.x(), r.y(), r.width(), r.height()); - OSStatus err = _HIViewScrollRectWithOptions(view, valid_rect ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); - if (err) { - // The only parameter that can go wrong, is the rect. - qWarning("QWidget::scroll: Your rectangle was too big for the widget, clipping rect"); - scrollrect = CGRectMake(qMax(r.x(), 0), qMax(r.y(), 0), - qMin(r.width(), q->width()), qMin(r.height(), q->height())); - _HIViewScrollRectWithOptions(view, valid_rect ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); - } + OSViewRef view = qt_mac_nativeview_for(q); + HIRect scrollrect = CGRectMake(qscrollRect.x(), qscrollRect.y(), qscrollRect.width(), qscrollRect.height()); + OSStatus err = _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); + if (err) { + // The only parameter that can go wrong, is the rect. + qWarning("QWidget::scroll: Your rectangle was too big for the widget, clipping rect"); + scrollrect = CGRectMake(qMax(qscrollRect.x(), 0), qMax(qscrollRect.y(), 0), + qMin(qscrollRect.width(), q->width()), qMin(qscrollRect.height(), q->height())); + _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); + } #else - NSRect scrollRect = valid_rect ? NSMakeRect(r.x(), r.y(), r.width(), r.height()) - : NSMakeRect(0, 0, q->width(), q->height()); + QWidget *nativeWidget = isAlien ? q->nativeParentWidget() : q; + if (!nativeWidget) + return; + OSViewRef view = qt_mac_nativeview_for(nativeWidget); + if (!view) + return; - // calc the updateRect - NSRect deltaXRect = { {0, 0}, {0, 0} }; - NSRect deltaYRect = { {0, 0}, {0, 0} }; - if (dy != 0) { - deltaYRect.size.width = scrollRect.size.width; - if (dy > 0) { - deltaYRect.size.height = dy; - } else { - deltaYRect.size.height = -dy; - deltaYRect.origin.y = scrollRect.size.height + dy; + // Calculate the rectangles that needs to be redrawn + // after the scroll. This will be source rect minus destination rect: + QRect deltaXRect; + if (dx != 0) { + deltaXRect.setY(validScrollRect.y()); + deltaXRect.setHeight(validScrollRect.height()); + if (dx > 0) { + deltaXRect.setX(validScrollRect.x()); + deltaXRect.setWidth(dx); + } else { + deltaXRect.setX(validScrollRect.x() + validScrollRect.width() + dx); + deltaXRect.setWidth(-dx); + } } - } - if (dx != 0) { - deltaXRect.size.height = scrollRect.size.height; - if (dx > 0) { - deltaXRect.size.width = dx; - } else { - deltaXRect.size.width = -dx; - deltaXRect.origin.x = scrollRect.size.width + dx; + + QRect deltaYRect; + if (dy != 0) { + deltaYRect.setX(validScrollRect.x()); + deltaYRect.setWidth(validScrollRect.width()); + if (dy > 0) { + deltaYRect.setY(validScrollRect.y()); + deltaYRect.setHeight(dy); + } else { + deltaYRect.setY(validScrollRect.y() + validScrollRect.height() + dy); + deltaYRect.setHeight(-dy); + } } - } - // ### Scroll the dirty regions as well, the following is not correct. - QRegion displayRegion = r.isNull() ? dirtyOnWidget : (dirtyOnWidget & r); - const QVector<QRect> &rects = dirtyOnWidget.rects(); - const QVector<QRect>::const_iterator end = rects.end(); - QVector<QRect>::const_iterator it = rects.begin(); - while (it != end) { - const QRect rect = *it; - const NSRect dirtyRect = NSMakeRect(rect.x() + dx, rect.y() + dy, - rect.width(), rect.height()); - [view setNeedsDisplayInRect:dirtyRect]; - ++it; - } + if (isAlien) { + // Adjust the scroll rect to the location as seen from the native parent: + QPoint scrollTopLeftInsideNative = nativeWidget->mapFromGlobal(q->mapToGlobal(validScrollRect.topLeft())); + validScrollRect.moveTo(scrollTopLeftInsideNative); + } + + // Make the pixel copy rect within the validScrollRect bounds: + NSRect nsscrollRect = NSMakeRect( + validScrollRect.x() + (dx < 0 ? -dx : 0), + validScrollRect.y() + (dy < 0 ? -dy : 0), + validScrollRect.width() + (dx > 0 ? -dx : 0), + validScrollRect.height() + (dy > 0 ? -dy : 0)); + + NSSize deltaSize = NSMakeSize(dx, dy); + [view scrollRect:nsscrollRect by:deltaSize]; + + // Some areas inside the scroll rect might have been marked as dirty from before, which + // means that they are scheduled to be redrawn. But as we now scroll, those dirty rects + // should also move along to ensure that q receives repaints on the correct places. + // Since some of the dirty rects might lay outside, or only intersect with, the scroll + // rect, the old calls to setNeedsDisplay still makes sense. + // NB: Using [view translateRectsNeedingDisplayInRect:nsscrollRect by:deltaSize] have + // so far not been proven fruitful to solve this problem. + const QVector<QRect> &dirtyRectsToScroll = dirtyOnWidget.rects(); + for (int i=0; i<dirtyRectsToScroll.size(); ++i) { + QRect qdirtyRect = dirtyRectsToScroll[i]; + qdirtyRect.translate(dx, dy); + update_sys(qdirtyRect); + } + + // Update newly exposed areas. This will generate new dirty areas on + // q, and therefore, we do it after updating the old dirty rects above: + if (dx != 0) + update_sys(deltaXRect); + if (dy != 0) + update_sys(deltaYRect); - NSSize deltaSize = NSMakeSize(dx, dy); - [view scrollRect:scrollRect by:deltaSize]; - [view setNeedsDisplayInRect:deltaXRect]; - [view setNeedsDisplayInRect:deltaYRect]; #endif // QT_MAC_USE_COCOA + } + + for (int i=0; i<movedChildren.size(); i++) { + QWidget *w = movedChildren.at(i); + QMoveEvent e(w->pos(), w->pos() - scrollDelta); + QApplication::sendEvent(w, &e); + } } int QWidget::metric(PaintDeviceMetric m) const @@ -4698,16 +4892,19 @@ int QWidget::metric(PaintDeviceMetric m) const case PdmWidthMM: return qRound(metric(PdmWidth) * 25.4 / qreal(metric(PdmDpiX))); case PdmHeight: - case PdmWidth: { + case PdmWidth: #ifndef QT_MAC_USE_COCOA - HIRect rect; + { HIRect rect; HIViewGetFrame(qt_mac_nativeview_for(this), &rect); -#else - NSRect rect = [qt_mac_nativeview_for(this) frame]; -#endif if(m == PdmWidth) return (int)rect.size.width; return (int)rect.size.height; } +#else + if (m == PdmWidth) + return data->crect.width(); + else + return data->crect.height(); +#endif case PdmDepth: return 32; case PdmNumColors: @@ -4822,19 +5019,35 @@ void QWidgetPrivate::registerDropSite(bool on) #endif } -void QWidgetPrivate::registerTouchWindow() +void QWidgetPrivate::registerTouchWindow(bool enable) { + Q_UNUSED(enable); +#ifdef QT_MAC_USE_COCOA #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_6) return; + Q_Q(QWidget); - if (!q->testAttribute(Qt::WA_WState_Created)) + if (enable == touchEventsEnabled) return; -#ifndef QT_MAC_USE_COCOA - // Needs implementation! -#else - NSView *view = qt_mac_nativeview_for(q); - [view setAcceptsTouchEvents:YES]; + + QT_MANGLE_NAMESPACE(QCocoaView) *view = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(qt_mac_effectiveview_for(q)); + if (!view) + return; + + if (enable) { + ++view->alienTouchCount; + if (view->alienTouchCount == 1) { + touchEventsEnabled = true; + [view setAcceptsTouchEvents:YES]; + } + } else { + --view->alienTouchCount; + if (view->alienTouchCount == 0) { + touchEventsEnabled = false; + [view setAcceptsTouchEvents:NO]; + } + } #endif #endif } @@ -4842,13 +5055,17 @@ void QWidgetPrivate::registerTouchWindow() void QWidgetPrivate::setMask_sys(const QRegion ®ion) { Q_UNUSED(region); -#ifndef QT_MAC_USE_COCOA Q_Q(QWidget); + +#ifndef QT_MAC_USE_COCOA if (q->isWindow()) ReshapeCustomWindow(qt_mac_window_for(q)); else HIViewReshapeStructure(qt_mac_nativeview_for(q)); #else + if (!q->internalWinId()) + return; + if (extra->mask.isEmpty()) { extra->maskBits = QImage(); finishCocoaMaskSetup(); @@ -4856,6 +5073,7 @@ void QWidgetPrivate::setMask_sys(const QRegion ®ion) syncCocoaMask(); } + topLevelAt_cache = 0; #endif } @@ -4933,7 +5151,7 @@ void QWidgetPrivate::finishCocoaMaskSetup() [window setOpaque:(extra->imageMask == 0)]; [window invalidateShadow]; } - qt_mac_set_needs_display(q, QRegion()); + macSetNeedsDisplay(QRegion()); } #endif diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index e30497c..aefffb6 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -102,6 +102,9 @@ class QWSManager; #if defined(Q_WS_MAC) class QCoreGraphicsPaintEnginePrivate; #endif +#if defined(Q_WS_QPA) +class QPlatformWindow; +#endif class QPaintEngine; class QPixmap; class QWidgetBackingStore; @@ -110,6 +113,8 @@ class QWidgetItemV2; class QStyle; +class QUnifiedToolbarSurface; + class Q_AUTOTEST_EXPORT QWidgetBackingStoreTracker { @@ -228,6 +233,10 @@ struct QTLWExtra { uint inExpose : 1; // Prevents drawing recursion uint nativeWindowTransparencyEnabled : 1; // Tracks native window transparency uint forcedToRaster : 1; +#elif defined(Q_WS_QPA) + QPlatformWindow *platformWindow; + QPlatformWindowFormat platformWindowFormat; + quint32 screenIndex; // index in qplatformscreenlist #endif }; @@ -360,7 +369,8 @@ public: DrawInvisible = 0x08, DontSubtractOpaqueChildren = 0x10, DontSetCompositionMode = 0x20, - DontDrawOpaqueChildren = 0x40 + DontDrawOpaqueChildren = 0x40, + DontDrawNativeChildren = 0x80 }; enum CloseMode { @@ -550,6 +560,7 @@ public: bool setMinimumSize_helper(int &minw, int &minh); bool setMaximumSize_helper(int &maxw, int &maxh); + virtual bool hasHeightForWidth() const; void setConstraints_sys(); bool pointInsideRectAndMask(const QPoint &) const; QWidget *childAt_helper(const QPoint &, bool) const; @@ -795,7 +806,6 @@ public: #elif defined(Q_WS_MAC) // <--------------------------------------------------------- MAC // This is new stuff uint needWindowChange : 1; - uint hasAlienChildren : 1; // Each wiget keeps a list of all its child and grandchild OpenGL widgets. // This list is used to update the gl context whenever a parent and a granparent @@ -826,6 +836,7 @@ public: void macUpdateIgnoreMouseEvents(); void macUpdateMetalAttribute(); void macUpdateIsOpaque(); + void macSetNeedsDisplay(QRegion region); void setEnabled_helper_sys(bool enable); bool isRealWindow() const; void adjustWithinMaxAndMinSize(int &w, int &h); @@ -854,7 +865,15 @@ public: bool originalDrawMethod; // Do we need to change the methods? bool changeMethods; -#endif + + // Unified toolbar variables + bool isInUnifiedToolbar; + QUnifiedToolbarSurface *unifiedSurface; + QPoint toolbar_offset; + QWidget *toolbar_ancestor; + bool flushRequested; + bool touchEventsEnabled; +#endif // QT_MAC_USE_COCOA void determineWindowClass(); void transferChildren(); bool qt_mac_dnd_event(uint, DragRef); @@ -866,7 +885,7 @@ public: static OSStatus qt_window_event(EventHandlerCallRef er, EventRef event, void *); static OSStatus qt_widget_event(EventHandlerCallRef er, EventRef event, void *); static bool qt_widget_rgn(QWidget *, short, RgnHandle, bool); - void registerTouchWindow(); + void registerTouchWindow(bool enable = true); #elif defined(Q_WS_QWS) // <--------------------------------------------------------- QWS void setMaxWindowState_helper(); void setFullScreenSize_helper(); @@ -881,6 +900,12 @@ public: void updateCursor() const; #endif QScreen* getScreen() const; +#elif defined(Q_WS_QPA) // <--------------------------------------------------------- QPA + void setMaxWindowState_helper(); + void setFullScreenSize_helper(); +#ifndef QT_NO_CURSOR + void updateCursor() const; +#endif #elif defined(Q_OS_SYMBIAN) // <--------------------------------------------------------- SYMBIAN static QWidget *mouseGrabber; static QWidget *keyboardGrabber; diff --git a/src/gui/kernel/qwidget_qpa.cpp b/src/gui/kernel/qwidget_qpa.cpp new file mode 100644 index 0000000..224f574 --- /dev/null +++ b/src/gui/kernel/qwidget_qpa.cpp @@ -0,0 +1,906 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtGui/qwidget.h" +#include "QtGui/qevent.h" +#include "QtGui/qapplication.h" +#include "QtGui/private/qbackingstore_p.h" +#include "QtGui/private/qwidget_p.h" +#include "QtGui/private/qgraphicssystem_p.h" +#include "QtGui/private/qapplication_p.h" +#include "QtGui/qdesktopwidget.h" +#include "QtGui/qplatformwindow_qpa.h" +#include "QtGui/qplatformglcontext_qpa.h" + +#include <QtGui/QPlatformCursor> + +QT_BEGIN_NAMESPACE + +void q_createNativeChildrenAndSetParent(QPlatformWindow *parentWindow, const QWidget *parentWidget) +{ + QObjectList children = parentWidget->children(); + for (int i = 0; i < children.size(); i++) { + if (children.at(i)->isWidgetType()) { + const QWidget *childWidget = qobject_cast<const QWidget *>(children.at(i)); + if (childWidget) { // should not be necessary + if (childWidget->testAttribute(Qt::WA_NativeWindow)) { + if (!childWidget->platformWindow()) + childWidget->winId(); + } + if (childWidget->platformWindow()) { + childWidget->platformWindow()->setParent(parentWindow); + } else { + q_createNativeChildrenAndSetParent(parentWindow,childWidget); + } + } + } + } + +} + +void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow) +{ + Q_Q(QWidget); + + Q_UNUSED(window); + Q_UNUSED(initializeWindow); + Q_UNUSED(destroyOldWindow); + + Qt::WindowFlags flags = data.window_flags; + + if ((!q->testAttribute(Qt::WA_NativeWindow) && !q->isWindow()) || q->windowType() == Qt::Desktop ) + return; // we only care about real toplevels + + QWindowSurface *surface = q->windowSurface(); + QPlatformWindow *platformWindow = q->platformWindow(); + + if (!platformWindow) { + platformWindow = QApplicationPrivate::platformIntegration()->createPlatformWindow(q); + } + Q_ASSERT(platformWindow); + + if (!surface ) { + if (platformWindow && q->platformWindowFormat().hasWindowSurface()) { + surface = QApplicationPrivate::platformIntegration()->createWindowSurface(q,platformWindow->winId()); + } else { + q->setAttribute(Qt::WA_PaintOnScreen,true); + } + } + + data.window_flags = q->platformWindow()->setWindowFlags(data.window_flags); + + setWinId(q->platformWindow()->winId()); + + //first check children. and create them if necessary + q_createNativeChildrenAndSetParent(q->platformWindow(),q); + + //if we we have a parent, then set correct parent; + if (!q->isWindow()) { + if (QWidget *nativeParent = q->nativeParentWidget()) { + if (nativeParent->platformWindow()) { + platformWindow->setParent(nativeParent->platformWindow()); + } + } + } + + QApplicationPrivate::platformIntegration()->moveToScreen(q, topData()->screenIndex); +// qDebug() << "create_sys" << q << q->internalWinId(); +} + +void QWidget::destroy(bool destroyWindow, bool destroySubWindows) +{ + Q_D(QWidget); + + d->aboutToDestroy(); + if (!isWindow() && parentWidget()) + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); + d->deactivateWidgetCleanup(); + + if ((windowType() == Qt::Popup)) + qApp->d_func()->closePopup(this); + + //### we don't have proper focus event handling yet + if (this == QApplicationPrivate::active_window) + QApplication::setActiveWindow(0); + + setAttribute(Qt::WA_WState_Created, false); + + if (windowType() != Qt::Desktop) { + if (destroySubWindows) { + QObjectList childList(children()); + for (int i = 0; i < childList.size(); i++) { + QWidget *widget = qobject_cast<QWidget *>(childList.at(i)); + if (widget && widget->testAttribute(Qt::WA_NativeWindow)) { + if (widget->platformWindow()) { + widget->destroy(); + } + } + } + } + if (destroyWindow) { + d->deleteTLSysExtra(); + } else { + if (parentWidget() && parentWidget()->testAttribute(Qt::WA_WState_Created)) { + d->hide_sys(); + } + } + + d->setWinId(0); + } +} + +void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) +{ + Q_Q(QWidget); + + Qt::WindowFlags oldFlags = data.window_flags; + + int targetScreen = -1; + // Handle a request to move the widget to a particular screen + if (newparent && newparent->windowType() == Qt::Desktop) { + // make sure the widget is created on the same screen as the + // programmer specified desktop widget + + // get the desktop's screen number + targetScreen = newparent->window()->d_func()->topData()->screenIndex; + newparent = 0; + } + + if (parent != newparent) { + QObjectPrivate::setParent_helper(newparent); //### why does this have to be done in the _sys function??? + if (q->platformWindow() && newparent) { + QWidget * parentWithWindow = newparent->platformWindow()? newparent : newparent->nativeParentWidget(); + if (parentWithWindow && parentWithWindow->platformWindow()) { + q->platformWindow()->setParent(parentWithWindow->platformWindow()); + } + } + + } + + if (!newparent) { + f |= Qt::Window; + if (targetScreen == -1) { + if (parent) + targetScreen = q->parentWidget()->window()->d_func()->topData()->screenIndex; + } + } + + bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide); + + // Reparenting toplevel to child + if (!(f&Qt::Window) && (oldFlags&Qt::Window) && !q->testAttribute(Qt::WA_NativeWindow)) { + //qDebug() << "setParent_sys() change from toplevel"; + q->destroy(); + } + + data.window_flags = f; + q->setAttribute(Qt::WA_WState_Created, false); + q->setAttribute(Qt::WA_WState_Visible, false); + q->setAttribute(Qt::WA_WState_Hidden, false); + + if (f & Qt::Window) { + //qDebug() << "setParent_sys" << q << newparent << hex << f; + if (QPlatformWindow *window = q->platformWindow()) + data.window_flags = window->setWindowFlags(data.window_flags); + } + + if (q->isWindow() || (!newparent || newparent->isVisible()) || explicitlyHidden) + q->setAttribute(Qt::WA_WState_Hidden); + q->setAttribute(Qt::WA_WState_ExplicitShowHide, explicitlyHidden); + + // move the window to the selected screen + if (!newparent && targetScreen != -1) { + if (maybeTopData()) + maybeTopData()->screenIndex = targetScreen; + // only if it is already created + if (q->testAttribute(Qt::WA_WState_Created)) { + QPlatformIntegration *platform = QApplicationPrivate::platformIntegration(); + platform->moveToScreen(q, targetScreen); + } + } +} + +QPoint QWidget::mapToGlobal(const QPoint &pos) const +{ + int x=pos.x(), y=pos.y(); + const QWidget* w = this; + while (w) { + x += w->data->crect.x(); + y += w->data->crect.y(); + w = w->isWindow() ? 0 : w->parentWidget(); + } + return QPoint(x, y); +} + +QPoint QWidget::mapFromGlobal(const QPoint &pos) const +{ + int x=pos.x(), y=pos.y(); + const QWidget* w = this; + while (w) { + x -= w->data->crect.x(); + y -= w->data->crect.y(); + w = w->isWindow() ? 0 : w->parentWidget(); + } + return QPoint(x, y); +} + +void QWidgetPrivate::updateSystemBackground() {} + +#ifndef QT_NO_CURSOR +void QWidgetPrivate::setCursor_sys(const QCursor &cursor) +{ + Q_UNUSED(cursor); + Q_Q(QWidget); + if (q->isVisible()) + qt_qpa_set_cursor(q, false); +} + +void QWidgetPrivate::unsetCursor_sys() +{ + Q_Q(QWidget); + if (q->isVisible()) + qt_qpa_set_cursor(q, false); +} + +void QWidgetPrivate::updateCursor() const +{ + // XXX +} + +#endif //QT_NO_CURSOR + +void QWidgetPrivate::setWindowTitle_sys(const QString &caption) +{ + Q_Q(QWidget); + if (!q->isWindow()) + return; + + if (QPlatformWindow *window = q->platformWindow()) + window->setWindowTitle(caption); + +} + +void QWidgetPrivate::setWindowIcon_sys(bool /*forceReset*/) +{ +} + +void QWidgetPrivate::setWindowIconText_sys(const QString &iconText) +{ + Q_UNUSED(iconText); +} + +QWidget *qt_pressGrab = 0; +QWidget *qt_mouseGrb = 0; +static QWidget *keyboardGrb = 0; + +void QWidget::grabMouse() +{ + if (qt_mouseGrb) + qt_mouseGrb->releaseMouse(); + + // XXX + //qwsDisplay()->grabMouse(this,true); + + qt_mouseGrb = this; + qt_pressGrab = 0; +} + +#ifndef QT_NO_CURSOR +void QWidget::grabMouse(const QCursor &cursor) +{ + Q_UNUSED(cursor); + + if (qt_mouseGrb) + qt_mouseGrb->releaseMouse(); + + // XXX + //qwsDisplay()->grabMouse(this,true); + //qwsDisplay()->selectCursor(this, cursor.handle()); + qt_mouseGrb = this; + qt_pressGrab = 0; +} +#endif + +void QWidget::releaseMouse() +{ + if (qt_mouseGrb == this) { + // XXX + //qwsDisplay()->grabMouse(this,false); + qt_mouseGrb = 0; + } +} + +void QWidget::grabKeyboard() +{ + if (keyboardGrb) + keyboardGrb->releaseKeyboard(); + // XXX + //qwsDisplay()->grabKeyboard(this, true); + keyboardGrb = this; +} + +void QWidget::releaseKeyboard() +{ + if (keyboardGrb == this) { + // XXX + //qwsDisplay()->grabKeyboard(this, false); + keyboardGrb = 0; + } +} + +QWidget *QWidget::mouseGrabber() +{ + if (qt_mouseGrb) + return qt_mouseGrb; + return qt_pressGrab; +} + +QWidget *QWidget::keyboardGrabber() +{ + return keyboardGrb; +} + +void QWidget::activateWindow() +{ + if (platformWindow()) + platformWindow()->requestActivateWindow(); +} + +void QWidgetPrivate::show_sys() +{ + Q_Q(QWidget); + q->setAttribute(Qt::WA_Mapped); + if (q->testAttribute(Qt::WA_DontShowOnScreen)) { + invalidateBuffer(q->rect()); + return; + } + + QApplication::postEvent(q, new QUpdateLaterEvent(q->rect())); + + QPlatformWindow *window = q->platformWindow(); + if (window) { + QRect geomRect = q->geometry(); + if (!q->isWindow()) { + QPoint topLeftOfWindow = q->mapTo(q->nativeParentWidget(),QPoint()); + geomRect.moveTopLeft(topLeftOfWindow); + } + const QRect windowRect = window->geometry(); + if (windowRect != geomRect) { + window->setGeometry(geomRect); + } + if (QWindowSurface *surface = q->windowSurface()) { + if (windowRect.size() != geomRect.size()) { + surface->resize(geomRect.size()); + } + } + if (window) + window->setVisible(true); + } +} + + +void QWidgetPrivate::hide_sys() +{ + Q_Q(QWidget); + q->setAttribute(Qt::WA_Mapped, false); + deactivateWidgetCleanup(); + if (!q->isWindow()) { + QWidget *p = q->parentWidget(); + if (p &&p->isVisible()) { + invalidateBuffer(q->rect()); + } + return; + } + if (QPlatformWindow *window = q->platformWindow()) { + window->setVisible(false); + } + + //### we don't yet have proper focus event handling + if (q == QApplicationPrivate::active_window) + QApplication::setActiveWindow(0); + +} + +void QWidgetPrivate::setMaxWindowState_helper() +{ + setFullScreenSize_helper(); //### decoration size +} + +void QWidgetPrivate::setFullScreenSize_helper() +{ + Q_Q(QWidget); + + const uint old_state = data.in_set_window_state; + data.in_set_window_state = 1; + + const QRect screen = qApp->desktop()->screenGeometry(qApp->desktop()->screenNumber(q)); + q->move(screen.topLeft()); + q->resize(screen.size()); + + data.in_set_window_state = old_state; +} + +static Qt::WindowStates effectiveState(Qt::WindowStates state) + { + if (state & Qt::WindowMinimized) + return Qt::WindowMinimized; + else if (state & Qt::WindowFullScreen) + return Qt::WindowFullScreen; + else if (state & Qt::WindowMaximized) + return Qt::WindowMaximized; + return Qt::WindowNoState; + } + +void QWidget::setWindowState(Qt::WindowStates newstate) +{ + Q_D(QWidget); + Qt::WindowStates oldstate = windowState(); + if (oldstate == newstate) + return; + if (isWindow() && !testAttribute(Qt::WA_WState_Created)) + create(); + + data->window_state = newstate; + data->in_set_window_state = 1; + bool needShow = false; + Qt::WindowStates newEffectiveState = effectiveState(newstate); + Qt::WindowStates oldEffectiveState = effectiveState(oldstate); + if (isWindow() && newEffectiveState != oldEffectiveState) { + d->createTLExtra(); + if (oldEffectiveState == Qt::WindowNoState) { //normal + d->topData()->normalGeometry = geometry(); + } else if (oldEffectiveState == Qt::WindowFullScreen) { + setParent(0, d->topData()->savedFlags); + needShow = true; + } else if (oldEffectiveState == Qt::WindowMinimized) { + needShow = true; + } + + if (newEffectiveState == Qt::WindowMinimized) { + //### not ideal... + hide(); + needShow = false; + } else if (newEffectiveState == Qt::WindowFullScreen) { + d->topData()->savedFlags = windowFlags(); + setParent(0, Qt::FramelessWindowHint | (windowFlags() & Qt::WindowStaysOnTopHint)); + d->setFullScreenSize_helper(); + raise(); + needShow = true; + } else if (newEffectiveState == Qt::WindowMaximized) { + createWinId(); + d->setMaxWindowState_helper(); + } else { //normal + QRect r = d->topData()->normalGeometry; + if (r.width() >= 0) { + d->topData()->normalGeometry = QRect(0,0,-1,-1); + setGeometry(r); + } + } + } + data->in_set_window_state = 0; + + if (needShow) + show(); + + if (newstate & Qt::WindowActive) + activateWindow(); + + QWindowStateChangeEvent e(oldstate); + QApplication::sendEvent(this, &e); +} + +void QWidgetPrivate::setFocus_sys() +{ + +} + +void QWidgetPrivate::raise_sys() +{ + Q_Q(QWidget); + if (q->isWindow()) { + q->platformWindow()->raise(); + } +} + +void QWidgetPrivate::lower_sys() +{ + Q_Q(QWidget); + if (q->isWindow()) { + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + q->platformWindow()->lower(); + } else if (QWidget *p = q->parentWidget()) { + setDirtyOpaqueRegion(); + p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); + } +} + +void QWidgetPrivate::stackUnder_sys(QWidget*) +{ + Q_Q(QWidget); + if (QWidget *p = q->parentWidget()) { + setDirtyOpaqueRegion(); + p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); + } +} + +void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) +{ + Q_Q(QWidget); + if (extra) { // any size restrictions? + w = qMin(w,extra->maxw); + h = qMin(h,extra->maxh); + w = qMax(w,extra->minw); + h = qMax(h,extra->minh); + } + + QPoint oldp = q->geometry().topLeft(); + QSize olds = q->size(); + QRect r(x, y, w, h); + + bool isResize = olds != r.size(); + isMove = oldp != r.topLeft(); //### why do we have isMove as a parameter? + + + // We only care about stuff that changes the geometry, or may + // cause the window manager to change its state + if (r.size() == olds && oldp == r.topLeft()) + return; + + if (!data.in_set_window_state) { + q->data->window_state &= ~Qt::WindowMaximized; + q->data->window_state &= ~Qt::WindowFullScreen; + if (q->isWindow()) + topData()->normalGeometry = QRect(0, 0, -1, -1); + } + + QPoint oldPos = q->pos(); + data.crect = r; + + if (q->isVisible()) { + if (q->platformWindow()) { + if (q->isWindow()) { + q->platformWindow()->setGeometry(q->geometry()); + } else { + QPoint posInNativeParent = q->mapTo(q->nativeParentWidget(),QPoint()); + q->platformWindow()->setGeometry(QRect(posInNativeParent,r.size())); + } + const QWidgetBackingStore *bs = maybeBackingStore(); + if (bs->windowSurface) { + if (isResize) + bs->windowSurface->resize(r.size()); + } + } else { + if (isMove && !isResize) + moveRect(QRect(oldPos, olds), x - oldPos.x(), y - oldPos.y()); + else + invalidateBuffer_resizeHelper(oldPos, olds); + } + + if (isMove) { + QMoveEvent e(q->pos(), oldPos); + QApplication::sendEvent(q, &e); + } + if (isResize) { + QResizeEvent e(r.size(), olds); + QApplication::sendEvent(q, &e); + if (q->platformWindow()) + q->update(); + } + } else { // not visible + if (isMove && q->pos() != oldPos) + q->setAttribute(Qt::WA_PendingMoveEvent, true); + if (isResize) + q->setAttribute(Qt::WA_PendingResizeEvent, true); + } + +} + +void QWidgetPrivate::setConstraints_sys() +{ +} + +void QWidgetPrivate::scroll_sys(int dx, int dy) +{ + Q_Q(QWidget); + scrollChildren(dx, dy); + scrollRect(q->rect(), dx, dy); +} + +void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) +{ + scrollRect(r, dx, dy); +} + +int QWidget::metric(PaintDeviceMetric m) const +{ + Q_D(const QWidget); + + QPlatformScreen *screen = QPlatformScreen::platformScreenForWidget(this); + if (!screen) { + if (m == PdmDpiX || m == PdmDpiY) + return 72; + return QPaintDevice::metric(m); + } + int val; + if (m == PdmWidth) { + val = data->crect.width(); + } else if (m == PdmWidthMM) { + val = data->crect.width() * screen->physicalSize().width() / screen->geometry().width(); + } else if (m == PdmHeight) { + val = data->crect.height(); + } else if (m == PdmHeightMM) { + val = data->crect.height() * screen->physicalSize().height() / screen->geometry().height(); + } else if (m == PdmDepth) { + return screen->depth(); + } else if (m == PdmDpiX || m == PdmPhysicalDpiX) { + if (d->extra && d->extra->customDpiX) + return d->extra->customDpiX; + else if (d->parent) + return static_cast<QWidget *>(d->parent)->metric(m); + return qRound(screen->geometry().width() / double(screen->physicalSize().width() / 25.4)); + } else if (m == PdmDpiY || m == PdmPhysicalDpiY) { + if (d->extra && d->extra->customDpiY) + return d->extra->customDpiY; + else if (d->parent) + return static_cast<QWidget *>(d->parent)->metric(m); + return qRound(screen->geometry().height() / double(screen->physicalSize().height() / 25.4)); + } else { + val = QPaintDevice::metric(m);// XXX + } + return val; +} + +/*! + \preliminary + \since 4.8 + + Sets the window to be the platform \a window specified. + + The widget takes ownership of the \a window. Any platform window + previously set on the widget will be destroyed. +*/ +void QWidget::setPlatformWindow(QPlatformWindow *window) +{ + Q_D(QWidget); + + QTLWExtra *topData = d->topData(); + if (topData->platformWindow == window) + return; + + delete topData->platformWindow; + topData->platformWindow = window; +} + +/*! + \preliminary + \since 4.8 + + Returns the QPlatformWindow this widget will be drawn into. +*/ +QPlatformWindow *QWidget::platformWindow() const +{ + Q_D(const QWidget); + QTLWExtra *extra = d->maybeTopData(); + if (extra && extra->platformWindow) + return extra->platformWindow; + + return 0; +} + +/*! + \since 4.8 + + Sets the platform window format for the widget to the \a format specified. +*/ +void QWidget::setPlatformWindowFormat(const QPlatformWindowFormat &format) +{ + if (isWindow() || testAttribute(Qt::WA_NativeWindow)) { + Q_D(QWidget); + QTLWExtra *topData = d->topData(); + topData->platformWindowFormat = format; + if (testAttribute(Qt::WA_WState_Created)) { + bool wasVisible = testAttribute(Qt::WA_WState_Visible); + destroy(); + d->create_sys(0,true,true); + if (wasVisible) + topData->platformWindow->setVisible(true); + } + } +} + +/*! + \since 4.8 + + Returns the platform window format for the widget. +*/ +QPlatformWindowFormat QWidget::platformWindowFormat() const +{ + Q_D(const QWidget); + + QPlatformWindowFormat format; + + QTLWExtra *extra = d->maybeTopData(); + if (extra){ + format = extra->platformWindowFormat; + } else { + format = QPlatformWindowFormat::defaultFormat(); + } + + if (testAttribute(Qt::WA_TranslucentBackground)) + format.setAlpha(true); + + return format; +} + +void QWidgetPrivate::createSysExtra() +{ +} + +void QWidgetPrivate::deleteSysExtra() +{ + +} + +void QWidgetPrivate::createTLSysExtra() +{ +} + +void QWidgetPrivate::deleteTLSysExtra() +{ + if (extra && extra->topextra) { + //the toplevel might have a context with a "qglcontext associated with it. We need to + //delete the qglcontext before we delete the qplatformglcontext. + //One unfortunate thing about this is that we potentially create a glContext just to + //delete it straight afterwards. + if (extra->topextra->platformWindow) { + if (QPlatformGLContext *context = extra->topextra->platformWindow->glContext()) { + context->deleteQGLContext(); + } + } + setWinId(0); + delete extra->topextra->platformWindow; + extra->topextra->platformWindow = 0; + } +} + +void QWidgetPrivate::registerDropSite(bool on) +{ + Q_UNUSED(on); +} + +void QWidgetPrivate::setMask_sys(const QRegion ®ion) +{ + Q_UNUSED(region); + // XXX +} + +void QWidgetPrivate::updateFrameStrut() +{ + // XXX +} + +void QWidgetPrivate::setWindowOpacity_sys(qreal level) +{ + Q_Q(QWidget); + q->platformWindow()->setOpacity(level); +} + +void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &oldRect) +{ + Q_UNUSED(dontShow); + Q_UNUSED(oldRect); + // XXX +} + +QPaintEngine *QWidget::paintEngine() const +{ + qWarning("QWidget::paintEngine: Should no longer be called"); + return 0; //##### @@@ +} + +QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys() +{ + Q_Q(QWidget); + if (q->platformWindowFormat().hasWindowSurface()) + return QApplicationPrivate::platformIntegration()->createWindowSurface(q,0); + else + return 0; +} + +void QWidgetPrivate::setModal_sys() +{ +} + +#ifndef QT_NO_CURSOR +void qt_qpa_set_cursor(QWidget * w, bool force) +{ + static QCursor arrowCursor(Qt::ArrowCursor); + static QPointer<QWidget> lastUnderMouse = 0; + + QCursor * override = QApplication::overrideCursor(); + + if (override && w != 0) + return; + + QWidget *cursorWidget; + QCursor cursorCursor; + + do { + if (w == 0) { + if (override) { + cursorCursor = *override; + cursorWidget = QApplication::topLevelAt(QCursor::pos()); + break; + } + w = QApplication::widgetAt(QCursor::pos()); + if (w == 0) // clear the override cursor while over empty space + w = QApplication::desktop(); + } else if (force) { + lastUnderMouse = w; + } else if (w->testAttribute(Qt::WA_WState_Created) && lastUnderMouse + && lastUnderMouse->effectiveWinId() == w->effectiveWinId()) { + w = lastUnderMouse; + } + if (w == QApplication::desktop() && !override) { + cursorCursor = arrowCursor; + cursorWidget = w; + break; + } + + QWidget * curWin = QApplication::activeWindow(); + if (!curWin && w && w->internalWinId()) + return; + QWidget* cW = w && !w->internalWinId() ? w : curWin; + + if (!cW || cW->window() != w->window() || + !cW->isVisible() || !cW->underMouse() || override) + return; + + cursorCursor = w->cursor(); + cursorWidget = w; + } while (0); + foreach (QWeakPointer<QPlatformCursor> cursor, QPlatformCursorPrivate::getInstances()) + if (cursor) + cursor.data()->changeCursor(&cursorCursor, cursorWidget); +} +#endif //QT_NO_CURSOR + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qwidget_s60.cpp b/src/gui/kernel/qwidget_s60.cpp index e80eced..2b51aaa 100644 --- a/src/gui/kernel/qwidget_s60.cpp +++ b/src/gui/kernel/qwidget_s60.cpp @@ -393,7 +393,9 @@ void QWidgetPrivate::create_sys(WId window, bool /* initializeWindow */, bool de if (!q->testAttribute(Qt::WA_Moved) && !q->testAttribute(Qt::WA_DontShowOnScreen)) data.crect.moveTopLeft(QPoint(clientRect.iTl.iX, clientRect.iTl.iY)); - QScopedPointer<QSymbianControl> control( q_check_ptr(new QSymbianControl(q)) ); + QScopedPointer<QSymbianControl> control( new QSymbianControl(q) ); + Q_CHECK_PTR(control); + QT_TRAP_THROWING(control->ConstructL(true, desktop)); control->SetMopParent(static_cast<CEikAppUi*>(S60->appUi())); @@ -438,7 +440,9 @@ void QWidgetPrivate::create_sys(WId window, bool /* initializeWindow */, bool de } else if (q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) { // create native child widget - QScopedPointer<QSymbianControl> control( q_check_ptr(new QSymbianControl(q)) ); + QScopedPointer<QSymbianControl> control( new QSymbianControl(q) ); + Q_CHECK_PTR(control); + QT_TRAP_THROWING(control->ConstructL(!parentWidget)); // Symbian windows are always created in an inactive state diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index 55cfed8..60446dd 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -329,18 +329,11 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO if (topLevel) { if ((type == Qt::Window || dialog || tool)) { if (!(flags & Qt::FramelessWindowHint)) { - if (!(flags & Qt::MSWindowsFixedSizeDialogHint)) { + style |= WS_POPUP; + if (!(flags & Qt::MSWindowsFixedSizeDialogHint)) style |= WS_THICKFRAME; - if(!(flags & - ( Qt::WindowSystemMenuHint - | Qt::WindowTitleHint - | Qt::WindowMinMaxButtonsHint - | Qt::WindowCloseButtonHint - | Qt::WindowContextHelpButtonHint))) - style |= WS_POPUP; - } else { - style |= WS_POPUP | WS_DLGFRAME; - } + else + style |= WS_DLGFRAME; } if (flags & Qt::WindowTitleHint) style |= WS_CAPTION; @@ -424,6 +417,14 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO if (!q->testAttribute(Qt::WA_Resized)) { w = sw/2; h = 4*sh/10; + if (extra) { + int dx = rect.right - rect.left; + int dy = rect.bottom - rect.top; + w = qMin(w, extra->maxw + dx); + h = qMin(h, extra->maxh + dy); + w = qMax(w, extra->minw + dx); + h = qMax(h, extra->minh + dy); + } } if (!wasMoved) { x = sw/2 - w/2; @@ -1635,8 +1636,6 @@ void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) } } -extern Q_GUI_EXPORT HDC qt_win_display_dc(); - int QWidget::metric(PaintDeviceMetric m) const { Q_D(const QWidget); @@ -1646,7 +1645,7 @@ int QWidget::metric(PaintDeviceMetric m) const } else if (m == PdmHeight) { val = data->crect.height(); } else { - HDC gdc = qt_win_display_dc(); + HDC gdc = GetDC(0); switch (m) { case PdmDpiX: case PdmPhysicalDpiX: @@ -1697,6 +1696,7 @@ int QWidget::metric(PaintDeviceMetric m) const val = 0; qWarning("QWidget::metric: Invalid metric command"); } + ReleaseDC(0, gdc); } return val; } diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index 65d9837..f99cc2c 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -346,7 +346,7 @@ Q_GUI_EXPORT void qt_x11_enforce_cursor(QWidget * w) qt_x11_enforce_cursor(w, false); } -Q_GUI_EXPORT void qt_x11_wait_for_window_manager(QWidget* w) +void qt_x11_wait_for_window_manager(QWidget *w, bool sendPostedEvents) { if (!w || (!w->isWindow() && !w->internalWinId())) return; @@ -361,7 +361,8 @@ Q_GUI_EXPORT void qt_x11_wait_for_window_manager(QWidget* w) WId winid = w->internalWinId(); // first deliver events that are already in the local queue - QApplication::sendPostedEvents(); + if (sendPostedEvents) + QApplication::sendPostedEvents(); // the normal sequence is: // ... ConfigureNotify ... ReparentNotify ... MapNotify ... Expose @@ -396,6 +397,11 @@ Q_GUI_EXPORT void qt_x11_wait_for_window_manager(QWidget* w) } while(1); } +Q_GUI_EXPORT void qt_x11_wait_for_window_manager(QWidget *w) +{ + qt_x11_wait_for_window_manager(w, true); +} + void qt_change_net_wm_state(const QWidget* w, bool set, Atom one, Atom two = 0) { if (!w->isVisible()) // not managed by the window manager @@ -480,8 +486,6 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO bool topLevel = (flags & Qt::Window); bool popup = (type == Qt::Popup); - bool dialog = (type == Qt::Dialog - || type == Qt::Sheet); bool desktop = (type == Qt::Desktop); bool tool = (type == Qt::Tool || type == Qt::SplashScreen || type == Qt::ToolTip || type == Qt::Drawer); @@ -547,7 +551,7 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO int sh = DisplayHeight(dpy,scr); if (desktop) { // desktop widget - dialog = popup = false; // force these flags off + popup = false; // force these flags off data.crect.setRect(0, 0, sw, sh); } else if (topLevel && !q->testAttribute(Qt::WA_Resized)) { QDesktopWidget *desktopWidget = qApp->desktop(); @@ -907,8 +911,17 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO inputContext->setFocusWidget(q); } - if (destroyw) + if (destroyw) { qt_XDestroyWindow(q, dpy, destroyw); + if (QTLWExtra *topData = maybeTopData()) { +#ifndef QT_NO_XSYNC + if (topData->syncUpdateCounter) + XSyncDestroyCounter(dpy, topData->syncUpdateCounter); +#endif + // we destroyed our old window - reset the top-level state + createTLSysExtra(); + } + } // newly created windows are positioned at the window system's // (0,0) position. If the parent uses wrect mapping to expand the @@ -939,8 +952,13 @@ static void qt_x11_recreateWidget(QWidget *widget) // recreate their GL context, which in turn causes them to choose // their visual again. Now that WA_TranslucentBackground is set, // QGLContext::chooseVisual will select an ARGB visual. - QEvent e(QEvent::ParentChange); - QApplication::sendEvent(widget, &e); + + // QGLWidget expects a ParentAboutToChange to be sent first + QEvent aboutToChangeEvent(QEvent::ParentAboutToChange); + QApplication::sendEvent(widget, &aboutToChangeEvent); + + QEvent parentChangeEvent(QEvent::ParentChange); + QApplication::sendEvent(widget, &parentChangeEvent); } else { // For regular widgets, reparent them with their parent which // also triggers a recreation of the native window @@ -1318,12 +1336,40 @@ QPoint QWidgetPrivate::mapFromGlobal(const QPoint &pos) const QPoint QWidget::mapToGlobal(const QPoint &pos) const { Q_D(const QWidget); + QPoint offset = data->crect.topLeft(); + const QWidget *w = this; + const QWidget *p = w->parentWidget(); + while (!w->isWindow() && p) { + w = p; + p = p->parentWidget(); + offset += w->data->crect.topLeft(); + } + + const QWidgetPrivate *wd = w->d_func(); + QTLWExtra *tlw = wd->topData(); + if (!tlw->embedded) + return pos + offset; + return d->mapToGlobal(pos); } QPoint QWidget::mapFromGlobal(const QPoint &pos) const { Q_D(const QWidget); + QPoint offset = data->crect.topLeft(); + const QWidget *w = this; + const QWidget *p = w->parentWidget(); + while (!w->isWindow() && p) { + w = p; + p = p->parentWidget(); + offset += w->data->crect.topLeft(); + } + + const QWidgetPrivate *wd = w->d_func(); + QTLWExtra *tlw = wd->topData(); + if (!tlw->embedded) + return pos - offset; + return d->mapFromGlobal(pos); } @@ -1337,9 +1383,15 @@ void QWidgetPrivate::updateSystemBackground() if (brush.style() == Qt::NoBrush || q->testAttribute(Qt::WA_NoSystemBackground) || q->testAttribute(Qt::WA_UpdatesDisabled) - || type == Qt::Popup || type == Qt::ToolTip - ) - XSetWindowBackgroundPixmap(X11->display, q->internalWinId(), XNone); + || type == Qt::Popup || type == Qt::ToolTip) { + if (QX11Info::isCompositingManagerRunning() + && q->testAttribute(Qt::WA_TranslucentBackground) + && !(q->parent())) + XSetWindowBackground(X11->display, q->internalWinId(), + QColormap::instance(xinfo.screen()).pixel(Qt::transparent)); + else + XSetWindowBackgroundPixmap(X11->display, q->internalWinId(), XNone); + } else if (brush.style() == Qt::SolidPattern && brush.isOpaque()) XSetWindowBackground(X11->display, q->internalWinId(), QColormap::instance(xinfo.screen()).pixel(brush.color())); @@ -1487,7 +1539,7 @@ void QWidgetPrivate::setWindowIcon_sys(bool forceReset) || !QX11Info::appDefaultColormap(xinfo.screen())) { // unknown DE or non-default visual/colormap, use 1bpp bitmap if (!forceReset || !topData->iconPixmap) - topData->iconPixmap = new QBitmap(qt_toX11Pixmap(icon.pixmap(QSize(64,64)))); + topData->iconPixmap = new QPixmap(qt_toX11Pixmap(QBitmap(icon.pixmap(QSize(64,64))))); pixmap_handle = topData->iconPixmap->handle(); } else { // default depth, use a normal pixmap (even though this diff --git a/src/gui/kernel/qwindowdefs.h b/src/gui/kernel/qwindowdefs.h index d0c1dc1..6b24dde 100644 --- a/src/gui/kernel/qwindowdefs.h +++ b/src/gui/kernel/qwindowdefs.h @@ -131,6 +131,12 @@ QT_END_HEADER #endif // Q_WS_QWS +#if defined(Q_WS_QPA) + +typedef unsigned long WId; + +#endif // Q_WS_QPA + #if defined(Q_OS_SYMBIAN) class CCoeControl; typedef CCoeControl * WId; diff --git a/src/gui/kernel/qwindowsysteminterface_qpa.cpp b/src/gui/kernel/qwindowsysteminterface_qpa.cpp new file mode 100644 index 0000000..0ae18d5 --- /dev/null +++ b/src/gui/kernel/qwindowsysteminterface_qpa.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qwindowsysteminterface_qpa.h" +#include "qwindowsysteminterface_qpa_p.h" +#include "qapplication_p.h" +#include <QAbstractEventDispatcher> + +QT_BEGIN_NAMESPACE + + +QTime QWindowSystemInterfacePrivate::eventTime; + +//------------------------------------------------------------ +// +// Callback functions for plugins: +// + +QList<QWindowSystemInterfacePrivate::WindowSystemEvent *> QWindowSystemInterfacePrivate::windowSystemEventQueue; +QMutex QWindowSystemInterfacePrivate::queueMutex; + +extern QPointer<QWidget> qt_last_mouse_receiver; + + +void QWindowSystemInterface::handleEnterEvent(QWidget *tlw) +{ + if (tlw) { + QWidgetData *data = qt_qwidget_data(tlw); + if (data->in_destructor) + return; + + QWindowSystemInterfacePrivate::EnterEvent *e = new QWindowSystemInterfacePrivate::EnterEvent(tlw); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); + } +} + +void QWindowSystemInterface::handleLeaveEvent(QWidget *tlw) +{ + if (tlw) { + QWidgetData *data = qt_qwidget_data(tlw); + if (data->in_destructor) + return; + } + QWindowSystemInterfacePrivate::LeaveEvent *e = new QWindowSystemInterfacePrivate::LeaveEvent(tlw); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleWindowActivated(QWidget *tlw) +{ + QWindowSystemInterfacePrivate::ActivatedWindowEvent *e = new QWindowSystemInterfacePrivate::ActivatedWindowEvent(tlw); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleGeometryChange(QWidget *tlw, const QRect &newRect) +{ + if (tlw) { + QWidgetData *data = qt_qwidget_data(tlw); + if (data->in_destructor) + return; + } + QWindowSystemInterfacePrivate::GeometryChangeEvent *e = new QWindowSystemInterfacePrivate::GeometryChangeEvent(tlw,newRect); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + + +void QWindowSystemInterface::handleCloseEvent(QWidget *tlw) +{ + if (tlw) { + QWindowSystemInterfacePrivate::CloseEvent *e = + new QWindowSystemInterfacePrivate::CloseEvent(tlw); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); + } +} + +/*! + +\a tlw == 0 means that \a ev is in global coords only + + +*/ +void QWindowSystemInterface::handleMouseEvent(QWidget *w, const QPoint & local, const QPoint & global, Qt::MouseButtons b) { + unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); + handleMouseEvent(w, time, local, global, b); +} + +void QWindowSystemInterface::handleMouseEvent(QWidget *tlw, ulong timestamp, const QPoint & local, const QPoint & global, Qt::MouseButtons b) +{ + if (tlw) { + QWidgetData *data = qt_qwidget_data(tlw); + if (data->in_destructor) + tlw = 0; + } + QWindowSystemInterfacePrivate::MouseEvent * e = + new QWindowSystemInterfacePrivate::MouseEvent(tlw, timestamp, local, global, b); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleKeyEvent(QWidget *w, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count) { + unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); + handleKeyEvent(w, time, t, k, mods, text, autorep, count); +} + +void QWindowSystemInterface::handleKeyEvent(QWidget *tlw, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count) +{ + if (tlw) { + QWidgetData *data = qt_qwidget_data(tlw); + if (data->in_destructor) + tlw = 0; + } + + QWindowSystemInterfacePrivate::KeyEvent * e = + new QWindowSystemInterfacePrivate::KeyEvent(tlw, timestamp, t, k, mods, text, autorep, count); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleExtendedKeyEvent(QWidget *w, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + quint32 nativeModifiers, + const QString& text, bool autorep, + ushort count) +{ + unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); + handleExtendedKeyEvent(w, time, type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, + text, autorep, count); +} + +void QWindowSystemInterface::handleExtendedKeyEvent(QWidget *tlw, ulong timestamp, QEvent::Type type, int key, + Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + quint32 nativeModifiers, + const QString& text, bool autorep, + ushort count) +{ + if (tlw) { + QWidgetData *data = qt_qwidget_data(tlw); + if (data->in_destructor) + tlw = 0; + } + + QWindowSystemInterfacePrivate::KeyEvent * e = + new QWindowSystemInterfacePrivate::KeyEvent(tlw, timestamp, type, key, modifiers, + nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleWheelEvent(QWidget *w, const QPoint & local, const QPoint & global, int d, Qt::Orientation o) { + unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); + handleWheelEvent(w, time, local, global, d, o); +} + +void QWindowSystemInterface::handleWheelEvent(QWidget *tlw, ulong timestamp, const QPoint & local, const QPoint & global, int d, Qt::Orientation o) +{ + if (tlw) { + QWidgetData *data = qt_qwidget_data(tlw); + if (data->in_destructor) + tlw = 0; + } + + QWindowSystemInterfacePrivate::WheelEvent *e = + new QWindowSystemInterfacePrivate::WheelEvent(tlw, timestamp, local, global, d, o); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +int QWindowSystemInterfacePrivate::windowSystemEventsQueued() +{ + queueMutex.lock(); + int ret = windowSystemEventQueue.count(); + queueMutex.unlock(); + return ret; +} + +QWindowSystemInterfacePrivate::WindowSystemEvent * QWindowSystemInterfacePrivate::getWindowSystemEvent() +{ + queueMutex.lock(); + QWindowSystemInterfacePrivate::WindowSystemEvent *ret; + if (windowSystemEventQueue.isEmpty()) + ret = 0; + else + ret = windowSystemEventQueue.takeFirst(); + queueMutex.unlock(); + return ret; +} + +void QWindowSystemInterfacePrivate::queueWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *ev) +{ + queueMutex.lock(); + windowSystemEventQueue.append(ev); + queueMutex.unlock(); + + QAbstractEventDispatcher *dispatcher = QApplicationPrivate::qt_qpa_core_dispatcher(); + if (dispatcher) + dispatcher->wakeUp(); +} + +void QWindowSystemInterface::handleTouchEvent(QWidget *w, QEvent::Type type, QTouchEvent::DeviceType devType, const QList<struct TouchPoint> &points) { + unsigned long time = QWindowSystemInterfacePrivate::eventTime.elapsed(); + handleTouchEvent(w, time, type, devType, points); +} + +void QWindowSystemInterface::handleTouchEvent(QWidget *tlw, ulong timestamp, QEvent::Type type, QTouchEvent::DeviceType devType, const QList<struct TouchPoint> &points) +{ + if (!points.size()) // Touch events must have at least one point + return; + + QList<QTouchEvent::TouchPoint> touchPoints; + Qt::TouchPointStates states; + QTouchEvent::TouchPoint p; + + QList<struct TouchPoint>::const_iterator point = points.constBegin(); + QList<struct TouchPoint>::const_iterator end = points.constEnd(); + while (point != end) { + p.setId(point->id); + p.setPressure(point->pressure); + states |= point->state; + Qt::TouchPointStates state = point->state; + if (point->isPrimary) { + state |= Qt::TouchPointPrimary; + } + p.setState(state); + p.setRect(point->area); + p.setScreenPos(point->area.center()); + p.setNormalizedPos(point->normalPosition); + + touchPoints.append(p); + ++point; + } + + QWindowSystemInterfacePrivate::TouchEvent *e = + new QWindowSystemInterfacePrivate::TouchEvent(tlw, timestamp, type, devType, touchPoints); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleScreenGeometryChange(int screenIndex) +{ + QWindowSystemInterfacePrivate::ScreenGeometryEvent *e = + new QWindowSystemInterfacePrivate::ScreenGeometryEvent(screenIndex); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleScreenAvailableGeometryChange(int screenIndex) +{ + QWindowSystemInterfacePrivate::ScreenAvailableGeometryEvent *e = + new QWindowSystemInterfacePrivate::ScreenAvailableGeometryEvent(screenIndex); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +void QWindowSystemInterface::handleScreenCountChange(int count) +{ + QWindowSystemInterfacePrivate::ScreenCountEvent *e = + new QWindowSystemInterfacePrivate::ScreenCountEvent(count); + QWindowSystemInterfacePrivate::queueWindowSystemEvent(e); +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qwindowsysteminterface_qpa.h b/src/gui/kernel/qwindowsysteminterface_qpa.h new file mode 100644 index 0000000..585d79f --- /dev/null +++ b/src/gui/kernel/qwindowsysteminterface_qpa.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QWINDOWSYSTEMINTERFACE_H +#define QWINDOWSYSTEMINTERFACE_H + +#include <QtCore/QTime> +#include <QtGui/qwindowdefs.h> +#include <QtCore/QEvent> +#include <QtGui/QWidget> +#include <QtCore/QWeakPointer> +#include <QtCore/QMutex> +#include <QtGui/QTouchEvent> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class Q_GUI_EXPORT QWindowSystemInterface +{ +public: + static void handleMouseEvent(QWidget *w, const QPoint & local, const QPoint & global, Qt::MouseButtons b); + static void handleMouseEvent(QWidget *w, ulong timestamp, const QPoint & local, const QPoint & global, Qt::MouseButtons b); + + static void handleKeyEvent(QWidget *w, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); + static void handleKeyEvent(QWidget *w, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); + + static void handleExtendedKeyEvent(QWidget *w, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + quint32 nativeModifiers, + const QString& text = QString(), bool autorep = false, + ushort count = 1); + static void handleExtendedKeyEvent(QWidget *w, ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, + quint32 nativeScanCode, quint32 nativeVirtualKey, + quint32 nativeModifiers, + const QString& text = QString(), bool autorep = false, + ushort count = 1); + + static void handleWheelEvent(QWidget *w, const QPoint & local, const QPoint & global, int d, Qt::Orientation o); + static void handleWheelEvent(QWidget *w, ulong timestamp, const QPoint & local, const QPoint & global, int d, Qt::Orientation o); + + struct TouchPoint { + int id; // for application use + bool isPrimary; // for application use + QPointF normalPosition; // touch device coordinates, (0 to 1, 0 to 1) + QRectF area; // the touched area, centered at position in screen coordinates + qreal pressure; // 0 to 1 + Qt::TouchPointState state; //Qt::TouchPoint{Pressed|Moved|Stationary|Released} + }; + + static void handleTouchEvent(QWidget *w, QEvent::Type type, QTouchEvent::DeviceType devType, const QList<struct TouchPoint> &points); + static void handleTouchEvent(QWidget *w, ulong timestamp, QEvent::Type type, QTouchEvent::DeviceType devType, const QList<struct TouchPoint> &points); + + static void handleGeometryChange(QWidget *w, const QRect &newRect); + static void handleCloseEvent(QWidget *w); + static void handleEnterEvent(QWidget *w); + static void handleLeaveEvent(QWidget *w); + static void handleWindowActivated(QWidget *w); + + // Changes to the screen + static void handleScreenGeometryChange(int screenIndex); + static void handleScreenAvailableGeometryChange(int screenIndex); + static void handleScreenCountChange(int count); +}; + +QT_END_NAMESPACE +QT_END_HEADER +#endif // QWINDOWSYSTEMINTERFACE_H diff --git a/src/gui/kernel/qwindowsysteminterface_qpa_p.h b/src/gui/kernel/qwindowsysteminterface_qpa_p.h new file mode 100644 index 0000000..7be64e6 --- /dev/null +++ b/src/gui/kernel/qwindowsysteminterface_qpa_p.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QWINDOWSYSTEMINTERFACE_QPA_P_H +#define QWINDOWSYSTEMINTERFACE_QPA_P_H + +#include "qwindowsysteminterface_qpa.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QWindowSystemInterfacePrivate { +public: + enum EventType { + Close, + GeometryChange, + Enter, + Leave, + ActivatedWindow, + Mouse, + Wheel, + Key, + Touch, + ScreenGeometry, + ScreenAvailableGeometry, + ScreenCountChange + }; + + class WindowSystemEvent { + public: + WindowSystemEvent(EventType t) + : type(t) { } + EventType type; + }; + + class CloseEvent : public WindowSystemEvent { + public: + CloseEvent(QWidget *tlw) + : WindowSystemEvent(Close), topLevel(tlw) { } + QWeakPointer<QWidget> topLevel; + }; + + class GeometryChangeEvent : public WindowSystemEvent { + public: + GeometryChangeEvent(QWidget *tlw, const QRect &newGeometry) + : WindowSystemEvent(GeometryChange), tlw(tlw), newGeometry(newGeometry) + { } + QWeakPointer<QWidget> tlw; + QRect newGeometry; + }; + + class EnterEvent : public WindowSystemEvent { + public: + EnterEvent(QWidget *enter) + : WindowSystemEvent(Enter), enter(enter) + { } + QWeakPointer<QWidget> enter; + }; + + class LeaveEvent : public WindowSystemEvent { + public: + LeaveEvent(QWidget *leave) + : WindowSystemEvent(Leave), leave(leave) + { } + QWeakPointer<QWidget> leave; + }; + + class ActivatedWindowEvent : public WindowSystemEvent { + public: + ActivatedWindowEvent(QWidget *activatedWindow) + : WindowSystemEvent(ActivatedWindow), activated(activatedWindow) + { } + QWeakPointer<QWidget> activated; + }; + + class UserEvent : public WindowSystemEvent { + public: + UserEvent(QWidget * w, ulong time, EventType t) + : WindowSystemEvent(t), widget(w), timestamp(time) { } + QWeakPointer<QWidget> widget; + unsigned long timestamp; + }; + + class MouseEvent : public UserEvent { + public: + MouseEvent(QWidget * w, ulong time, const QPoint & local, const QPoint & global, Qt::MouseButtons b) + : UserEvent(w, time, Mouse), localPos(local), globalPos(global), buttons(b) { } + QPoint localPos; + QPoint globalPos; + Qt::MouseButtons buttons; + }; + + class WheelEvent : public UserEvent { + public: + WheelEvent(QWidget *w, ulong time, const QPoint & local, const QPoint & global, int d, Qt::Orientation o) + : UserEvent(w, time, Wheel), delta(d), localPos(local), globalPos(global), orient(o) { } + int delta; + QPoint localPos; + QPoint globalPos; + Qt::Orientation orient; + }; + + class KeyEvent : public UserEvent { + public: + KeyEvent(QWidget *w, ulong time, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1) + :UserEvent(w, time, Key), key(k), unicode(text), repeat(autorep), + repeatCount(count), modifiers(mods), keyType(t), + nativeScanCode(0), nativeVirtualKey(0), nativeModifiers(0) { } + KeyEvent(QWidget *w, ulong time, QEvent::Type t, int k, Qt::KeyboardModifiers mods, + quint32 nativeSC, quint32 nativeVK, quint32 nativeMods, + const QString & text = QString(), bool autorep = false, ushort count = 1) + :UserEvent(w, time, Key), key(k), unicode(text), repeat(autorep), + repeatCount(count), modifiers(mods), keyType(t), + nativeScanCode(nativeSC), nativeVirtualKey(nativeVK), nativeModifiers(nativeMods) { } + int key; + QString unicode; + bool repeat; + ushort repeatCount; + Qt::KeyboardModifiers modifiers; + QEvent::Type keyType; + quint32 nativeScanCode; + quint32 nativeVirtualKey; + quint32 nativeModifiers; + }; + + class TouchEvent : public UserEvent { + public: + TouchEvent(QWidget *w, ulong time, QEvent::Type t, QTouchEvent::DeviceType d, const QList<QTouchEvent::TouchPoint> &p) + :UserEvent(w, time, Touch), devType(d), points(p), touchType(t) { } + QTouchEvent::DeviceType devType; + QList<QTouchEvent::TouchPoint> points; + QEvent::Type touchType; + + }; + + class ScreenCountEvent : public WindowSystemEvent { + public: + ScreenCountEvent (int count) + : WindowSystemEvent(ScreenCountChange) , count(count) { } + int count; + }; + + class ScreenGeometryEvent : public WindowSystemEvent { + public: + ScreenGeometryEvent(int index) + : WindowSystemEvent(ScreenGeometry), index(index) { } + int index; + }; + + class ScreenAvailableGeometryEvent : public WindowSystemEvent { + public: + ScreenAvailableGeometryEvent(int index) + : WindowSystemEvent(ScreenAvailableGeometry), index(index) { } + int index; + }; + + static QList<WindowSystemEvent *> windowSystemEventQueue; + static QMutex queueMutex; + + static int windowSystemEventsQueued(); + static WindowSystemEvent * getWindowSystemEvent(); + static void queueWindowSystemEvent(WindowSystemEvent *ev); + + static QTime eventTime; +}; + +QT_END_HEADER +QT_END_NAMESPACE + +#endif // QWINDOWSYSTEMINTERFACE_QPA_P_H diff --git a/src/gui/kernel/qx11embed_x11.cpp b/src/gui/kernel/qx11embed_x11.cpp index 32116a5..1fa658d 100644 --- a/src/gui/kernel/qx11embed_x11.cpp +++ b/src/gui/kernel/qx11embed_x11.cpp @@ -513,7 +513,7 @@ QX11EmbedWidget::~QX11EmbedWidget() << "from container with winId" << d->container; #endif XUnmapWindow(x11Info().display(), internalWinId()); - XReparentWindow(x11Info().display(), internalWinId(), x11Info().appRootWindow(), 0, 0); + XReparentWindow(x11Info().display(), internalWinId(), x11Info().appRootWindow(x11Info().screen()), 0, 0); } #ifdef QX11EMBED_DEBUG @@ -792,13 +792,13 @@ bool QX11EmbedWidget::x11Event(XEvent *event) qDebug() << "QX11EmbedWidget::x11Event: client" << (void *)this << "with winId" << winId() << "received a ReparentNotify to" - << ((event->xreparent.parent == x11Info().appRootWindow()) + << ((event->xreparent.parent == x11Info().appRootWindow(x11Info().screen())) ? QString::fromLatin1("root") : QString::number(event->xreparent.parent)); #endif // If the container shuts down, we will be reparented to the // root window. We must also consider the case that we may be // reparented from one container to another. - if (event->xreparent.parent == x11Info().appRootWindow()) { + if (event->xreparent.parent == x11Info().appRootWindow(x11Info().screen())) { if (((QHackWidget *)this)->topData()->embedded) { d->container = 0; emit containerClosed(); @@ -1118,7 +1118,7 @@ QX11EmbedContainer::~QX11EmbedContainer() Q_D(QX11EmbedContainer); if (d->client) { XUnmapWindow(x11Info().display(), d->client); - XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(), 0, 0); + XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(x11Info().screen()), 0, 0); } if (d->xgrab) @@ -1382,7 +1382,7 @@ bool QX11EmbedContainer::eventFilter(QObject *o, QEvent *event) // Wait until the messages have been processed. Then ask // the window manager to delete the window. XUnmapWindow(x11Info().display(), d->client); - XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(), 0, 0); + XReparentWindow(x11Info().display(), d->client, x11Info().appRootWindow(x11Info().screen()), 0, 0); XSync(x11Info().display(), false); XEvent ev; @@ -1630,7 +1630,7 @@ void QX11EmbedContainerPrivate::rejectClient(WId window) Q_Q(QX11EmbedContainer); q->setEnabled(false); XRemoveFromSaveSet(q->x11Info().display(), client); - XReparentWindow(q->x11Info().display(), window, q->x11Info().appRootWindow(), 0, 0); + XReparentWindow(q->x11Info().display(), window, q->x11Info().appRootWindow(q->x11Info().screen()), 0, 0); } /*! \internal diff --git a/src/gui/math3d/qgenericmatrix.h b/src/gui/math3d/qgenericmatrix.h index c09bf4e..a90d32a 100644 --- a/src/gui/math3d/qgenericmatrix.h +++ b/src/gui/math3d/qgenericmatrix.h @@ -79,9 +79,9 @@ public: void copyDataTo(T *values) const; - T *data() { return m[0]; } - const T *data() const { return m[0]; } - const T *constData() const { return m[0]; } + T *data() { return *m; } + const T *data() const { return *m; } + const T *constData() const { return *m; } #if !defined(Q_NO_TEMPLATE_FRIENDS) template<int NN, int MM, typename TT> diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp index 7950494..837bd51 100644 --- a/src/gui/math3d/qmatrix4x4.cpp +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -102,8 +102,6 @@ QMatrix4x4::QMatrix4x4(const qreal *values) \sa optimize() */ -#if !defined(QT_NO_MEMBER_TEMPLATES) || defined(Q_QDOC) - /*! \fn QMatrix4x4::QMatrix4x4(const QGenericMatrix<N, M, qreal>& matrix) @@ -112,7 +110,7 @@ QMatrix4x4::QMatrix4x4(const qreal *values) the remaining elements are filled with elements from the identity matrix. - \sa toGenericMatrix(), qGenericMatrixToMatrix4x4() + \sa toGenericMatrix() */ /*! @@ -122,34 +120,32 @@ QMatrix4x4::QMatrix4x4(const qreal *values) top-most M rows of this 4x4 matrix. If N or M is greater than 4, then the remaining elements are filled with elements from the identity matrix. - - \sa qGenericMatrixFromMatrix4x4() */ -#endif - /*! \fn QMatrix4x4 qGenericMatrixToMatrix4x4(const QGenericMatrix<N, M, qreal>& matrix) \relates QMatrix4x4 + \obsolete Returns a 4x4 matrix constructed from the left-most 4 columns and top-most 4 rows of \a matrix. If \a matrix has less than 4 columns or rows, the remaining elements are filled with elements from the identity matrix. - \sa qGenericMatrixFromMatrix4x4() + \sa QMatrix4x4(const QGenericMatrix &) */ /*! \fn QGenericMatrix<N, M, qreal> qGenericMatrixFromMatrix4x4(const QMatrix4x4& matrix) \relates QMatrix4x4 + \obsolete Returns a NxM generic matrix constructed from the left-most N columns and top-most M rows of \a matrix. If N or M is greater than 4, then the remaining elements are filled with elements from the identity matrix. - \sa qGenericMatrixToMatrix4x4(), QMatrix4x4::toGenericMatrix() + \sa QMatrix4x4::toGenericMatrix() */ /*! diff --git a/src/gui/math3d/qmatrix4x4.h b/src/gui/math3d/qmatrix4x4.h index e01081f..bfbe4b9 100644 --- a/src/gui/math3d/qmatrix4x4.h +++ b/src/gui/math3d/qmatrix4x4.h @@ -69,10 +69,10 @@ public: qreal m21, qreal m22, qreal m23, qreal m24, qreal m31, qreal m32, qreal m33, qreal m34, qreal m41, qreal m42, qreal m43, qreal m44); -#if !defined(QT_NO_MEMBER_TEMPLATES) || defined(Q_QDOC) + template <int N, int M> explicit QMatrix4x4(const QGenericMatrix<N, M, qreal>& matrix); -#endif + QMatrix4x4(const qreal *values, int cols, int rows); QMatrix4x4(const QTransform& transform); QMatrix4x4(const QMatrix& matrix); @@ -169,14 +169,12 @@ public: QRect mapRect(const QRect& rect) const; QRectF mapRect(const QRectF& rect) const; -#if !defined(QT_NO_MEMBER_TEMPLATES) || defined(Q_QDOC) template <int N, int M> QGenericMatrix<N, M, qreal> toGenericMatrix() const; -#endif inline qreal *data(); - inline const qreal *data() const { return m[0]; } - inline const qreal *constData() const { return m[0]; } + inline const qreal *data() const { return *m; } + inline const qreal *constData() const { return *m; } void optimize(); @@ -223,8 +221,6 @@ inline QMatrix4x4::QMatrix4x4 flagBits = General; } -#if !defined(QT_NO_MEMBER_TEMPLATES) - template <int N, int M> Q_INLINE_TEMPLATE QMatrix4x4::QMatrix4x4 (const QGenericMatrix<N, M, qreal>& matrix) @@ -261,8 +257,6 @@ QGenericMatrix<N, M, qreal> QMatrix4x4::toGenericMatrix() const return result; } -#endif - inline const qreal& QMatrix4x4::operator()(int aRow, int aColumn) const { Q_ASSERT(aRow >= 0 && aRow < 4 && aColumn >= 0 && aColumn < 4); @@ -980,7 +974,7 @@ inline qreal *QMatrix4x4::data() // We have to assume that the caller will modify the matrix elements, // so we flip it over to "General" mode. flagBits = General; - return m[0]; + return *m; } #ifndef QT_NO_DEBUG_STREAM @@ -992,14 +986,15 @@ Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QMatrix4x4 &); Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QMatrix4x4 &); #endif +#ifdef QT_DEPRECATED template <int N, int M> -QMatrix4x4 qGenericMatrixToMatrix4x4(const QGenericMatrix<N, M, qreal>& matrix) +QT_DEPRECATED QMatrix4x4 qGenericMatrixToMatrix4x4(const QGenericMatrix<N, M, qreal>& matrix) { return QMatrix4x4(matrix.constData(), N, M); } template <int N, int M> -QGenericMatrix<N, M, qreal> qGenericMatrixFromMatrix4x4(const QMatrix4x4& matrix) +QT_DEPRECATED QGenericMatrix<N, M, qreal> qGenericMatrixFromMatrix4x4(const QMatrix4x4& matrix) { QGenericMatrix<N, M, qreal> result; const qreal *m = matrix.constData(); @@ -1016,6 +1011,7 @@ QGenericMatrix<N, M, qreal> qGenericMatrixFromMatrix4x4(const QMatrix4x4& matrix } return result; } +#endif #endif diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index 8ee65b5..27b1bf2 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -6,6 +6,7 @@ HEADERS += \ painting/qcolor.h \ painting/qcolor_p.h \ painting/qcolormap.h \ + painting/qcosmeticstroker_p.h \ painting/qdrawutil.h \ painting/qemulationpaintengine_p.h \ painting/qgraphicssystem_p.h \ @@ -15,7 +16,7 @@ HEADERS += \ painting/qoutlinemapper_p.h \ painting/qpaintdevice.h \ painting/qpaintengine.h \ - painting/qpaintengine_p.h \ + painting/qpaintengine_p.h \ painting/qpaintengine_alpha_p.h \ painting/qpaintengine_preview_p.h \ painting/qpaintengineex_p.h \ @@ -35,6 +36,7 @@ HEADERS += \ painting/qprinter.h \ painting/qprinter_p.h \ painting/qprinterinfo.h \ + painting/qprinterinfo_p.h \ painting/qrasterizer_p.h \ painting/qregion.h \ painting/qstroker_p.h \ @@ -53,6 +55,7 @@ SOURCES += \ painting/qbrush.cpp \ painting/qcolor.cpp \ painting/qcolor_p.cpp \ + painting/qcosmeticstroker.cpp \ painting/qcssutil.cpp \ painting/qdrawutil.cpp \ painting/qemulationpaintengine.cpp \ @@ -74,6 +77,7 @@ SOURCES += \ painting/qprintengine_pdf.cpp \ painting/qprintengine_ps.cpp \ painting/qprinter.cpp \ + painting/qprinterinfo.cpp \ painting/qrasterizer.cpp \ painting/qregion.cpp \ painting/qstroker.cpp \ @@ -88,14 +92,18 @@ SOURCES += \ painting/qpaintengine_raster.cpp \ painting/qdrawhelper.cpp \ painting/qimagescale.cpp \ - painting/qgrayraster.c + painting/qgrayraster.c \ + painting/qpaintengine_blitter.cpp \ + painting/qblittable.cpp \ HEADERS += \ painting/qpaintengine_raster_p.h \ painting/qdrawhelper_p.h \ painting/qblendfunctions_p.h \ painting/qrasterdefs_p.h \ - painting/qgrayraster_p.h + painting/qgrayraster_p.h \ + painting/qpaintengine_blitter_p.h \ + painting/qblittable_p.h \ win32 { HEADERS += painting/qprintengine_win_p.h @@ -116,20 +124,20 @@ embedded { SOURCES += \ painting/qgraphicssystem_qws.cpp \ -} else { +} else: if(!qpa) { HEADERS += \ painting/qgraphicssystem_raster_p.h \ painting/qgraphicssystem_runtime_p.h \ painting/qgraphicssystemfactory_p.h \ painting/qgraphicssystemplugin_p.h \ - painting/qwindowsurface_raster_p.h \ + painting/qwindowsurface_raster_p.h SOURCES += \ painting/qgraphicssystem_raster.cpp \ painting/qgraphicssystem_runtime.cpp \ painting/qgraphicssystemfactory.cpp \ painting/qgraphicssystemplugin.cpp \ - painting/qwindowsurface_raster.cpp \ + painting/qwindowsurface_raster.cpp } unix:x11 { @@ -142,7 +150,7 @@ unix:x11 { painting/qpaintengine_x11.cpp } -!embedded:!x11:mac { +!embedded:!qpa:!x11:mac { HEADERS += \ painting/qpaintengine_mac_p.h \ painting/qgraphicssystem_mac_p.h \ @@ -158,14 +166,14 @@ unix:x11 { painting/qprintengine_mac.mm \ } -unix:!mac:!symbian { +unix:!mac:!symbian|qpa { HEADERS += \ painting/qprinterinfo_unix_p.h SOURCES += \ painting/qprinterinfo_unix.cpp } -win32|x11|mac|embedded|symbian { +win32|x11|mac|embedded|qpa|symbian { SOURCES += painting/qbackingstore.cpp HEADERS += painting/qbackingstore_p.h } @@ -182,6 +190,12 @@ embedded { painting/qpaintdevice_qws.cpp } +qpa { + SOURCES += \ + painting/qcolormap_qpa.cpp \ + painting/qpaintdevice_qpa.cpp +} + symbian { SOURCES += \ painting/qpaintengine_raster_symbian.cpp \ @@ -192,7 +206,7 @@ symbian { painting/qpaintengine_raster_symbian_p.h } -x11|embedded { +x11|embedded|qpa { contains(QT_CONFIG,qtopia) { DEFINES += QT_NO_CUPS QT_NO_LPR } else { @@ -222,9 +236,11 @@ x11 { SOURCES += painting/qwindowsurface_x11.cpp } -mac { - HEADERS += painting/qwindowsurface_mac_p.h - SOURCES += painting/qwindowsurface_mac.cpp +!embedded:!qpa:mac { + HEADERS += painting/qwindowsurface_mac_p.h \ + painting/qunifiedtoolbarsurface_mac_p.h + SOURCES += painting/qwindowsurface_mac.cpp \ + painting/qunifiedtoolbarsurface_mac.cpp } embedded { diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp index 94a5438..6fbb59f 100644 --- a/src/gui/painting/qbackingstore.cpp +++ b/src/gui/painting/qbackingstore.cpp @@ -98,6 +98,21 @@ static inline void qt_flush(QWidget *widget, const QRegion ®ion, QWindowSurfa QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false); #endif + //The performance hit by doing this should be negligible. However, be aware that + //using this FPS when you have > 1 windowsurface can give you inaccurate FPS + static bool fpsDebug = qgetenv("QT_DEBUG_FPS").toInt(); + if (fpsDebug) { + static QTime time = QTime::currentTime(); + static int frames = 0; + + frames++; + + if(time.elapsed() > 5000) { + double fps = double(frames * 1000) /time.restart(); + fprintf(stderr,"FPS: %.1f\n",fps); + frames = 0; + } + } if (widget != tlw) windowSurface->flush(widget, region, tlwOffset + widget->mapTo(tlw, QPoint())); else @@ -271,7 +286,11 @@ bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *wi void QWidgetBackingStore::releaseBuffer() { if (windowSurface) +#if defined(Q_WS_QPA) + windowSurface->resize(QSize()); +#else windowSurface->setGeometry(QRect()); +#endif #ifdef Q_BACKINGSTORE_SUBSURFACES for (int i = 0; i < subSurfaces.size(); ++i) subSurfaces.at(i)->setGeometry(QRect()); @@ -401,7 +420,11 @@ QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const { const bool widgetDirty = widget && widget != tlw; const QRect tlwRect(topLevelRect()); +#if defined(Q_WS_QPA) + const QRect surfaceGeometry(tlwRect.topLeft(), windowSurface->size()); +#else const QRect surfaceGeometry(windowSurface->geometry()); +#endif if (fullUpdatePending || (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size())) { if (widgetDirty) { const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size()); @@ -452,7 +475,11 @@ QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const { if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) { +#if defined(Q_WS_QPA) + const QSize surfaceGeometry(windowSurface->size()); +#else const QRect surfaceGeometry(windowSurface->geometry()); +#endif QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); if (!withinClipRect.isEmpty()) surfaceRect &= withinClipRect; @@ -561,7 +588,7 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up return; } - if (!windowSurface->hasPartialUpdateSupport()) { + if (!windowSurface->hasFeature(QWindowSurface::PartialUpdates)) { fullUpdatePending = true; sendUpdateRequest(tlw, updateImmediately); return; @@ -656,7 +683,7 @@ void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool upd return; } - if (!windowSurface->hasPartialUpdateSupport()) { + if (!windowSurface->hasFeature(QWindowSurface::PartialUpdates)) { fullUpdatePending = true; sendUpdateRequest(tlw, updateImmediately); return; @@ -720,9 +747,8 @@ void QWidgetBackingStore::markDirtyOnScreen(const QRegion ®ion, QWidget *widg } // Alien widgets. - if (!widget->internalWinId()) { - QWidget *nativeParent = widget->nativeParentWidget(); - // Alien widgets with the top-level as the native parent (common case). + if (!widget->internalWinId() && !widget->isWindow()) { + QWidget *nativeParent = widget->nativeParentWidget(); // Alien widgets with the top-level as the native parent (common case). if (nativeParent == tlw) { if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) dirtyOnScreen += region.translated(topLevelOffset); @@ -1117,9 +1143,9 @@ void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedReg return; } - // If there's no partial update support we always need + // If there's no preserved contents support we always need // to do a full repaint before flushing - if (!windowSurface->hasPartialUpdateSupport()) + if (!windowSurface->hasFeature(QWindowSurface::PreservedContents)) fullUpdatePending = true; // Nothing to repaint. @@ -1157,12 +1183,16 @@ void QWidgetBackingStore::sync() return; } - const bool inTopLevelResize = tlwExtra->inTopLevelResize; const bool updatesDisabled = !tlw->updatesEnabled(); - const QRect tlwRect(topLevelRect()); - const QRect surfaceGeometry(windowSurface->geometry()); bool repaintAllWidgets = false; + const bool inTopLevelResize = tlwExtra->inTopLevelResize; + const QRect tlwRect(topLevelRect()); +#ifdef Q_WS_QPA + const QRect surfaceGeometry(tlwRect.topLeft(), windowSurface->size()); +#else + const QRect surfaceGeometry(windowSurface->geometry()); +#endif if ((fullUpdatePending || inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { if (hasStaticContents()) { // Repaint existing dirty area and newly visible area. @@ -1182,8 +1212,13 @@ void QWidgetBackingStore::sync() } } +#ifdef Q_WS_QPA + if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) + windowSurface->resize(tlwRect.size()); +#else if (inTopLevelResize || surfaceGeometry != tlwRect) windowSurface->setGeometry(tlwRect); +#endif if (updatesDisabled) return; @@ -1590,7 +1625,11 @@ void QWidgetPrivate::repaint_sys(const QRegion &rgn) extra->staticContentsSize = data.crect.size(); } +#ifdef Q_WS_QPA //Dont even call q->p + QPaintEngine *engine = 0; +#else QPaintEngine *engine = q->paintEngine(); +#endif // QGLWidget does not support partial updates if: // 1) The context is double buffered // 2) The context is single buffered and auto-fill background is enabled. diff --git a/src/gui/painting/qbackingstore_p.h b/src/gui/painting/qbackingstore_p.h index 7fba1d2..1ef2e4e 100644 --- a/src/gui/painting/qbackingstore_p.h +++ b/src/gui/painting/qbackingstore_p.h @@ -209,8 +209,9 @@ private: { #ifdef Q_WS_QWS return tlw->frameGeometry(); -#endif +#else return tlw->data->crect; +#endif } inline void appendDirtyOnScreenWidget(QWidget *widget) @@ -261,7 +262,7 @@ private: } inline bool hasStaticContents() const - { return !staticWidgets.isEmpty() && windowSurface->hasStaticContentsSupport(); } + { return !staticWidgets.isEmpty() && windowSurface->hasFeature(QWindowSurface::StaticContents); } friend QRegion qt_dirtyRegion(QWidget *); friend class QWidgetPrivate; diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp index 9dfd477..05c1f6e 100644 --- a/src/gui/painting/qbezier.cpp +++ b/src/gui/painting/qbezier.cpp @@ -65,13 +65,6 @@ QT_BEGIN_NAMESPACE #define M_SQRT2 1.41421356237309504880 #endif -#define log2(x) (qLn(x)/qLn(2.)) - -static inline qreal log4(qreal x) -{ - return qreal(0.5) * log2(x); -} - /*! \internal */ @@ -184,7 +177,7 @@ static inline bool findInflections(qreal a, qreal b, qreal c, *t2 = r1; } if (!qFuzzyIsNull(a)) - *tCups = 0.5 * (-b / a); + *tCups = qreal(0.5) * (-b / a); else *tCups = 2; @@ -274,8 +267,8 @@ static ShiftResult good_offset(const QBezier *b1, const QBezier *b2, qreal offse const qreal o2 = offset*offset; const qreal max_dist_line = threshold*offset*offset; const qreal max_dist_normal = threshold*offset; - const qreal spacing = 0.25; - for (qreal i = spacing; i < 0.99; i += spacing) { + const qreal spacing = qreal(0.25); + for (qreal i = spacing; i < qreal(0.99); i += spacing) { QPointF p1 = b1->pointAt(i); QPointF p2 = b2->pointAt(i); qreal d = (p1.x() - p2.x())*(p1.x() - p2.x()) + (p1.y() - p2.y())*(p1.y() - p2.y()); @@ -284,7 +277,7 @@ static ShiftResult good_offset(const QBezier *b1, const QBezier *b2, qreal offse QPointF normalPoint = b1->normalVector(i); qreal l = qAbs(normalPoint.x()) + qAbs(normalPoint.y()); - if (l != 0.) { + if (l != qreal(0.0)) { d = qAbs( normalPoint.x()*(p1.y() - p2.y()) - normalPoint.y()*(p1.x() - p2.x()) ) / l; if (d > max_dist_normal) return Split; @@ -350,7 +343,7 @@ static ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qr QPointF normal_sum = prev_normal + next_normal; - qreal r = 1.0 + prev_normal.x() * next_normal.x() + qreal r = qreal(1.0) + prev_normal.x() * next_normal.x() + prev_normal.y() * next_normal.y(); if (qFuzzyIsNull(r)) { @@ -374,7 +367,7 @@ static ShiftResult shift(const QBezier *orig, QBezier *shifted, qreal offset, qr // This value is used to determine the length of control point vectors // when approximating arc segments as curves. The factor is multiplied // with the radius of the circle. -#define KAPPA 0.5522847498 +#define KAPPA qreal(0.5522847498) static bool addCircle(const QBezier *b, qreal offset, QBezier *o) @@ -417,11 +410,11 @@ static bool addCircle(const QBezier *b, qreal offset, QBezier *o) QPointF circle[3]; circle[0] = QPointF(b->x1, b->y1) + normals[0]*offset; - circle[1] = QPointF(0.5*(b->x1 + b->x4), 0.5*(b->y1 + b->y4)) + normals[1]*offset; + circle[1] = QPointF(qreal(0.5)*(b->x1 + b->x4), qreal(0.5)*(b->y1 + b->y4)) + normals[1]*offset; circle[2] = QPointF(b->x4, b->y4) + normals[2]*offset; for (int i = 0; i < 2; ++i) { - qreal kappa = 2.*KAPPA * sign * offset * angles[i]; + qreal kappa = qreal(2.0) * KAPPA * sign * offset * angles[i]; o->x1 = circle[i].x(); o->y1 = circle[i].y(); @@ -456,8 +449,8 @@ redo: while (b >= beziers) { int stack_segments = b - beziers + 1; if ((stack_segments == 10) || (o - curveSegments == maxSegments - stack_segments)) { - threshold *= 1.5; - if (threshold > 2.) + threshold *= qreal(1.5); + if (threshold > qreal(2.0)) goto give_up; goto redo; } @@ -535,7 +528,7 @@ static inline void splitBezierAt(const QBezier &bez, qreal t, qreal QBezier::length(qreal error) const { - qreal length = 0.0; + qreal length = qreal(0.0); addIfClose(&length, error); @@ -546,8 +539,8 @@ void QBezier::addIfClose(qreal *length, qreal error) const { QBezier left, right; /* bez poly splits */ - qreal len = 0.0; /* arc length */ - qreal chord; /* chord length */ + qreal len = qreal(0.0); /* arc length */ + qreal chord; /* chord length */ len = len + QLineF(QPointF(x1, y1),QPointF(x2, y2)).length(); len = len + QLineF(QPointF(x2, y2),QPointF(x3, y3)).length(); @@ -589,7 +582,7 @@ qreal QBezier::tForY(qreal t0, qreal t1, qreal y) const qreal lt = t0; qreal dt; do { - qreal t = 0.5 * (t0 + t1); + qreal t = qreal(0.5) * (t0 + t1); qreal a, b, c, d; QBezier::coefficients(t, a, b, c, d); @@ -604,7 +597,7 @@ qreal QBezier::tForY(qreal t0, qreal t1, qreal y) const } dt = lt - t; lt = t; - } while (qAbs(dt) > 1e-7); + } while (qAbs(dt) > qreal(1e-7)); return t0; } @@ -661,15 +654,15 @@ int QBezier::stationaryYPoints(qreal &t0, qreal &t1) const qreal QBezier::tAtLength(qreal l) const { qreal len = length(); - qreal t = 1.0; - const qreal error = (qreal)0.01; + qreal t = qreal(1.0); + const qreal error = qreal(0.01); if (l > len || qFuzzyCompare(l, len)) return t; - t *= 0.5; + t *= qreal(0.5); //int iters = 0; //qDebug()<<"LEN is "<<l<<len; - qreal lastBigger = 1.; + qreal lastBigger = qreal(1.0); while (1) { //qDebug()<<"\tt is "<<t; QBezier right = *this; @@ -680,10 +673,10 @@ qreal QBezier::tAtLength(qreal l) const break; if (lLen < l) { - t += (lastBigger - t)*.5; + t += (lastBigger - t) * qreal(0.5); } else { lastBigger = t; - t -= t*.5; + t -= t * qreal(0.5); } //++iters; } diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h index 399dd89..f1f7eb1 100644 --- a/src/gui/painting/qbezier_p.h +++ b/src/gui/painting/qbezier_p.h @@ -162,27 +162,27 @@ inline void QBezier::coefficients(qreal t, qreal &a, qreal &b, qreal &c, qreal & inline QPointF QBezier::pointAt(qreal t) const { -#if 1 - qreal a, b, c, d; - coefficients(t, a, b, c, d); - return QPointF(a*x1 + b*x2 + c*x3 + d*x4, a*y1 + b*y2 + c*y3 + d*y4); -#else // numerically more stable: + qreal x, y; + qreal m_t = 1. - t; - qreal a = x1*m_t + x2*t; - qreal b = x2*m_t + x3*t; - qreal c = x3*m_t + x4*t; - a = a*m_t + b*t; - b = b*m_t + c*t; - qreal x = a*m_t + b*t; - qreal a = y1*m_t + y2*t; - qreal b = y2*m_t + y3*t; - qreal c = y3*m_t + y4*t; - a = a*m_t + b*t; - b = b*m_t + c*t; - qreal y = a*m_t + b*t; + { + qreal a = x1*m_t + x2*t; + qreal b = x2*m_t + x3*t; + qreal c = x3*m_t + x4*t; + a = a*m_t + b*t; + b = b*m_t + c*t; + x = a*m_t + b*t; + } + { + qreal a = y1*m_t + y2*t; + qreal b = y2*m_t + y3*t; + qreal c = y3*m_t + y4*t; + a = a*m_t + b*t; + b = b*m_t + c*t; + y = a*m_t + b*t; + } return QPointF(x, y); -#endif } inline QPointF QBezier::normalVector(qreal t) const diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp index 3a7928e..fd3a8fc 100644 --- a/src/gui/painting/qblendfunctions.cpp +++ b/src/gui/painting/qblendfunctions.cpp @@ -309,9 +309,9 @@ template <typename T> void qt_blend_argb24_on_rgb16(uchar *destPixels, int dbpl, const uchar *src = srcPixels + y * sbpl; const uchar *srcEnd = src + srcOffset; while (src < srcEnd) { -#if defined(QT_ARCH_ARM) || defined(QT_ARCH_POWERPC) || defined(QT_ARCH_SH) || defined(QT_ARCH_AVR32) || (defined(QT_ARCH_WINDOWSCE) && !defined(_X86_)) || (defined(QT_ARCH_SPARC) && defined(Q_CC_GNU)) +#if defined(QT_ARCH_ARMV5) || defined(QT_ARCH_POWERPC) || defined(QT_ARCH_SH) || defined(QT_ARCH_AVR32) || (defined(QT_ARCH_WINDOWSCE) && !defined(_X86_)) || (defined(QT_ARCH_SPARC) && defined(Q_CC_GNU)) || (defined(QT_ARCH_INTEGRITY) && !defined(_X86_)) // non-16-bit aligned memory access is not possible on PowerPC, - // ARM <v6 (QT_ARCH_ARMV6) & SH & AVR32 & SPARC w/GCC + // ARM <v6 (QT_ARCH_ARMV5) & SH & AVR32 & SPARC w/GCC quint16 spix = (quint16(src[2])<<8) + src[1]; #else quint16 spix = *(quint16 *) (src + 1); diff --git a/src/gui/painting/qblendfunctions_p.h b/src/gui/painting/qblendfunctions_p.h index d905a08..76e71b9 100644 --- a/src/gui/painting/qblendfunctions_p.h +++ b/src/gui/painting/qblendfunctions_p.h @@ -275,8 +275,8 @@ void qt_transform_image_rasterize(DestT *destPixels, int dbpl, qreal rightSlope = (bottomRight.x - topRight.x) / (bottomRight.y - topRight.y); int dx_l = int(leftSlope * 0x10000); int dx_r = int(rightSlope * 0x10000); - int x_l = int((topLeft.x + (0.5 + fromY - topLeft.y) * leftSlope + 0.5) * 0x10000); - int x_r = int((topRight.x + (0.5 + fromY - topRight.y) * rightSlope + 0.5) * 0x10000); + int x_l = int((topLeft.x + (qreal(0.5) + fromY - topLeft.y) * leftSlope + qreal(0.5)) * 0x10000); + int x_r = int((topRight.x + (qreal(0.5) + fromY - topRight.y) * rightSlope + qreal(0.5)) * 0x10000); int fromX, toX, x1, x2, u, v, i, ii; DestT *line; @@ -471,8 +471,8 @@ void qt_transform_image(DestT *destPixels, int dbpl, int dvdx = int(m21 * 0x10000); int dudy = int(m12 * 0x10000); int dvdy = int(m22 * 0x10000); - int u0 = qCeil((0.5 * m11 + 0.5 * m12 + mdx) * 0x10000) - 1; - int v0 = qCeil((0.5 * m21 + 0.5 * m22 + mdy) * 0x10000) - 1; + int u0 = qCeil((qreal(0.5) * m11 + qreal(0.5) * m12 + mdx) * 0x10000) - 1; + int v0 = qCeil((qreal(0.5) * m21 + qreal(0.5) * m22 + mdy) * 0x10000) - 1; int x1 = qFloor(sourceRect.left()); int y1 = qFloor(sourceRect.top()); diff --git a/src/gui/painting/qblittable.cpp b/src/gui/painting/qblittable.cpp new file mode 100644 index 0000000..1105858 --- /dev/null +++ b/src/gui/painting/qblittable.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qblittable_p.h" + +#ifndef QT_NO_BLITTABLE +QT_BEGIN_NAMESPACE + +class QBlittablePrivate +{ +public: + QBlittablePrivate(const QSize &size, QBlittable::Capabilities caps) + : caps(caps), m_size(size), locked(false), cachedImg(0) + {} + QBlittable::Capabilities caps; + QSize m_size; + bool locked; + QImage *cachedImg; +}; + + +QBlittable::QBlittable(const QSize &size, Capabilities caps) + : d_ptr(new QBlittablePrivate(size,caps)) +{ +} + +QBlittable::~QBlittable() +{ + delete d_ptr; +} + + +QBlittable::Capabilities QBlittable::capabilities() const +{ + Q_D(const QBlittable); + return d->caps; +} + +QSize QBlittable::size() const +{ + Q_D(const QBlittable); + return d->m_size; +} + +QImage *QBlittable::lock() +{ + Q_D(QBlittable); + if (!d->locked) { + d->cachedImg = doLock(); + d->locked = true; + } + + return d->cachedImg; +} + +void QBlittable::unlock() +{ + Q_D(QBlittable); + if (d->locked) { + doUnlock(); + d->locked = false; + } +} + +QT_END_NAMESPACE +#endif //QT_NO_BLITTABLE + diff --git a/src/gui/painting/qblittable_p.h b/src/gui/painting/qblittable_p.h new file mode 100644 index 0000000..a843733 --- /dev/null +++ b/src/gui/painting/qblittable_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBLITTABLE_P_H +#define QBLITTABLE_P_H + +#include <QtCore/qsize.h> +#include <QtGui/private/qpixmap_blitter_p.h> + + +#ifndef QT_NO_BLITTABLE +QT_BEGIN_NAMESPACE + +class QImage; +class QBlittablePrivate; + +class Q_GUI_EXPORT QBlittable +{ + Q_DECLARE_PRIVATE(QBlittable); +public: + enum Capability { + + SolidRectCapability = 0x0001, + SourcePixmapCapability = 0x0002, + SourceOverPixmapCapability = 0x0004, + SourceOverScaledPixmapCapability = 0x0008, + + // Internal ones + OutlineCapability = 0x0001000, + }; + Q_DECLARE_FLAGS (Capabilities, Capability); + + QBlittable(const QSize &size, Capabilities caps); + virtual ~QBlittable(); + + Capabilities capabilities() const; + QSize size() const; + + virtual void fillRect(const QRectF &rect, const QColor &color) = 0; + virtual void drawPixmap(const QRectF &rect, const QPixmap &pixmap, const QRectF &subrect) = 0; + + QImage *lock(); + void unlock(); + +protected: + virtual QImage *doLock() = 0; + virtual void doUnlock() = 0; + QBlittablePrivate *d_ptr; +}; + +QT_END_NAMESPACE +#endif //QT_NO_BLITTABLE +#endif //QBLITTABLE_P_H diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp index ab23c6c..79c4c4e 100644 --- a/src/gui/painting/qbrush.cpp +++ b/src/gui/painting/qbrush.cpp @@ -636,6 +636,15 @@ QBrush &QBrush::operator=(const QBrush &b) return *this; } + +/*! + \fn void QBrush::swap(QBrush &other) + \since 4.8 + + Swaps brush \a other with this brush. This operation is very + fast and never fails. +*/ + /*! Returns the brush as a QVariant */ @@ -831,6 +840,22 @@ const QGradient *QBrush::gradient() const return 0; } +Q_GUI_EXPORT bool qt_isExtendedRadialGradient(const QBrush &brush) +{ + if (brush.style() == Qt::RadialGradientPattern) { + const QGradient *g = brush.gradient(); + const QRadialGradient *rg = static_cast<const QRadialGradient *>(g); + + if (!qFuzzyIsNull(rg->focalRadius())) + return true; + + QPointF delta = rg->focalPoint() - rg->center(); + if (delta.x() * delta.x() + delta.y() * delta.y() > rg->radius() * rg->radius()) + return true; + } + + return false; +} /*! Returns true if the brush is fully opaque otherwise false. A brush @@ -840,6 +865,7 @@ const QGradient *QBrush::gradient() const \i The alpha component of the color() is 255. \i Its texture() does not have an alpha channel and is not a QBitmap. \i The colors in the gradient() all have an alpha component that is 255. + \i It is an extended radial gradient. \endlist */ @@ -851,6 +877,9 @@ bool QBrush::isOpaque() const if (d->style == Qt::SolidPattern) return opaqueColor; + if (qt_isExtendedRadialGradient(*this)) + return false; + if (d->style == Qt::LinearGradientPattern || d->style == Qt::RadialGradientPattern || d->style == Qt::ConicalGradientPattern) { @@ -1200,8 +1229,10 @@ QDataStream &operator>>(QDataStream &s, QBrush &b) \list \o \e Linear gradients interpolate colors between start and end points. - \o \e Radial gradients interpolate colors between a focal point and end - points on a circle surrounding it. + \o \e Simple radial gradients interpolate colors between a focal point + and end points on a circle surrounding it. + \o \e Extended radial gradients interpolate colors between a center and + a focal circle. \o \e Conical gradients interpolate colors around a center point. \endlist @@ -1497,8 +1528,6 @@ void QGradient::setInterpolationMode(InterpolationMode mode) dummy = p; } -#undef Q_DUMMY_ACCESSOR - /*! \fn bool QGradient::operator!=(const QGradient &gradient) const \since 4.2 @@ -1532,7 +1561,7 @@ bool QGradient::operator==(const QGradient &gradient) const || m_data.radial.cy != gradient.m_data.radial.cy || m_data.radial.fx != gradient.m_data.radial.fx || m_data.radial.fy != gradient.m_data.radial.fy - || m_data.radial.radius != gradient.m_data.radial.radius) + || m_data.radial.cradius != gradient.m_data.radial.cradius) return false; } else { // m_type == ConicalGradient if (m_data.conical.cx != gradient.m_data.conical.cx @@ -1738,10 +1767,17 @@ void QLinearGradient::setFinalStop(const QPointF &stop) \brief The QRadialGradient class is used in combination with QBrush to specify a radial gradient brush. - Radial gradients interpolate colors between a focal point and end - points on a circle surrounding it. Outside the end points the - gradient is either padded, reflected or repeated depending on the - currently set \l {QGradient::Spread}{spread} method: + Qt supports both simple and extended radial gradients. + + Simple radial gradients interpolate colors between a focal point and end + points on a circle surrounding it. Extended radial gradients interpolate + colors between a focal circle and a center circle. Points outside the cone + defined by the two circles will be transparent. For simple radial gradients + the focal point is adjusted to lie inside the center circle, whereas the + focal point can have any position in an extended radial gradient. + + Outside the end points the gradient is either padded, reflected or repeated + depending on the currently set \l {QGradient::Spread}{spread} method: \table \row @@ -1778,7 +1814,7 @@ static QPointF qt_radial_gradient_adapt_focal_point(const QPointF ¢er, // We have a one pixel buffer zone to avoid numerical instability on the // circle border //### this is hacky because technically we should adjust based on current matrix - const qreal compensated_radius = radius - radius * 0.001; + const qreal compensated_radius = radius - radius * qreal(0.001); QLineF line(center, focalPoint); if (line.length() > (compensated_radius)) line.setLength(compensated_radius); @@ -1786,9 +1822,14 @@ static QPointF qt_radial_gradient_adapt_focal_point(const QPointF ¢er, } /*! - Constructs a radial gradient with the given \a center, \a + Constructs a simple radial gradient with the given \a center, \a radius and \a focalPoint. + \note If the given focal point is outside the circle defined by the + \a center point and \a radius, it will be re-adjusted to lie at a point on + the circle where it intersects with the line from \a center to + \a focalPoint. + \sa QGradient::setColorAt(), QGradient::setStops() */ @@ -1798,7 +1839,7 @@ QRadialGradient::QRadialGradient(const QPointF ¢er, qreal radius, const QPoi m_spread = PadSpread; m_data.radial.cx = center.x(); m_data.radial.cy = center.y(); - m_data.radial.radius = radius; + m_data.radial.cradius = radius; QPointF adapted_focal = qt_radial_gradient_adapt_focal_point(center, radius, focalPoint); m_data.radial.fx = adapted_focal.x(); @@ -1806,7 +1847,7 @@ QRadialGradient::QRadialGradient(const QPointF ¢er, qreal radius, const QPoi } /*! - Constructs a radial gradient with the given \a center, \a + Constructs a simple radial gradient with the given \a center, \a radius and the focal point in the circle center. \sa QGradient::setColorAt(), QGradient::setStops() @@ -1817,16 +1858,21 @@ QRadialGradient::QRadialGradient(const QPointF ¢er, qreal radius) m_spread = PadSpread; m_data.radial.cx = center.x(); m_data.radial.cy = center.y(); - m_data.radial.radius = radius; + m_data.radial.cradius = radius; m_data.radial.fx = center.x(); m_data.radial.fy = center.y(); } /*! - Constructs a radial gradient with the given center (\a cx, \a cy), + Constructs a simple radial gradient with the given center (\a cx, \a cy), \a radius and focal point (\a fx, \a fy). + \note If the given focal point is outside the circle defined by the + center (\a cx, \a cy) and the \a radius it will be re-adjusted to + the intersection between the line from the center to the focal point + and the circle. + \sa QGradient::setColorAt(), QGradient::setStops() */ @@ -1836,7 +1882,7 @@ QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qre m_spread = PadSpread; m_data.radial.cx = cx; m_data.radial.cy = cy; - m_data.radial.radius = radius; + m_data.radial.cradius = radius; QPointF adapted_focal = qt_radial_gradient_adapt_focal_point(QPointF(cx, cy), radius, @@ -1847,7 +1893,7 @@ QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qre } /*! - Constructs a radial gradient with the center at (\a cx, \a cy) and the + Constructs a simple radial gradient with the center at (\a cx, \a cy) and the specified \a radius. The focal point lies at the center of the circle. \sa QGradient::setColorAt(), QGradient::setStops() @@ -1858,14 +1904,14 @@ QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius) m_spread = PadSpread; m_data.radial.cx = cx; m_data.radial.cy = cy; - m_data.radial.radius = radius; + m_data.radial.cradius = radius; m_data.radial.fx = cx; m_data.radial.fy = cy; } /*! - Constructs a radial gradient with the center and focal point at + Constructs a simple radial gradient with the center and focal point at (0, 0) with a radius of 1. */ QRadialGradient::QRadialGradient() @@ -1874,11 +1920,49 @@ QRadialGradient::QRadialGradient() m_spread = PadSpread; m_data.radial.cx = 0; m_data.radial.cy = 0; - m_data.radial.radius = 1; + m_data.radial.cradius = 1; m_data.radial.fx = 0; m_data.radial.fy = 0; } +/*! + \since 4.8 + + Constructs an extended radial gradient with the given \a center, \a + centerRadius, \a focalPoint, and \a focalRadius. +*/ +QRadialGradient::QRadialGradient(const QPointF ¢er, qreal centerRadius, const QPointF &focalPoint, qreal focalRadius) +{ + m_type = RadialGradient; + m_spread = PadSpread; + m_data.radial.cx = center.x(); + m_data.radial.cy = center.y(); + m_data.radial.cradius = centerRadius; + + m_data.radial.fx = focalPoint.x(); + m_data.radial.fy = focalPoint.y(); + setFocalRadius(focalRadius); +} + +/*! + \since 4.8 + + Constructs an extended radial gradient with the given center + (\a cx, \a cy), center radius, \a centerRadius, focal point, (\a fx, \a fy), + and focal radius \a focalRadius. +*/ +QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal centerRadius, qreal fx, qreal fy, qreal focalRadius) +{ + m_type = RadialGradient; + m_spread = PadSpread; + m_data.radial.cx = cx; + m_data.radial.cy = cy; + m_data.radial.cradius = centerRadius; + + m_data.radial.fx = fx; + m_data.radial.fy = fy; + setFocalRadius(focalRadius); +} /*! Returns the center of this radial gradient in logical coordinates. @@ -1923,13 +2007,15 @@ void QRadialGradient::setCenter(const QPointF ¢er) /*! Returns the radius of this radial gradient in logical coordinates. + Equivalent to centerRadius() + \sa QGradient::stops() */ qreal QRadialGradient::radius() const { Q_ASSERT(m_type == RadialGradient); - return m_data.radial.radius; + return m_data.radial.cradius; } @@ -1938,13 +2024,81 @@ qreal QRadialGradient::radius() const Sets the radius of this radial gradient in logical coordinates to \a radius + + Equivalent to setCenterRadius() */ void QRadialGradient::setRadius(qreal radius) { Q_ASSERT(m_type == RadialGradient); - m_data.radial.radius = radius; + m_data.radial.cradius = radius; +} + +/*! + \since 4.8 + + Returns the center radius of this radial gradient in logical + coordinates. + + \sa QGradient::stops() +*/ +qreal QRadialGradient::centerRadius() const +{ + Q_ASSERT(m_type == RadialGradient); + return m_data.radial.cradius; +} + +/*! + \since 4.8 + + Sets the center radius of this radial gradient in logical coordinates + to \a radius +*/ +void QRadialGradient::setCenterRadius(qreal radius) +{ + Q_ASSERT(m_type == RadialGradient); + m_data.radial.cradius = radius; +} + +/*! + \since 4.8 + + Returns the focal radius of this radial gradient in logical + coordinates. + + \sa QGradient::stops() +*/ +qreal QRadialGradient::focalRadius() const +{ + Q_ASSERT(m_type == RadialGradient); + Q_DUMMY_ACCESSOR + + // mask away low three bits + union { float f; quint32 i; } u; + u.i = i & ~0x07; + return u.f; } +/*! + \since 4.8 + + Sets the focal radius of this radial gradient in logical coordinates + to \a radius +*/ +void QRadialGradient::setFocalRadius(qreal radius) +{ + Q_ASSERT(m_type == RadialGradient); + Q_DUMMY_ACCESSOR + + // Since there's no QGradientData, we only have the dummy void * to + // store additional data in. The three lowest bits are already + // taken, thus we cut the three lowest bits from the significand + // and store the radius as a float. + union { float f; quint32 i; } u; + u.f = float(radius); + // add 0x04 to round up when we drop the three lowest bits + i |= (u.i + 0x04) & ~0x07; + dummy = p; +} /*! Returns the focal point of this radial gradient in logical @@ -2184,4 +2338,6 @@ void QConicalGradient::setAngle(qreal angle) \sa setTransform() */ +#undef Q_DUMMY_ACCESSOR + QT_END_NAMESPACE diff --git a/src/gui/painting/qbrush.h b/src/gui/painting/qbrush.h index 5679553..4a0bfcc 100644 --- a/src/gui/painting/qbrush.h +++ b/src/gui/painting/qbrush.h @@ -92,6 +92,12 @@ public: ~QBrush(); QBrush &operator=(const QBrush &brush); +#ifdef Q_COMPILER_RVALUE_REFS + inline QBrush &operator=(QBrush &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QBrush &other) { qSwap(d, other.d); } + operator QVariant() const; inline Qt::BrushStyle style() const; @@ -249,6 +255,7 @@ private: friend class QLinearGradient; friend class QRadialGradient; friend class QConicalGradient; + friend class QBrush; Type m_type; Spread m_spread; @@ -258,7 +265,7 @@ private: qreal x1, y1, x2, y2; } linear; struct { - qreal cx, cy, fx, fy, radius; + qreal cx, cy, fx, fy, cradius; } radial; struct { qreal cx, cy, angle; @@ -297,6 +304,9 @@ public: QRadialGradient(const QPointF ¢er, qreal radius); QRadialGradient(qreal cx, qreal cy, qreal radius); + QRadialGradient(const QPointF ¢er, qreal centerRadius, const QPointF &focalPoint, qreal focalRadius); + QRadialGradient(qreal cx, qreal cy, qreal centerRadius, qreal fx, qreal fy, qreal focalRadius); + QPointF center() const; void setCenter(const QPointF ¢er); inline void setCenter(qreal x, qreal y) { setCenter(QPointF(x, y)); } @@ -307,6 +317,12 @@ public: qreal radius() const; void setRadius(qreal radius); + + qreal centerRadius() const; + void setCenterRadius(qreal radius); + + qreal focalRadius() const; + void setFocalRadius(qreal radius); }; diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index ddbf6c0..3d895b7 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -532,8 +532,7 @@ QString QColor::name() const void QColor::setNamedColor(const QString &name) { - if (!setColorFromString(name)) - qWarning("QColor::setNamedColor: Unknown color name '%s'", name.toLatin1().constData()); + setColorFromString(name); } /*! @@ -629,7 +628,7 @@ void QColor::getHsvF(qreal *h, qreal *s, qreal *v, qreal *a) const return; } - *h = ct.ahsv.hue == USHRT_MAX ? -1.0 : ct.ahsv.hue / 36000.0; + *h = ct.ahsv.hue == USHRT_MAX ? qreal(-1.0) : ct.ahsv.hue / qreal(36000.0); *s = ct.ahsv.saturation / qreal(USHRT_MAX); *v = ct.ahsv.value / qreal(USHRT_MAX); @@ -676,17 +675,17 @@ void QColor::getHsv(int *h, int *s, int *v, int *a) const */ void QColor::setHsvF(qreal h, qreal s, qreal v, qreal a) { - if (((h < 0.0 || h > 1.0) && h != -1.0) - || (s < 0.0 || s > 1.0) - || (v < 0.0 || v > 1.0) - || (a < 0.0 || a > 1.0)) { + if (((h < qreal(0.0) || h > qreal(1.0)) && h != qreal(-1.0)) + || (s < qreal(0.0) || s > qreal(1.0)) + || (v < qreal(0.0) || v > qreal(1.0)) + || (a < qreal(0.0) || a > qreal(1.0))) { qWarning("QColor::setHsvF: HSV parameters out of range"); return; } cspec = Hsv; ct.ahsv.alpha = qRound(a * USHRT_MAX); - ct.ahsv.hue = h == -1.0 ? USHRT_MAX : qRound(h * 36000); + ct.ahsv.hue = h == qreal(-1.0) ? USHRT_MAX : qRound(h * 36000); ct.ahsv.saturation = qRound(s * USHRT_MAX); ct.ahsv.value = qRound(v * USHRT_MAX); ct.ahsv.pad = 0; @@ -740,7 +739,7 @@ void QColor::getHslF(qreal *h, qreal *s, qreal *l, qreal *a) const return; } - *h = ct.ahsl.hue == USHRT_MAX ? -1.0 : ct.ahsl.hue / 36000.0; + *h = ct.ahsl.hue == USHRT_MAX ? qreal(-1.0) : ct.ahsl.hue / qreal(36000.0); *s = ct.ahsl.saturation / qreal(USHRT_MAX); *l = ct.ahsl.lightness / qreal(USHRT_MAX); @@ -790,17 +789,17 @@ void QColor::getHsl(int *h, int *s, int *l, int *a) const */ void QColor::setHslF(qreal h, qreal s, qreal l, qreal a) { - if (((h < 0.0 || h > 1.0) && h != -1.0) - || (s < 0.0 || s > 1.0) - || (l < 0.0 || l > 1.0) - || (a < 0.0 || a > 1.0)) { + if (((h < qreal(0.0) || h > qreal(1.0)) && h != qreal(-1.0)) + || (s < qreal(0.0) || s > qreal(1.0)) + || (l < qreal(0.0) || l > qreal(1.0)) + || (a < qreal(0.0) || a > qreal(1.0))) { qWarning("QColor::setHsvF: HSV parameters out of range"); return; } cspec = Hsl; ct.ahsl.alpha = qRound(a * USHRT_MAX); - ct.ahsl.hue = h == -1.0 ? USHRT_MAX : qRound(h * 36000); + ct.ahsl.hue = h == qreal(-1.0) ? USHRT_MAX : qRound(h * 36000); ct.ahsl.saturation = qRound(s * USHRT_MAX); ct.ahsl.lightness = qRound(l * USHRT_MAX); ct.ahsl.pad = 0; @@ -909,10 +908,10 @@ void QColor::getRgb(int *r, int *g, int *b, int *a) const */ void QColor::setRgbF(qreal r, qreal g, qreal b, qreal a) { - if (r < 0.0 || r > 1.0 - || g < 0.0 || g > 1.0 - || b < 0.0 || b > 1.0 - || a < 0.0 || a > 1.0) { + if (r < qreal(0.0) || r > qreal(1.0) + || g < qreal(0.0) || g > qreal(1.0) + || b < qreal(0.0) || b > qreal(1.0) + || a < qreal(0.0) || a > qreal(1.0)) { qWarning("QColor::setRgbF: RGB parameters out of range"); invalidate(); return; @@ -1322,7 +1321,7 @@ qreal QColor::hsvHueF() const { if (cspec != Invalid && cspec != Hsv) return toHsv().hueF(); - return ct.ahsv.hue == USHRT_MAX ? -1.0 : ct.ahsv.hue / 36000.0; + return ct.ahsv.hue == USHRT_MAX ? qreal(-1.0) : ct.ahsv.hue / qreal(36000.0); } /*! @@ -1418,7 +1417,7 @@ qreal QColor::hslHueF() const { if (cspec != Invalid && cspec != Hsl) return toHsl().hslHueF(); - return ct.ahsl.hue == USHRT_MAX ? -1.0 : ct.ahsl.hue / 36000.0; + return ct.ahsl.hue == USHRT_MAX ? qreal(-1.0) : ct.ahsl.hue / qreal(36000.0); } /*! @@ -1584,10 +1583,10 @@ QColor QColor::toRgb() const const qreal v = ct.ahsv.value / qreal(USHRT_MAX); const int i = int(h); const qreal f = h - i; - const qreal p = v * (1.0 - s); + const qreal p = v * (qreal(1.0) - s); if (i & 1) { - const qreal q = v * (1.0 - (s * f)); + const qreal q = v * (qreal(1.0) - (s * f)); switch (i) { case 1: @@ -1607,7 +1606,7 @@ QColor QColor::toRgb() const break; } } else { - const qreal t = v * (1.0 - (s * (1.0 - f))); + const qreal t = v * (qreal(1.0) - (s * (qreal(1.0) - f))); switch (i) { case 0: @@ -1683,9 +1682,9 @@ QColor QColor::toRgb() const const qreal y = ct.acmyk.yellow / qreal(USHRT_MAX); const qreal k = ct.acmyk.black / qreal(USHRT_MAX); - color.ct.argb.red = qRound((1.0 - (c * (1.0 - k) + k)) * USHRT_MAX); - color.ct.argb.green = qRound((1.0 - (m * (1.0 - k) + k)) * USHRT_MAX); - color.ct.argb.blue = qRound((1.0 - (y * (1.0 - k) + k)) * USHRT_MAX); + color.ct.argb.red = qRound((qreal(1.0) - (c * (qreal(1.0) - k) + k)) * USHRT_MAX); + color.ct.argb.green = qRound((qreal(1.0) - (m * (qreal(1.0) - k) + k)) * USHRT_MAX); + color.ct.argb.blue = qRound((qreal(1.0) - (y * (qreal(1.0) - k) + k)) * USHRT_MAX); break; } default: @@ -1737,15 +1736,15 @@ QColor QColor::toHsv() const if (qFuzzyCompare(r, max)) { hue = ((g - b) /delta); } else if (qFuzzyCompare(g, max)) { - hue = (2.0 + (b - r) / delta); + hue = (qreal(2.0) + (b - r) / delta); } else if (qFuzzyCompare(b, max)) { - hue = (4.0 + (r - g) / delta); + hue = (qreal(4.0) + (r - g) / delta); } else { Q_ASSERT_X(false, "QColor::toHsv", "internal error"); } - hue *= 60.0; - if (hue < 0.0) - hue += 360.0; + hue *= qreal(60.0); + if (hue < qreal(0.0)) + hue += qreal(360.0); color.ct.ahsv.hue = qRound(hue * 100); } @@ -1781,7 +1780,7 @@ QColor QColor::toHsl() const color.ct.ahsl.lightness = qRound(lightness * USHRT_MAX); if (qFuzzyIsNull(delta)) { // achromatic case, hue is undefined - color.ct.ahsl.hue = 0; + color.ct.ahsl.hue = USHRT_MAX; color.ct.ahsl.saturation = 0; } else { // chromatic case @@ -1793,15 +1792,15 @@ QColor QColor::toHsl() const if (qFuzzyCompare(r, max)) { hue = ((g - b) /delta); } else if (qFuzzyCompare(g, max)) { - hue = (2.0 + (b - r) / delta); + hue = (qreal(2.0) + (b - r) / delta); } else if (qFuzzyCompare(b, max)) { - hue = (4.0 + (r - g) / delta); + hue = (qreal(4.0) + (r - g) / delta); } else { Q_ASSERT_X(false, "QColor::toHsv", "internal error"); } - hue *= 60.0; - if (hue < 0.0) - hue += 360.0; + hue *= qreal(60.0); + if (hue < qreal(0.0)) + hue += qreal(360.0); color.ct.ahsl.hue = qRound(hue * 100); } @@ -1829,17 +1828,17 @@ QColor QColor::toCmyk() const const qreal r = ct.argb.red / qreal(USHRT_MAX); const qreal g = ct.argb.green / qreal(USHRT_MAX); const qreal b = ct.argb.blue / qreal(USHRT_MAX); - qreal c = 1.0 - r; - qreal m = 1.0 - g; - qreal y = 1.0 - b; + qreal c = qreal(1.0) - r; + qreal m = qreal(1.0) - g; + qreal y = qreal(1.0) - b; // cmy -> cmyk const qreal k = qMin(c, qMin(m, y)); if (!qFuzzyIsNull(k - 1)) { - c = (c - k) / (1.0 - k); - m = (m - k) / (1.0 - k); - y = (y - k) / (1.0 - k); + c = (c - k) / (qreal(1.0) - k); + m = (m - k) / (qreal(1.0) - k); + y = (y - k) / (qreal(1.0) - k); } color.ct.acmyk.cyan = qRound(c * USHRT_MAX); @@ -1942,10 +1941,10 @@ QColor QColor::fromRgb(int r, int g, int b, int a) */ QColor QColor::fromRgbF(qreal r, qreal g, qreal b, qreal a) { - if (r < 0.0 || r > 1.0 - || g < 0.0 || g > 1.0 - || b < 0.0 || b > 1.0 - || a < 0.0 || a > 1.0) { + if (r < qreal(0.0) || r > qreal(1.0) + || g < qreal(0.0) || g > qreal(1.0) + || b < qreal(0.0) || b > qreal(1.0) + || a < qreal(0.0) || a > qreal(1.0)) { qWarning("QColor::fromRgbF: RGB parameters out of range"); return QColor(); } @@ -2005,10 +2004,10 @@ QColor QColor::fromHsv(int h, int s, int v, int a) */ QColor QColor::fromHsvF(qreal h, qreal s, qreal v, qreal a) { - if (((h < 0.0 || h > 1.0) && h != -1.0) - || (s < 0.0 || s > 1.0) - || (v < 0.0 || v > 1.0) - || (a < 0.0 || a > 1.0)) { + if (((h < qreal(0.0) || h > qreal(1.0)) && h != qreal(-1.0)) + || (s < qreal(0.0) || s > qreal(1.0)) + || (v < qreal(0.0) || v > qreal(1.0)) + || (a < qreal(0.0) || a > qreal(1.0))) { qWarning("QColor::fromHsvF: HSV parameters out of range"); return QColor(); } @@ -2016,7 +2015,7 @@ QColor QColor::fromHsvF(qreal h, qreal s, qreal v, qreal a) QColor color; color.cspec = Hsv; color.ct.ahsv.alpha = qRound(a * USHRT_MAX); - color.ct.ahsv.hue = h == -1.0 ? USHRT_MAX : qRound(h * 36000); + color.ct.ahsv.hue = h == qreal(-1.0) ? USHRT_MAX : qRound(h * 36000); color.ct.ahsv.saturation = qRound(s * USHRT_MAX); color.ct.ahsv.value = qRound(v * USHRT_MAX); color.ct.ahsv.pad = 0; @@ -2069,10 +2068,10 @@ QColor QColor::fromHsl(int h, int s, int l, int a) */ QColor QColor::fromHslF(qreal h, qreal s, qreal l, qreal a) { - if (((h < 0.0 || h > 1.0) && h != -1.0) - || (s < 0.0 || s > 1.0) - || (l < 0.0 || l > 1.0) - || (a < 0.0 || a > 1.0)) { + if (((h < qreal(0.0) || h > qreal(1.0)) && h != qreal(-1.0)) + || (s < qreal(0.0) || s > qreal(1.0)) + || (l < qreal(0.0) || l > qreal(1.0)) + || (a < qreal(0.0) || a > qreal(1.0))) { qWarning("QColor::fromHsvF: HSV parameters out of range"); return QColor(); } @@ -2080,7 +2079,7 @@ QColor QColor::fromHslF(qreal h, qreal s, qreal l, qreal a) QColor color; color.cspec = Hsl; color.ct.ahsl.alpha = qRound(a * USHRT_MAX); - color.ct.ahsl.hue = (h == -1.0) ? USHRT_MAX : qRound(h * 36000); + color.ct.ahsl.hue = (h == qreal(-1.0)) ? USHRT_MAX : qRound(h * 36000); if (color.ct.ahsl.hue == 36000) color.ct.ahsl.hue = 0; color.ct.ahsl.saturation = qRound(s * USHRT_MAX); @@ -2189,11 +2188,11 @@ void QColor::setCmyk(int c, int m, int y, int k, int a) */ void QColor::setCmykF(qreal c, qreal m, qreal y, qreal k, qreal a) { - if (c < 0.0 || c > 1.0 - || m < 0.0 || m > 1.0 - || y < 0.0 || y > 1.0 - || k < 0.0 || k > 1.0 - || a < 0.0 || a > 1.0) { + if (c < qreal(0.0) || c > qreal(1.0) + || m < qreal(0.0) || m > qreal(1.0) + || y < qreal(0.0) || y > qreal(1.0) + || k < qreal(0.0) || k > qreal(1.0) + || a < qreal(0.0) || a > qreal(1.0)) { qWarning("QColor::setCmykF: CMYK parameters out of range"); return; } @@ -2251,11 +2250,11 @@ QColor QColor::fromCmyk(int c, int m, int y, int k, int a) */ QColor QColor::fromCmykF(qreal c, qreal m, qreal y, qreal k, qreal a) { - if (c < 0.0 || c > 1.0 - || m < 0.0 || m > 1.0 - || y < 0.0 || y > 1.0 - || k < 0.0 || k > 1.0 - || a < 0.0 || a > 1.0) { + if (c < qreal(0.0) || c > qreal(1.0) + || m < qreal(0.0) || m > qreal(1.0) + || y < qreal(0.0) || y > qreal(1.0) + || k < qreal(0.0) || k > qreal(1.0) + || a < qreal(0.0) || a > qreal(1.0)) { qWarning("QColor::fromCmykF: CMYK parameters out of range"); return QColor(); } diff --git a/src/gui/painting/qcolor_p.cpp b/src/gui/painting/qcolor_p.cpp index 588b73d..454fe82 100644 --- a/src/gui/painting/qcolor_p.cpp +++ b/src/gui/painting/qcolor_p.cpp @@ -49,9 +49,6 @@ #include "qrgb.h" #include "qstringlist.h" -#if defined(Q_WS_WINCE) -#include "qguifunctions_wince.h" -#endif QT_BEGIN_NAMESPACE static inline int h2i(char hex) @@ -290,33 +287,16 @@ static const int rgbTblSize = sizeof(rgbTbl) / sizeof(RGBData); #undef rgb -QT_BEGIN_INCLUDE_NAMESPACE -#include <stdlib.h> -QT_END_INCLUDE_NAMESPACE - -#if defined(Q_C_CALLBACKS) -extern "C" { -#endif - -#ifdef Q_OS_WINCE -static int __cdecl rgb_cmp(const void *d1, const void *d2) -#else -static int rgb_cmp(const void *d1, const void *d2) -#endif -{ - return qstricmp(((RGBData *)d1)->name, ((RGBData *)d2)->name); -} - -#if defined(Q_C_CALLBACKS) -} -#endif +inline bool operator<(const char *name, const RGBData &data) +{ return qstrcmp(name, data.name) < 0; } +inline bool operator<(const RGBData &data, const char *name) +{ return qstrcmp(data.name, name) < 0; } -static bool get_named_rgb(const char *name, QRgb *rgb) +static bool get_named_rgb(const char *name_no_space, QRgb *rgb) { - RGBData x; - x.name = name; - RGBData *r = (RGBData*)bsearch(&x, rgbTbl, rgbTblSize, sizeof(RGBData), rgb_cmp); - if (r) { + QByteArray name = QByteArray(name_no_space).toLower(); + const RGBData *r = qBinaryFind(rgbTbl, rgbTbl + rgbTblSize, name.constData()); + if (r != rgbTbl + rgbTblSize) { *rgb = r->value; return true; } diff --git a/src/gui/painting/qcolormap_qpa.cpp b/src/gui/painting/qcolormap_qpa.cpp new file mode 100644 index 0000000..40bf364 --- /dev/null +++ b/src/gui/painting/qcolormap_qpa.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcolormap.h" +#include "qcolor.h" +#include "qpaintdevice.h" +#include "private/qapplication_p.h" +#include "private/qgraphicssystem_p.h" + +QT_BEGIN_NAMESPACE + +class QColormapPrivate +{ +public: + inline QColormapPrivate() + : ref(1), mode(QColormap::Direct), depth(0), numcolors(0) + { } + + QAtomicInt ref; + + QColormap::Mode mode; + int depth; + int numcolors; +}; + +static QColormapPrivate *screenMap = 0; + +void QColormap::initialize() +{ + screenMap = new QColormapPrivate; + + QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); + QList<QPlatformScreen*> screens = pi->screens(); + + screenMap->depth = screens.at(0)->depth(); + if (screenMap->depth < 8) { + screenMap->mode = QColormap::Indexed; + screenMap->numcolors = 256; + } else { + screenMap->mode = QColormap::Direct; + screenMap->numcolors = -1; + } +} + +void QColormap::cleanup() +{ + delete screenMap; + screenMap = 0; +} + +QColormap QColormap::instance(int /*screen*/) +{ + return QColormap(); +} + +QColormap::QColormap() + : d(screenMap) +{ d->ref.ref(); } + +QColormap::QColormap(const QColormap &colormap) + :d (colormap.d) +{ d->ref.ref(); } + +QColormap::~QColormap() +{ + if (!d->ref.deref()) + delete d; +} + +QColormap::Mode QColormap::mode() const +{ return d->mode; } + + +int QColormap::depth() const +{ return d->depth; } + + +int QColormap::size() const +{ + return d->numcolors; +} + +#ifndef QT_QWS_DEPTH16_RGB +#define QT_QWS_DEPTH16_RGB 565 +#endif +static const int qt_rbits = (QT_QWS_DEPTH16_RGB/100); +static const int qt_gbits = (QT_QWS_DEPTH16_RGB/10%10); +static const int qt_bbits = (QT_QWS_DEPTH16_RGB%10); +static const int qt_red_shift = qt_bbits+qt_gbits-(8-qt_rbits); +static const int qt_green_shift = qt_bbits-(8-qt_gbits); +static const int qt_neg_blue_shift = 8-qt_bbits; +static const int qt_blue_mask = (1<<qt_bbits)-1; +static const int qt_green_mask = (1<<(qt_gbits+qt_bbits))-(1<<qt_bbits); +static const int qt_red_mask = (1<<(qt_rbits+qt_gbits+qt_bbits))-(1<<(qt_gbits+qt_bbits)); + +static const int qt_red_rounding_shift = qt_red_shift + qt_rbits; +static const int qt_green_rounding_shift = qt_green_shift + qt_gbits; +static const int qt_blue_rounding_shift = qt_bbits - qt_neg_blue_shift; + +inline ushort qt_convRgbTo16(QRgb c) +{ + const int tr = qRed(c) << qt_red_shift; + const int tg = qGreen(c) << qt_green_shift; + const int tb = qBlue(c) >> qt_neg_blue_shift; + + return (tb & qt_blue_mask) | (tg & qt_green_mask) | (tr & qt_red_mask); +} + +inline QRgb qt_conv16ToRgb(ushort c) +{ + const int r=(c & qt_red_mask); + const int g=(c & qt_green_mask); + const int b=(c & qt_blue_mask); + const int tr = r >> qt_red_shift | r >> qt_red_rounding_shift; + const int tg = g >> qt_green_shift | g >> qt_green_rounding_shift; + const int tb = b << qt_neg_blue_shift | b >> qt_blue_rounding_shift; + + return qRgb(tr,tg,tb); +} + +uint QColormap::pixel(const QColor &color) const +{ + QRgb rgb = color.rgba(); + if (d->mode == QColormap::Direct) { + switch(d->depth) { + case 16: + return qt_convRgbTo16(rgb); + case 24: + case 32: + { + const int r = qRed(rgb); + const int g = qGreen(rgb); + const int b = qBlue(rgb); + const int red_shift = 16; + const int green_shift = 8; + const int red_mask = 0xff0000; + const int green_mask = 0x00ff00; + const int blue_mask = 0x0000ff; + const int tg = g << green_shift; +#ifdef QT_QWS_DEPTH_32_BGR + if (qt_screen->pixelType() == QScreen::BGRPixel) { + const int tb = b << red_shift; + return 0xff000000 | (r & blue_mask) | (tg & green_mask) | (tb & red_mask); + } +#endif + const int tr = r << red_shift; + return 0xff000000 | (b & blue_mask) | (tg & green_mask) | (tr & red_mask); + } + } + } + //XXX + //return qt_screen->alloc(qRed(rgb), qGreen(rgb), qBlue(rgb)); + return 0; +} + +const QColor QColormap::colorAt(uint pixel) const +{ + if (d->mode == Direct) { + if (d->depth == 16) { + pixel = qt_conv16ToRgb(pixel); + } + const int red_shift = 16; + const int green_shift = 8; + const int red_mask = 0xff0000; + const int green_mask = 0x00ff00; + const int blue_mask = 0x0000ff; +#ifdef QT_QWS_DEPTH_32_BGR + if (qt_screen->pixelType() == QScreen::BGRPixel) { + return QColor((pixel & blue_mask), + (pixel & green_mask) >> green_shift, + (pixel & red_mask) >> red_shift); + } +#endif + return QColor((pixel & red_mask) >> red_shift, + (pixel & green_mask) >> green_shift, + (pixel & blue_mask)); + } +#if 0 // XXX + Q_ASSERT_X(int(pixel) < qt_screen->numCols(), "QColormap::colorAt", "pixel out of bounds of palette"); + return QColor(qt_screen->clut()[pixel]); +#endif + return QColor(); +} + +const QVector<QColor> QColormap::colormap() const +{ + return QVector<QColor>(); +} + +QColormap &QColormap::operator=(const QColormap &colormap) +{ qAtomicAssign(d, colormap.d); return *this; } + +QT_END_NAMESPACE diff --git a/src/gui/painting/qcosmeticstroker.cpp b/src/gui/painting/qcosmeticstroker.cpp new file mode 100644 index 0000000..3528e6f --- /dev/null +++ b/src/gui/painting/qcosmeticstroker.cpp @@ -0,0 +1,1012 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcosmeticstroker_p.h" +#include "private/qpainterpath_p.h" +#include <qdebug.h> +#include <math.h> + +QT_BEGIN_NAMESPACE + +#if 0 +inline QString capString(int caps) +{ + QString str; + if (caps & QCosmeticStroker::CapBegin) { + str += "CapBegin "; + } + if (caps & QCosmeticStroker::CapEnd) { + str += "CapEnd "; + } + return str; +} +#endif + +#define toF26Dot6(x) ((int)((x)*64.)) + +static inline uint sourceOver(uint d, uint color) +{ + return color + BYTE_MUL(d, qAlpha(~color)); +} + +inline static int F16Dot16FixedDiv(int x, int y) +{ + if (qAbs(x) > 0x7fff) + return (((qlonglong)x) << 16) / y; + return (x << 16) / y; +} + +typedef void (*DrawPixel)(QCosmeticStroker *stroker, int x, int y, int coverage); + +namespace { + +struct Dasher { + QCosmeticStroker *stroker; + int *pattern; + int offset; + int dashIndex; + int dashOn; + + Dasher(QCosmeticStroker *s, bool reverse, int start, int stop) + : stroker(s) + { + int delta = stop - start; + if (reverse) { + pattern = stroker->reversePattern; + offset = stroker->patternLength - stroker->patternOffset - delta - ((start & 63) - 32); + dashOn = 0; + } else { + pattern = stroker->pattern; + offset = stroker->patternOffset - ((start & 63) - 32); + dashOn = 1; + } + offset %= stroker->patternLength; + if (offset < 0) + offset += stroker->patternLength; + + dashIndex = 0; + while (offset>= pattern[dashIndex]) + ++dashIndex; + +// qDebug() << " dasher" << offset/64. << reverse << dashIndex; + stroker->patternOffset += delta; + stroker->patternOffset %= stroker->patternLength; + } + + bool on() const { + return (dashIndex + dashOn) & 1; + } + void adjust() { + offset += 64; + if (offset >= pattern[dashIndex]) { + ++dashIndex; + dashIndex %= stroker->patternSize; + } + offset %= stroker->patternLength; +// qDebug() << "dasher.adjust" << offset/64. << dashIndex; + } +}; + +struct NoDasher { + NoDasher(QCosmeticStroker *, bool, int, int) {} + bool on() const { return true; } + void adjust(int = 0) {} +}; + +}; + +template<DrawPixel drawPixel, class Dasher> +static void drawLine(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps); +template<DrawPixel drawPixel, class Dasher> +static void drawLineAA(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps); + +inline void drawPixel(QCosmeticStroker *stroker, int x, int y, int coverage) +{ + const QRect &cl = stroker->clip; + if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom()) + return; + + int lastx = stroker->spans[stroker->current_span-1].x + stroker->spans[stroker->current_span-1].len ; + int lasty = stroker->spans[stroker->current_span-1].y; + + if (stroker->current_span == QCosmeticStroker::NSPANS || y < lasty || (y == lasty && x < lastx)) { + stroker->blend(stroker->current_span, stroker->spans, &stroker->state->penData); + stroker->current_span = 0; + } + + stroker->spans[stroker->current_span].x = ushort(x); + stroker->spans[stroker->current_span].len = 1; + stroker->spans[stroker->current_span].y = y; + stroker->spans[stroker->current_span].coverage = coverage*stroker->opacity >> 8; + ++stroker->current_span; +} + +inline void drawPixelARGB32(QCosmeticStroker *stroker, int x, int y, int coverage) +{ + const QRect &cl = stroker->clip; + if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom()) + return; + + int offset = x + stroker->ppl*y; + uint c = BYTE_MUL(stroker->color, coverage); + stroker->pixels[offset] = sourceOver(stroker->pixels[offset], c); +} + +inline void drawPixelARGB32Opaque(QCosmeticStroker *stroker, int x, int y, int) +{ + const QRect &cl = stroker->clip; + if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom()) + return; + + int offset = x + stroker->ppl*y; + stroker->pixels[offset] = sourceOver(stroker->pixels[offset], stroker->color); +} + +enum StrokeSelection { + Aliased = 0, + AntiAliased = 1, + Solid = 0, + Dashed = 2, + RegularDraw = 0, + FastDraw = 4 +}; + +static StrokeLine strokeLine(int strokeSelection) +{ + StrokeLine stroke; + + switch (strokeSelection) { + case Aliased|Solid|RegularDraw: + stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, NoDasher>; + break; + case Aliased|Solid|FastDraw: + stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, NoDasher>; + break; + case Aliased|Dashed|RegularDraw: + stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, Dasher>; + break; + case Aliased|Dashed|FastDraw: + stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, Dasher>; + break; + case AntiAliased|Solid|RegularDraw: + stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, NoDasher>; + break; + case AntiAliased|Solid|FastDraw: + stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, NoDasher>; + break; + case AntiAliased|Dashed|RegularDraw: + stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, Dasher>; + break; + case AntiAliased|Dashed|FastDraw: + stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, Dasher>; + break; + default: + Q_ASSERT(false); + stroke = 0; + } + return stroke; +} + +void QCosmeticStroker::setup() +{ + blend = state->penData.blend; + if (state->clip && state->clip->enabled && state->clip->hasRectClip && !state->clip->clipRect.isEmpty()) { + clip &= state->clip->clipRect; + blend = state->penData.unclipped_blend; + } + + int strokeSelection = 0; + if (blend == state->penData.unclipped_blend + && state->penData.type == QSpanData::Solid + && (state->penData.rasterBuffer->format == QImage::Format_ARGB32_Premultiplied + || state->penData.rasterBuffer->format == QImage::Format_RGB32) + && state->compositionMode() == QPainter::CompositionMode_SourceOver) + strokeSelection |= FastDraw; + + if (state->renderHints & QPainter::Antialiasing) + strokeSelection |= AntiAliased; + + const QVector<qreal> &penPattern = state->lastPen.dashPattern(); + if (penPattern.isEmpty()) { + Q_ASSERT(!pattern && !reversePattern); + pattern = 0; + reversePattern = 0; + patternLength = 0; + patternSize = 0; + } else { + pattern = (int *)malloc(penPattern.size()*sizeof(int)); + reversePattern = (int *)malloc(penPattern.size()*sizeof(int)); + patternSize = penPattern.size(); + + patternLength = 0; + for (int i = 0; i < patternSize; ++i) { + patternLength += (int) qMax(1. , penPattern.at(i)*64.); + pattern[i] = patternLength; + } + patternLength = 0; + for (int i = 0; i < patternSize; ++i) { + patternLength += (int) qMax(1., penPattern.at(patternSize - 1 - i)*64.); + reversePattern[i] = patternLength; + } + strokeSelection |= Dashed; +// qDebug() << "setup: size=" << patternSize << "length=" << patternLength/64.; + } + + stroke = strokeLine(strokeSelection); + + qreal width = state->lastPen.widthF(); + if (width == 0) + opacity = 256; + else if (state->lastPen.isCosmetic()) + opacity = (int) 256*width; + else + opacity = (int) 256*width*state->txscale; + opacity = qBound(0, opacity, 256); + + drawCaps = state->lastPen.capStyle() != Qt::FlatCap; + + if (strokeSelection & FastDraw) { + color = INTERPOLATE_PIXEL_256(state->penData.solid.color, opacity, 0, 0); + QRasterBuffer *buffer = state->penData.rasterBuffer; + pixels = (uint *)buffer->buffer(); + ppl = buffer->bytesPerLine()>>2; + } + + // setup FP clip bounds + xmin = clip.left() - 1; + xmax = clip.right() + 2; + ymin = clip.top() - 1; + ymax = clip.bottom() + 2; + + lastPixel.x = -1; +} + +// returns true if the whole line gets clipped away +bool QCosmeticStroker::clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2) +{ + // basic/rough clipping is done in floating point coordinates to avoid + // integer overflow problems. + if (x1 < xmin) { + if (x2 <= xmin) + goto clipped; + y1 += (y2 - y1)/(x2 - x1) * (xmin - x1); + x1 = xmin; + } else if (x1 > xmax) { + if (x2 >= xmax) + goto clipped; + y1 += (y2 - y1)/(x2 - x1) * (xmax - x1); + x1 = xmax; + } + if (x2 < xmin) { + lastPixel.x = -1; + y2 += (y2 - y1)/(x2 - x1) * (xmin - x2); + x2 = xmin; + } else if (x2 > xmax) { + lastPixel.x = -1; + y2 += (y2 - y1)/(x2 - x1) * (xmax - x2); + x2 = xmax; + } + + if (y1 < ymin) { + if (y2 <= ymin) + goto clipped; + x1 += (x2 - x1)/(y2 - y1) * (ymin - y1); + y1 = ymin; + } else if (y1 > ymax) { + if (y2 >= ymax) + goto clipped; + x1 += (x2 - x1)/(y2 - y1) * (ymax - y1); + y1 = ymax; + } + if (y2 < ymin) { + lastPixel.x = -1; + x2 += (x2 - x1)/(y2 - y1) * (ymin - y2); + y2 = ymin; + } else if (y2 > ymax) { + lastPixel.x = -1; + x2 += (x2 - x1)/(y2 - y1) * (ymax - y2); + y2 = ymax; + } + + return false; + + clipped: + lastPixel.x = -1; + return true; +} + + +void QCosmeticStroker::drawLine(const QPointF &p1, const QPointF &p2) +{ + QPointF start = p1 * state->matrix; + QPointF end = p2 * state->matrix; + + patternOffset = state->lastPen.dashOffset()*64; + lastPixel.x = -1; + + stroke(this, start.x(), start.y(), end.x(), end.y(), drawCaps ? CapBegin|CapEnd : 0); + + blend(current_span, spans, &state->penData); + current_span = 0; +} + +void QCosmeticStroker::drawPoints(const QPoint *points, int num) +{ + const QPoint *end = points + num; + while (points < end) { + QPointF p = QPointF(*points) * state->matrix; + drawPixel(this, qRound(p.x()), qRound(p.y()), 255); + ++points; + } + + blend(current_span, spans, &state->penData); + current_span = 0; +} + +void QCosmeticStroker::drawPoints(const QPointF *points, int num) +{ + const QPointF *end = points + num; + while (points < end) { + QPointF p = (*points) * state->matrix; + drawPixel(this, qRound(p.x()), qRound(p.y()), 255); + ++points; + } + + blend(current_span, spans, &state->penData); + current_span = 0; +} + +void QCosmeticStroker::calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2) +{ + // this is basically the same code as used in the aliased stroke method, + // but it only determines the direction and last point of a line + // + // This is being used to have proper dropout control for closed contours + // by calculating the direction and last pixel of the last segment in the contour. + // the info is then used to perform dropout control when drawing the first line segment + // of the contour + lastPixel.x = -1; + lastPixel.y = -1; + + if (clipLine(rx1, ry1, rx2, ry2)) + return; + + const int half = 32; + int x1 = toF26Dot6(rx1) + half; + int y1 = toF26Dot6(ry1) + half; + int x2 = toF26Dot6(rx2) + half; + int y2 = toF26Dot6(ry2) + half; + + int dx = qAbs(x2 - x1); + int dy = qAbs(y2 - y1); + + if (dx < dy) { + // vertical + bool swapped = false; + if (y1 > y2) { + swapped = true; + qSwap(y1, y2); + qSwap(x1, x2); + } + int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1); + int x = x1 << 10; + + int y = y1 >> 6; + int ys = y2 >> 6; + + if (y != ys) { + x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6; + + if (swapped) { + lastPixel.x = x >> 16; + lastPixel.y = y; + lastDir = QCosmeticStroker::BottomToTop; + } else { + lastPixel.x = (x + (ys - y - 1)*xinc) >> 16; + lastPixel.y = ys - 1; + lastDir = QCosmeticStroker::TopToBottom; + } + lastAxisAligned = qAbs(xinc) < (1 << 14); + } + } else { + // horizontal + if (!dx) + return; + + bool swapped = false; + if (x1 > x2) { + swapped = true; + qSwap(x1, x2); + qSwap(y1, y2); + } + int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1); + int y = y1 << 10; + + int x = x1 >> 6; + int xs = x2 >> 6; + + if (x != xs) { + y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6; + + if (swapped) { + lastPixel.x = x; + lastPixel.y = y >> 16; + lastDir = QCosmeticStroker::RightToLeft; + } else { + lastPixel.x = xs - 1; + lastPixel.y = (y + (xs - x - 1)*yinc) >> 16; + lastDir = QCosmeticStroker::LeftToRight; + } + lastAxisAligned = qAbs(yinc) < (1 << 14); + } + } +// qDebug() << " moveTo: setting last pixel to x/y dir" << lastPixel.x << lastPixel.y << lastDir; +} + +static inline const QPainterPath::ElementType *subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end, + const qreal *points, bool *closed) +{ + const QPainterPath::ElementType *start = t; + ++t; + + // find out if the subpath is closed + while (t < end) { + if (*t == QPainterPath::MoveToElement) + break; + ++t; + } + + int offset = t - start - 1; +// qDebug() << "subpath" << offset << points[0] << points[1] << points[2*offset] << points[2*offset+1]; + *closed = (points[0] == points[2*offset] && points[1] == points[2*offset + 1]); + + return t; +} + +void QCosmeticStroker::drawPath(const QVectorPath &path) +{ +// qDebug() << ">>>> drawpath" << path.convertToPainterPath() +// << "antialiasing:" << (bool)(state->renderHints & QPainter::Antialiasing) << " implicit close:" << path.hasImplicitClose(); + if (path.isEmpty()) + return; + + const qreal *points = path.points(); + const QPainterPath::ElementType *type = path.elements(); + + if (type) { + const QPainterPath::ElementType *end = type + path.elementCount(); + + while (type < end) { + Q_ASSERT(type == path.elements() || *type == QPainterPath::MoveToElement); + + QPointF p = QPointF(points[0], points[1]) * state->matrix; + QPointF movedTo = p; + patternOffset = state->lastPen.dashOffset()*64; + lastPixel.x = -1; + + bool closed; + const QPainterPath::ElementType *e = subPath(type, end, points, &closed); + if (closed) { + const qreal *p = points + 2*(e-type); + QPointF p1 = QPointF(p[-4], p[-3]) * state->matrix; + QPointF p2 = QPointF(p[-2], p[-1]) * state->matrix; + calculateLastPoint(p1.x(), p1.y(), p2.x(), p2.y()); + } + int caps = (!closed & drawCaps) ? CapBegin : NoCaps; +// qDebug() << "closed =" << closed << capString(caps); + + points += 2; + ++type; + + while (type < e) { + QPointF p2 = QPointF(points[0], points[1]) * state->matrix; + switch (*type) { + case QPainterPath::MoveToElement: + Q_ASSERT(!"Logic error"); + break; + + case QPainterPath::LineToElement: + if (!closed && drawCaps && type == e - 1) + caps |= CapEnd; + stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps); + p = p2; + points += 2; + ++type; + break; + + case QPainterPath::CurveToElement: { + if (!closed && drawCaps && type == e - 3) + caps |= CapEnd; + QPointF p3 = QPointF(points[2], points[3]) * state->matrix; + QPointF p4 = QPointF(points[4], points[5]) * state->matrix; + renderCubic(p, p2, p3, p4, caps); + p = p4; + type += 3; + points += 6; + break; + } + case QPainterPath::CurveToDataElement: + Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type"); + break; + } + caps = NoCaps; + } + } + } else { // !type, simple polygon + QPointF p = QPointF(points[0], points[1]) * state->matrix; + QPointF movedTo = p; + patternOffset = state->lastPen.dashOffset()*64; + lastPixel.x = -1; + + const qreal *end = points + 2*path.elementCount(); + // handle closed path case + bool closed = path.hasImplicitClose() || (points[0] == end[-2] && points[1] == end[-1]); + int caps = (!closed & drawCaps) ? CapBegin : NoCaps; + if (closed) { + QPointF p2 = QPointF(end[-2], end[-1]) * state->matrix; + calculateLastPoint(p2.x(), p2.y(), p.x(), p.y()); + } + + points += 2; + while (points < end) { + QPointF p2 = QPointF(points[0], points[1]) * state->matrix; + + if (!closed && drawCaps && points == end - 2) + caps |= CapEnd; + + stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps); + + p = p2; + points += 2; + caps = NoCaps; + } + if (path.hasImplicitClose()) + stroke(this, p.x(), p.y(), movedTo.x(), movedTo.y(), NoCaps); + } + + + blend(current_span, spans, &state->penData); + current_span = 0; +} + +void QCosmeticStroker::renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps) +{ +// qDebug() << ">>>> renderCubic" << p1 << p2 << p3 << p4 << capString(caps); + const int maxSubDivisions = 6; + PointF points[3*maxSubDivisions + 4]; + + points[3].x = p1.x(); + points[3].y = p1.y(); + points[2].x = p2.x(); + points[2].y = p2.y(); + points[1].x = p3.x(); + points[1].y = p3.y(); + points[0].x = p4.x(); + points[0].y = p4.y(); + + PointF *p = points; + int level = maxSubDivisions; + + renderCubicSubdivision(p, level, caps); +} + +static void splitCubic(QCosmeticStroker::PointF *points) +{ + const qreal half = .5; + qreal a, b, c, d; + + points[6].x = points[3].x; + c = points[1].x; + d = points[2].x; + points[1].x = a = ( points[0].x + c ) * half; + points[5].x = b = ( points[3].x + d ) * half; + c = ( c + d ) * half; + points[2].x = a = ( a + c ) * half; + points[4].x = b = ( b + c ) * half; + points[3].x = ( a + b ) * half; + + points[6].y = points[3].y; + c = points[1].y; + d = points[2].y; + points[1].y = a = ( points[0].y + c ) * half; + points[5].y = b = ( points[3].y + d ) * half; + c = ( c + d ) * half; + points[2].y = a = ( a + c ) * half; + points[4].y = b = ( b + c ) * half; + points[3].y = ( a + b ) * half; +} + +void QCosmeticStroker::renderCubicSubdivision(QCosmeticStroker::PointF *points, int level, int caps) +{ + if (level) { + qreal dx = points[3].x - points[0].x; + qreal dy = points[3].y - points[0].y; + qreal len = ((qreal).25) * (qAbs(dx) + qAbs(dy)); + + if (qAbs(dx * (points[0].y - points[2].y) - dy * (points[0].x - points[2].x)) >= len || + qAbs(dx * (points[0].y - points[1].y) - dy * (points[0].x - points[1].x)) >= len) { + splitCubic(points); + + --level; + renderCubicSubdivision(points + 3, level, caps & CapBegin); + renderCubicSubdivision(points, level, caps & CapEnd); + return; + } + } + + stroke(this, points[3].x, points[3].y, points[0].x, points[0].y, caps); +} + +static inline int swapCaps(int caps) +{ + return ((caps & QCosmeticStroker::CapBegin) << 1) | + ((caps & QCosmeticStroker::CapEnd) >> 1); +} + +// adjust line by half a pixel +static inline void capAdjust(int caps, int &x1, int &x2, int &y, int yinc) +{ + if (caps & QCosmeticStroker::CapBegin) { + x1 -= 32; + y -= yinc >> 1; + } + if (caps & QCosmeticStroker::CapEnd) { + x2 += 32; + } +} + +/* + The hard part about this is dropout control and avoiding douple drawing of points when + the drawing shifts from horizontal to vertical or back. + */ +template<DrawPixel drawPixel, class Dasher> +static void drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps) +{ + if (stroker->clipLine(rx1, ry1, rx2, ry2)) + return; + + static const int half = 32; + int x1 = toF26Dot6(rx1) + half; + int y1 = toF26Dot6(ry1) + half; + int x2 = toF26Dot6(rx2) + half; + int y2 = toF26Dot6(ry2) + half; + + int dx = qAbs(x2 - x1); + int dy = qAbs(y2 - y1); + + QCosmeticStroker::Point last = stroker->lastPixel; + +// qDebug() << "stroke" << x1/64. << y1/64. << x2/64. << y2/64.; + + if (dx < dy) { + // vertical + QCosmeticStroker::Direction dir = QCosmeticStroker::TopToBottom; + + bool swapped = false; + if (y1 > y2) { + swapped = true; + qSwap(y1, y2); + qSwap(x1, x2); + caps = swapCaps(caps); + dir = QCosmeticStroker::BottomToTop; + } + int xinc = F16Dot16FixedDiv(x2 - x1, y2 - y1); + int x = x1 << 10; + + if ((stroker->lastDir ^ QCosmeticStroker::VerticalMask) == dir) + caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin; + + capAdjust(caps, y1, y2, x, xinc); + + int y = y1 >> 6; + int ys = y2 >> 6; + + if (y != ys) { + x += ( ((((y << 6) + 32 - y1))) * xinc ) >> 6; + + // calculate first and last pixel and perform dropout control + QCosmeticStroker::Point first; + first.x = x >> 16; + first.y = y; + last.x = (x + (ys - y - 1)*xinc) >> 16; + last.y = ys - 1; + if (swapped) + qSwap(first, last); + + bool axisAligned = qAbs(xinc) < (1 << 14); + if (stroker->lastPixel.x >= 0) { + if (first.x == stroker->lastPixel.x && + first.y == stroker->lastPixel.y) { + // remove duplicated pixel + if (swapped) { + --ys; + } else { + ++y; + x += xinc; + } + } else if (stroker->lastDir != dir && + (((axisAligned && stroker->lastAxisAligned) && + stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) || + (qAbs(stroker->lastPixel.x - first.x) > 1 || + qAbs(stroker->lastPixel.y - first.y) > 1))) { + // have a missing pixel, insert it + if (swapped) { + ++ys; + } else { + --y; + x -= xinc; + } + } + } + stroker->lastDir = dir; + stroker->lastAxisAligned = axisAligned; + + Dasher dasher(stroker, swapped, y << 6, ys << 6); + + do { + if (dasher.on()) + drawPixel(stroker, x >> 16, y, 255); + dasher.adjust(); + x += xinc; + } while (++y < ys); + } + } else { + // horizontal + if (!dx) + return; + + QCosmeticStroker::Direction dir = QCosmeticStroker::LeftToRight; + + bool swapped = false; + if (x1 > x2) { + swapped = true; + qSwap(x1, x2); + qSwap(y1, y2); + caps = swapCaps(caps); + dir = QCosmeticStroker::RightToLeft; + } + int yinc = F16Dot16FixedDiv(y2 - y1, x2 - x1); + int y = y1 << 10; + + if ((stroker->lastDir ^ QCosmeticStroker::HorizontalMask) == dir) + caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin; + + capAdjust(caps, x1, x2, y, yinc); + + int x = x1 >> 6; + int xs = x2 >> 6; + + if (x != xs) { + y += ( ((((x << 6) + 32 - x1))) * yinc ) >> 6; + + // calculate first and last pixel to perform dropout control + QCosmeticStroker::Point first; + first.x = x; + first.y = y >> 16; + last.x = xs - 1; + last.y = (y + (xs - x - 1)*yinc) >> 16; + if (swapped) + qSwap(first, last); + + bool axisAligned = qAbs(yinc) < (1 << 14); + if (stroker->lastPixel.x >= 0) { + if (first.x == stroker->lastPixel.x && first.y == stroker->lastPixel.y) { + // remove duplicated pixel + if (swapped) { + --xs; + } else { + ++x; + y += yinc; + } + } else if (stroker->lastDir != dir && + (((axisAligned && stroker->lastAxisAligned) && + stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) || + (qAbs(stroker->lastPixel.x - first.x) > 1 || + qAbs(stroker->lastPixel.y - first.y) > 1))) { + // have a missing pixel, insert it + if (swapped) { + ++xs; + } else { + --x; + y -= yinc; + } + } + } + stroker->lastDir = dir; + stroker->lastAxisAligned = axisAligned; + + Dasher dasher(stroker, swapped, x << 6, xs << 6); + + do { + if (dasher.on()) + drawPixel(stroker, x, y >> 16, 255); + dasher.adjust(); + y += yinc; + } while (++x < xs); + } + } + stroker->lastPixel = last; +} + + +template<DrawPixel drawPixel, class Dasher> +static void drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps) +{ + if (stroker->clipLine(rx1, ry1, rx2, ry2)) + return; + + int x1 = toF26Dot6(rx1); + int y1 = toF26Dot6(ry1); + int x2 = toF26Dot6(rx2); + int y2 = toF26Dot6(ry2); + + int dx = x2 - x1; + int dy = y2 - y1; + + if (qAbs(dx) < qAbs(dy)) { + // vertical + + int xinc = F16Dot16FixedDiv(dx, dy); + + bool swapped = false; + if (y1 > y2) { + qSwap(y1, y2); + qSwap(x1, x2); + swapped = true; + caps = swapCaps(caps); + } + + int x = (x1 - 32) << 10; + x -= ( ((y1 & 63) - 32) * xinc ) >> 6; + + capAdjust(caps, y1, y2, x, xinc); + + Dasher dasher(stroker, swapped, y1, y2); + + int y = y1 >> 6; + int ys = y2 >> 6; + + int alphaStart, alphaEnd; + if (y == ys) { + alphaStart = y2 - y1; + Q_ASSERT(alphaStart >= 0 && alphaStart < 64); + alphaEnd = 0; + } else { + alphaStart = 64 - (y1 & 63); + alphaEnd = (y2 & 63); + } +// qDebug() << "vertical" << x1/64. << y1/64. << x2/64. << y2/64.; +// qDebug() << " x=" << x << "dx=" << dx << "xi=" << (x>>16) << "xsi=" << ((x+(ys-y)*dx)>>16) << "y=" << y << "ys=" << ys; + + // draw first pixel + if (dasher.on()) { + uint alpha = (quint8)(x >> 8); + drawPixel(stroker, x>>16, y, (255-alpha) * alphaStart >> 6); + drawPixel(stroker, (x>>16) + 1, y, alpha * alphaStart >> 6); + } + dasher.adjust(); + x += xinc; + ++y; + if (y < ys) { + do { + if (dasher.on()) { + uint alpha = (quint8)(x >> 8); + drawPixel(stroker, x>>16, y, (255-alpha)); + drawPixel(stroker, (x>>16) + 1, y, alpha); + } + dasher.adjust(); + x += xinc; + } while (++y < ys); + } + // draw last pixel + if (alphaEnd && dasher.on()) { + uint alpha = (quint8)(x >> 8); + drawPixel(stroker, x>>16, y, (255-alpha) * alphaEnd >> 6); + drawPixel(stroker, (x>>16) + 1, y, alpha * alphaEnd >> 6); + } + } else { + // horizontal + if (!dx) + return; + + int yinc = F16Dot16FixedDiv(dy, dx); + + bool swapped = false; + if (x1 > x2) { + qSwap(x1, x2); + qSwap(y1, y2); + swapped = true; + caps = swapCaps(caps); + } + + int y = (y1 - 32) << 10; + y -= ( ((x1 & 63) - 32) * yinc ) >> 6; + + capAdjust(caps, x1, x2, y, yinc); + + Dasher dasher(stroker, swapped, x1, x2); + + int x = x1 >> 6; + int xs = x2 >> 6; + +// qDebug() << "horizontal" << x1/64. << y1/64. << x2/64. << y2/64.; +// qDebug() << " y=" << y << "dy=" << dy << "x=" << x << "xs=" << xs << "yi=" << (y>>16) << "ysi=" << ((y+(xs-x)*dy)>>16); + int alphaStart, alphaEnd; + if (x == xs) { + alphaStart = x2 - x1; + Q_ASSERT(alphaStart >= 0 && alphaStart < 64); + alphaEnd = 0; + } else { + alphaStart = 64 - (x1 & 63); + alphaEnd = (x2 & 63); + } + + // draw first pixel + if (dasher.on()) { + uint alpha = (quint8)(y >> 8); + drawPixel(stroker, x, y>>16, (255-alpha) * alphaStart >> 6); + drawPixel(stroker, x, (y>>16) + 1, alpha * alphaStart >> 6); + } + dasher.adjust(); + y += yinc; + ++x; + // draw line + if (x < xs) { + do { + if (dasher.on()) { + uint alpha = (quint8)(y >> 8); + drawPixel(stroker, x, y>>16, (255-alpha)); + drawPixel(stroker, x, (y>>16) + 1, alpha); + } + dasher.adjust(); + y += yinc; + } while (++x < xs); + } + // draw last pixel + if (alphaEnd && dasher.on()) { + uint alpha = (quint8)(y >> 8); + drawPixel(stroker, x, y>>16, (255-alpha) * alphaEnd >> 6); + drawPixel(stroker, x, (y>>16) + 1, alpha * alphaEnd >> 6); + } + } +} + +QT_END_NAMESPACE diff --git a/src/gui/painting/qcosmeticstroker_p.h b/src/gui/painting/qcosmeticstroker_p.h new file mode 100644 index 0000000..53cdf2c --- /dev/null +++ b/src/gui/painting/qcosmeticstroker_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOSMETICSTROKER_P_H +#define QCOSMETICSTROKER_P_H + +#include <private/qdrawhelper_p.h> +#include <private/qvectorpath_p.h> +#include <private/qpaintengine_raster_p.h> +#include <qpen.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QCosmeticStroker; + + +typedef void (*StrokeLine)(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps); + +class QCosmeticStroker +{ +public: + struct Point { + int x; + int y; + }; + struct PointF { + qreal x; + qreal y; + }; + + enum Caps { + NoCaps = 0, + CapBegin = 0x1, + CapEnd = 0x2, + }; + + // used to avoid drop outs or duplicated points + enum Direction { + TopToBottom = 0x1, + BottomToTop = 0x2, + LeftToRight = 0x4, + RightToLeft = 0x8, + VerticalMask = 0x3, + HorizontalMask = 0xc + }; + + QCosmeticStroker(QRasterPaintEngineState *s, const QRect &dr) + : state(s), + clip(dr), + pattern(0), + reversePattern(0), + patternSize(0), + patternLength(0), + patternOffset(0), + current_span(0), + lastDir(LeftToRight), + lastAxisAligned(false) + { setup(); } + ~QCosmeticStroker() { free(pattern); free(reversePattern); } + void drawLine(const QPointF &p1, const QPointF &p2); + void drawPath(const QVectorPath &path); + void drawPoints(const QPoint *points, int num); + void drawPoints(const QPointF *points, int num); + + + QRasterPaintEngineState *state; + QRect clip; + // clip bounds in real + qreal xmin, xmax; + qreal ymin, ymax; + + StrokeLine stroke; + bool drawCaps; + + int *pattern; + int *reversePattern; + int patternSize; + int patternLength; + int patternOffset; + + enum { NSPANS = 255 }; + QT_FT_Span spans[NSPANS]; + int current_span; + ProcessSpans blend; + + int opacity; + + uint color; + uint *pixels; + int ppl; + + Direction lastDir; + Point lastPixel; + bool lastAxisAligned; + +private: + void setup(); + + void renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps); + void renderCubicSubdivision(PointF *points, int level, int caps); + // used for closed subpaths + void calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2); + +public: + bool clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QCOSMETICLINE_H diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index ac9a519..c97ef24 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -75,43 +75,9 @@ enum { fixed_scale = 1 << 16, half_point = 1 << 15 }; -static const int buffer_size = 2048; - -struct LinearGradientValues -{ - qreal dx; - qreal dy; - qreal l; - qreal off; -}; - -struct RadialGradientValues -{ - qreal dx; - qreal dy; - qreal a; -}; - -struct Operator; -typedef uint* (QT_FASTCALL *DestFetchProc)(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length); -typedef void (QT_FASTCALL *DestStoreProc)(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length); -typedef const uint* (QT_FASTCALL *SourceFetchProc)(uint *buffer, const Operator *o, const QSpanData *data, int y, int x, int length); - -struct Operator -{ - QPainter::CompositionMode mode; - DestFetchProc dest_fetch; - DestStoreProc dest_store; - SourceFetchProc src_fetch; - CompositionFunctionSolid funcSolid; - CompositionFunction func; - union { - LinearGradientValues linear; - RadialGradientValues radial; -// TextureValues texture; - }; -}; +// must be multiple of 4 for easier SIMD implementations +static const int buffer_size = 2048; /* Destination fetch. This is simple as we don't have to do bounds checks or @@ -570,8 +536,8 @@ const uint * QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const int image_width = data->texture.width; int image_height = data->texture.height; - const qreal cx = x + 0.5; - const qreal cy = y + 0.5; + const qreal cx = x + qreal(0.5); + const qreal cy = y + qreal(0.5); const uint *end = buffer + length; uint *b = buffer; @@ -792,8 +758,8 @@ const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator * int image_x2 = data->texture.x2 - 1; int image_y2 = data->texture.y2 - 1; - const qreal cx = x + 0.5; - const qreal cy = y + 0.5; + const qreal cx = x + qreal(0.5); + const qreal cy = y + qreal(0.5); uint *end = buffer + length; uint *b = buffer; @@ -1188,8 +1154,8 @@ const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator * while (b < end) { const qreal iw = fw == 0 ? 1 : 1 / fw; - const qreal px = fx * iw - 0.5; - const qreal py = fy * iw - 0.5; + const qreal px = fx * iw - qreal(0.5); + const qreal py = fy * iw - qreal(0.5); int x1 = int(px) - (px < 0); int x2; @@ -1346,64 +1312,13 @@ static const SourceFetchProc sourceFetch[NBlendTypes][QImage::NImageFormats] = { }, }; - -static inline uint qt_gradient_pixel(const QGradientData *data, qreal pos) -{ - int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + 0.5); - - // calculate the actual offset. - if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) { - if (data->spread == QGradient::RepeatSpread) { - ipos = ipos % GRADIENT_STOPTABLE_SIZE; - ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos; - - } else if (data->spread == QGradient::ReflectSpread) { - const int limit = GRADIENT_STOPTABLE_SIZE * 2 - 1; - ipos = ipos % limit; - ipos = ipos < 0 ? limit + ipos : ipos; - ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - ipos : ipos; - - } else { - if (ipos < 0) ipos = 0; - else if (ipos >= GRADIENT_STOPTABLE_SIZE) ipos = GRADIENT_STOPTABLE_SIZE-1; - } - } - - Q_ASSERT(ipos >= 0); - Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE); - - return data->colorTable[ipos]; -} - #define FIXPT_BITS 8 #define FIXPT_SIZE (1<<FIXPT_BITS) static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos) { int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS; - - // calculate the actual offset. - if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) { - if (data->spread == QGradient::RepeatSpread) { - ipos = ipos % GRADIENT_STOPTABLE_SIZE; - ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos; - - } else if (data->spread == QGradient::ReflectSpread) { - const int limit = GRADIENT_STOPTABLE_SIZE * 2 - 1; - ipos = ipos % limit; - ipos = ipos < 0 ? limit + ipos : ipos; - ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - ipos : ipos; - - } else { - if (ipos < 0) ipos = 0; - else if (ipos >= GRADIENT_STOPTABLE_SIZE) ipos = GRADIENT_STOPTABLE_SIZE-1; - } - } - - Q_ASSERT(ipos >= 0); - Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE); - - return data->colorTable[ipos]; + return data->colorTable[qt_gradient_clamp(data, ipos)]; } static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const QSpanData *data) @@ -1419,8 +1334,8 @@ static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const Q } } -static const uint * QT_FASTCALL fetchLinearGradient(uint *buffer, const Operator *op, const QSpanData *data, - int y, int x, int length) +static const uint * QT_FASTCALL qt_fetch_linear_gradient(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length) { const uint *b = buffer; qreal t, inc; @@ -1430,8 +1345,8 @@ static const uint * QT_FASTCALL fetchLinearGradient(uint *buffer, const Operator if (op->linear.l == 0) { t = inc = 0; } else { - rx = data->m21 * (y + 0.5) + data->m11 * (x + 0.5) + data->dx; - ry = data->m22 * (y + 0.5) + data->m12 * (x + 0.5) + data->dy; + rx = data->m21 * (y + qreal(0.5)) + data->m11 * (x + qreal(0.5)) + data->dx; + ry = data->m22 * (y + qreal(0.5)) + data->m12 * (x + qreal(0.5)) + data->dy; t = op->linear.dx*rx + op->linear.dy*ry + op->linear.off; inc = op->linear.dx * data->m11 + op->linear.dy * data->m12; affine = !data->m13 && !data->m23; @@ -1444,7 +1359,7 @@ static const uint * QT_FASTCALL fetchLinearGradient(uint *buffer, const Operator const uint *end = buffer + length; if (affine) { - if (inc > -1e-5 && inc < 1e-5) { + if (inc > qreal(-1e-5) && inc < qreal(1e-5)) { QT_MEMFILL_UINT(buffer, length, qt_gradient_pixel_fixed(&data->gradient, int(t * FIXPT_SIZE))); } else { if (t+inc*length < qreal(INT_MAX >> (FIXPT_BITS + 1)) && @@ -1467,7 +1382,7 @@ static const uint * QT_FASTCALL fetchLinearGradient(uint *buffer, const Operator } } } else { // fall back to float math here as well - qreal rw = data->m23 * (y + 0.5) + data->m13 * (x + 0.5) + data->m33; + qreal rw = data->m23 * (y + qreal(0.5)) + data->m13 * (x + qreal(0.5)) + data->m33; while (buffer < end) { qreal x = rx/rw; qreal y = ry/rw; @@ -1487,116 +1402,71 @@ static const uint * QT_FASTCALL fetchLinearGradient(uint *buffer, const Operator return b; } -static inline qreal determinant(qreal a, qreal b, qreal c) -{ - return (b * b) - (4 * a * c); -} - -// function to evaluate real roots -static inline qreal realRoots(qreal a, qreal b, qreal detSqrt) -{ - return (-b + detSqrt)/(2 * a); -} - -static inline qreal qSafeSqrt(qreal x) -{ - return x > 0 ? qSqrt(x) : 0; -} - static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data) { v->dx = data->gradient.radial.center.x - data->gradient.radial.focal.x; v->dy = data->gradient.radial.center.y - data->gradient.radial.focal.y; - v->a = data->gradient.radial.radius*data->gradient.radial.radius - v->dx*v->dx - v->dy*v->dy; -} - -static const uint * QT_FASTCALL fetchRadialGradient(uint *buffer, const Operator *op, const QSpanData *data, - int y, int x, int length) -{ - const uint *b = buffer; - qreal rx = data->m21 * (y + 0.5) - + data->dx + data->m11 * (x + 0.5); - qreal ry = data->m22 * (y + 0.5) - + data->dy + data->m12 * (x + 0.5); - bool affine = !data->m13 && !data->m23; - //qreal r = data->gradient.radial.radius; - - const uint *end = buffer + length; - if (affine) { - rx -= data->gradient.radial.focal.x; - ry -= data->gradient.radial.focal.y; - - qreal inv_a = 1 / qreal(2 * op->radial.a); - - const qreal delta_rx = data->m11; - const qreal delta_ry = data->m12; - qreal b = 2*(rx * op->radial.dx + ry * op->radial.dy); - qreal delta_b = 2*(delta_rx * op->radial.dx + delta_ry * op->radial.dy); - const qreal b_delta_b = 2 * b * delta_b; - const qreal delta_b_delta_b = 2 * delta_b * delta_b; + v->dr = data->gradient.radial.center.radius - data->gradient.radial.focal.radius; + v->sqrfr = data->gradient.radial.focal.radius * data->gradient.radial.focal.radius; - const qreal bb = b * b; - const qreal delta_bb = delta_b * delta_b; + v->a = v->dr * v->dr - v->dx*v->dx - v->dy*v->dy; + v->inv2a = 1 / (2 * v->a); - b *= inv_a; - delta_b *= inv_a; - - const qreal rxrxryry = rx * rx + ry * ry; - const qreal delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry; - const qreal rx_plus_ry = 2*(rx * delta_rx + ry * delta_ry); - const qreal delta_rx_plus_ry = 2 * delta_rxrxryry; - - inv_a *= inv_a; - - qreal det = (bb + 4 * op->radial.a * rxrxryry) * inv_a; - qreal delta_det = (b_delta_b + delta_bb + 4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) * inv_a; - const qreal delta_delta_det = (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a; + v->extended = !qFuzzyIsNull(data->gradient.radial.focal.radius) || v->a <= 0; +} - while (buffer < end) { - *buffer = qt_gradient_pixel(&data->gradient, qSafeSqrt(det) - b); +class RadialFetchPlain +{ +public: + static inline void fetch(uint *buffer, uint *end, const Operator *op, const QSpanData *data, qreal det, + qreal delta_det, qreal delta_delta_det, qreal b, qreal delta_b) + { + if (op->radial.extended) { + while (buffer < end) { + quint32 result = 0; + if (det >= 0) { + qreal w = qSqrt(det) - b; + if (data->gradient.radial.focal.radius + op->radial.dr * w >= 0) + result = qt_gradient_pixel(&data->gradient, w); + } - det += delta_det; - delta_det += delta_delta_det; - b += delta_b; + *buffer = result; - ++buffer; - } - } else { - qreal rw = data->m23 * (y + 0.5) - + data->m33 + data->m13 * (x + 0.5); - if (!rw) - rw = 1; - while (buffer < end) { - qreal gx = rx/rw - data->gradient.radial.focal.x; - qreal gy = ry/rw - data->gradient.radial.focal.y; - qreal b = 2*(gx*op->radial.dx + gy*op->radial.dy); - qreal det = determinant(op->radial.a, b , -(gx*gx + gy*gy)); - qreal s = realRoots(op->radial.a, b, qSafeSqrt(det)); + det += delta_det; + delta_det += delta_delta_det; + b += delta_b; - *buffer = qt_gradient_pixel(&data->gradient, s); + ++buffer; + } + } else { + while (buffer < end) { + *buffer++ = qt_gradient_pixel(&data->gradient, qSqrt(det) - b); - rx += data->m11; - ry += data->m12; - rw += data->m13; - if (!rw) { - rw += data->m13; + det += delta_det; + delta_det += delta_delta_det; + b += delta_b; } - ++buffer; } } +}; - return b; +const uint * QT_FASTCALL qt_fetch_radial_gradient_plain(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length) +{ + return qt_fetch_radial_gradient_template<RadialFetchPlain>(buffer, op, data, y, x, length); } -static const uint * QT_FASTCALL fetchConicalGradient(uint *buffer, const Operator *, const QSpanData *data, - int y, int x, int length) +static SourceFetchProc qt_fetch_radial_gradient = qt_fetch_radial_gradient_plain; + +static const uint * QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Operator *, const QSpanData *data, + int y, int x, int length) { const uint *b = buffer; - qreal rx = data->m21 * (y + 0.5) - + data->dx + data->m11 * (x + 0.5); - qreal ry = data->m22 * (y + 0.5) - + data->dy + data->m12 * (x + 0.5); + qreal rx = data->m21 * (y + qreal(0.5)) + + data->dx + data->m11 * (x + qreal(0.5)); + qreal ry = data->m22 * (y + qreal(0.5)) + + data->dy + data->m12 * (x + qreal(0.5)); bool affine = !data->m13 && !data->m23; const uint *end = buffer + length; @@ -1613,8 +1483,8 @@ static const uint * QT_FASTCALL fetchConicalGradient(uint *buffer, const Operato ++buffer; } } else { - qreal rw = data->m23 * (y + 0.5) - + data->m33 + data->m13 * (x + 0.5); + qreal rw = data->m23 * (y + qreal(0.5)) + + data->m33 + data->m13 * (x + qreal(0.5)); if (!rw) rw = 1; while (buffer < end) { @@ -2819,7 +2689,7 @@ static inline int soft_light_op(int dst, int src, int da, int sa) # ifdef Q_CC_RVCT // needed to avoid compiler crash in RVCT 2.2 return (dst * sa * 255 + da * (src2 - sa) * (qIntSqrtInt(dst_np * 255) - dst_np) + temp) / 65025; # else - return (dst * sa * 255 + da * (src2 - sa) * (int(sqrt(qreal(dst_np * 255))) - dst_np) + temp) / 65025; + return (dst * sa * 255 + da * (src2 - sa) * (int(qSqrt(qreal(dst_np * 255))) - dst_np) + temp) / 65025; # endif } } @@ -3347,16 +3217,16 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in case QSpanData::LinearGradient: solidSource = !data->gradient.alphaColor; getLinearGradientValues(&op.linear, data); - op.src_fetch = fetchLinearGradient; + op.src_fetch = qt_fetch_linear_gradient; break; case QSpanData::RadialGradient: solidSource = !data->gradient.alphaColor; getRadialGradientValues(&op.radial, data); - op.src_fetch = fetchRadialGradient; + op.src_fetch = qt_fetch_radial_gradient; break; case QSpanData::ConicalGradient: solidSource = !data->gradient.alphaColor; - op.src_fetch = fetchConicalGradient; + op.src_fetch = qt_fetch_conical_gradient; break; case QSpanData::Texture: op.src_fetch = sourceFetch[getBlendType(data)][data->texture.format]; @@ -5928,8 +5798,8 @@ Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_argb(int count, const QSpan *s uint *target = ((uint *)t) + spans->x; uint *image_bits = (uint *)data->texture.imageData; - const qreal cx = spans->x + 0.5; - const qreal cy = spans->y + 0.5; + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); int x = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale); @@ -5976,8 +5846,8 @@ Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_argb(int count, const QSpan *s uint *target = ((uint *)t) + spans->x; uint *image_bits = (uint *)data->texture.imageData; - const qreal cx = spans->x + 0.5; - const qreal cy = spans->y + 0.5; + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); qreal x = data->m21 * cy + data->m11 * cx + data->dx; qreal y = data->m22 * cy + data->m12 * cx + data->dy; @@ -6328,8 +6198,8 @@ Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_tiled_argb(int count, const QS uint *target = ((uint *)t) + spans->x; uint *image_bits = (uint *)data->texture.imageData; - const qreal cx = spans->x + 0.5; - const qreal cy = spans->y + 0.5; + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); int x = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale); @@ -6380,8 +6250,8 @@ Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_tiled_argb(int count, const QS uint *target = ((uint *)t) + spans->x; uint *image_bits = (uint *)data->texture.imageData; - const qreal cx = spans->x + 0.5; - const qreal cy = spans->y + 0.5; + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); qreal x = data->m21 * cy + data->m11 * cx + data->dx; qreal y = data->m22 * cy + data->m12 * cx + data->dy; @@ -7067,7 +6937,7 @@ static void qt_gradient_quint32(int count, const QSpan *spans, void *userData) */ const int gss = GRADIENT_STOPTABLE_SIZE - 1; int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE); - int off = int((((linear.dy * (data->m22 * 0.5 + data->dy) + linear.off) * gss) * FIXPT_SIZE)); + int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE)); while (count--) { int y = spans->y; @@ -7115,7 +6985,7 @@ static void qt_gradient_quint16(int count, const QSpan *spans, void *userData) */ const int gss = GRADIENT_STOPTABLE_SIZE - 1; int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE); - int off = int((((linear.dy * (data->m22 * 0.5 + data->dy) + linear.off) * gss) * FIXPT_SIZE)); + int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE)); uint oldColor = data->solid.color; while (count--) { @@ -7194,18 +7064,12 @@ void qt_build_pow_tables() { #ifdef Q_WS_MAC // decided by testing a few things on an iMac, should probably get this from the // system... - smoothing = 2.0; + smoothing = qreal(2.0); #endif #ifdef Q_WS_WIN - int winSmooth; - if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0)) - smoothing = winSmooth / 1000.0; - - // Safeguard ourselves against corrupt registry values... - if (smoothing > 5 || smoothing < 1) - smoothing = 1.4; - + extern qreal qt_fontsmoothing_gamma; // qapplication_win.cpp + smoothing = qt_fontsmoothing_gamma; #endif #ifdef Q_WS_X11 @@ -7226,7 +7090,7 @@ void qt_build_pow_tables() { for (int i=0; i<256; ++i) qt_pow_gamma[i] = uint(qRound(qPow(i / qreal(255.), gray_gamma) * 2047)); for (int i=0; i<2048; ++i) - qt_pow_invgamma[i] = uchar(qRound(qPow(i / 2047.0, 1 / gray_gamma) * 255)); + qt_pow_invgamma[i] = uchar(qRound(qPow(i / qreal(2047.0), 1 / gray_gamma) * 255)); #endif } @@ -7888,6 +7752,11 @@ void qInitDrawhelperAsm() qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2; qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; + + extern const uint * QT_FASTCALL qt_fetch_radial_gradient_sse2(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length); + + qt_fetch_radial_gradient = qt_fetch_radial_gradient_sse2; } #ifdef QT_HAVE_SSSE3 @@ -7983,6 +7852,11 @@ void qInitDrawhelperAsm() qMemRotateFunctions[QImage::Format_RGB16][0] = qt_memrotate90_16_neon; qMemRotateFunctions[QImage::Format_RGB16][2] = qt_memrotate270_16_neon; qt_memfill32 = qt_memfill32_neon; + + extern const uint * QT_FASTCALL qt_fetch_radial_gradient_neon(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length); + + qt_fetch_radial_gradient = qt_fetch_radial_gradient_neon; } #endif diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp index e97b9c6..a7e510e 100644 --- a/src/gui/painting/qdrawhelper_neon.cpp +++ b/src/gui/painting/qdrawhelper_neon.cpp @@ -955,6 +955,46 @@ void qt_memrotate270_16_neon(const uchar *srcPixels, int w, int h, } } +class QSimdNeon +{ +public: + typedef int32x4_t Int32x4; + typedef float32x4_t Float32x4; + + union Vect_buffer_i { Int32x4 v; int i[4]; }; + union Vect_buffer_f { Float32x4 v; float f[4]; }; + + static inline Float32x4 v_dup(float x) { return vdupq_n_f32(x); } + static inline Int32x4 v_dup(int x) { return vdupq_n_s32(x); } + static inline Int32x4 v_dup(uint x) { return vdupq_n_s32(x); } + + static inline Float32x4 v_add(Float32x4 a, Float32x4 b) { return vaddq_f32(a, b); } + static inline Int32x4 v_add(Int32x4 a, Int32x4 b) { return vaddq_s32(a, b); } + + static inline Float32x4 v_max(Float32x4 a, Float32x4 b) { return vmaxq_f32(a, b); } + static inline Float32x4 v_min(Float32x4 a, Float32x4 b) { return vminq_f32(a, b); } + static inline Int32x4 v_min_16(Int32x4 a, Int32x4 b) { return vminq_s32(a, b); } + + static inline Int32x4 v_and(Int32x4 a, Int32x4 b) { return vandq_s32(a, b); } + + static inline Float32x4 v_sub(Float32x4 a, Float32x4 b) { return vsubq_f32(a, b); } + static inline Int32x4 v_sub(Int32x4 a, Int32x4 b) { return vsubq_s32(a, b); } + + static inline Float32x4 v_mul(Float32x4 a, Float32x4 b) { return vmulq_f32(a, b); } + + static inline Float32x4 v_sqrt(Float32x4 x) { Float32x4 y = vrsqrteq_f32(x); y = vmulq_f32(y, vrsqrtsq_f32(x, vmulq_f32(y, y))); return vmulq_f32(x, y); } + + static inline Int32x4 v_toInt(Float32x4 x) { return vcvtq_s32_f32(x); } + + static inline Int32x4 v_greaterOrEqual(Float32x4 a, Float32x4 b) { return vreinterpretq_s32_u32(vcgeq_f32(a, b)); } +}; + +const uint * QT_FASTCALL qt_fetch_radial_gradient_neon(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length) +{ + return qt_fetch_radial_gradient_template<QRadialFetchSimd<QSimdNeon> >(buffer, op, data, y, x, length); +} + QT_END_NAMESPACE #endif // QT_HAVE_NEON diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index 492eb0d..0766f2e 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -63,6 +63,7 @@ #endif #include "private/qrasterdefs_p.h" #include <private/qsimd_p.h> +#include <private/qmath_p.h> #ifdef Q_WS_QWS #include "QtGui/qscreen_qws.h" @@ -178,6 +179,44 @@ void qBlendTextureCallback(int count, const QSpan *spans, void *userData); typedef void (QT_FASTCALL *CompositionFunction)(uint *dest, const uint *src, int length, uint const_alpha); typedef void (QT_FASTCALL *CompositionFunctionSolid)(uint *dest, int length, uint color, uint const_alpha); +struct LinearGradientValues +{ + qreal dx; + qreal dy; + qreal l; + qreal off; +}; + +struct RadialGradientValues +{ + qreal dx; + qreal dy; + qreal dr; + qreal sqrfr; + qreal a; + qreal inv2a; + bool extended; +}; + +struct Operator; +typedef uint* (QT_FASTCALL *DestFetchProc)(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length); +typedef void (QT_FASTCALL *DestStoreProc)(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length); +typedef const uint* (QT_FASTCALL *SourceFetchProc)(uint *buffer, const Operator *o, const QSpanData *data, int y, int x, int length); + +struct Operator +{ + QPainter::CompositionMode mode; + DestFetchProc dest_fetch; + DestStoreProc dest_store; + SourceFetchProc src_fetch; + CompositionFunctionSolid funcSolid; + CompositionFunction func; + union { + LinearGradientValues linear; + RadialGradientValues radial; + }; +}; + void qInitDrawhelperAsm(); class QRasterPaintEngine; @@ -204,12 +243,13 @@ struct QRadialGradientData struct { qreal x; qreal y; + qreal radius; } center; struct { qreal x; qreal y; + qreal radius; } focal; - qreal radius; }; struct QConicalGradientData @@ -233,8 +273,10 @@ struct QGradientData #ifdef Q_WS_QWS #define GRADIENT_STOPTABLE_SIZE 256 +#define GRADIENT_STOPTABLE_SIZE_SHIFT 8 #else #define GRADIENT_STOPTABLE_SIZE 1024 +#define GRADIENT_STOPTABLE_SIZE_SHIFT 10 #endif uint* colorTable; //[GRADIENT_STOPTABLE_SIZE]; @@ -308,6 +350,218 @@ struct QSpanData void adjustSpanMethods(); }; +static inline uint qt_gradient_clamp(const QGradientData *data, int ipos) +{ + if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) { + if (data->spread == QGradient::RepeatSpread) { + ipos = ipos % GRADIENT_STOPTABLE_SIZE; + ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos; + } else if (data->spread == QGradient::ReflectSpread) { + const int limit = GRADIENT_STOPTABLE_SIZE * 2; + ipos = ipos % limit; + ipos = ipos < 0 ? limit + ipos : ipos; + ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - 1 - ipos : ipos; + } else { + if (ipos < 0) + ipos = 0; + else if (ipos >= GRADIENT_STOPTABLE_SIZE) + ipos = GRADIENT_STOPTABLE_SIZE-1; + } + } + + Q_ASSERT(ipos >= 0); + Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE); + + return ipos; +} + +static inline uint qt_gradient_pixel(const QGradientData *data, qreal pos) +{ + int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5)); + return data->colorTable[qt_gradient_clamp(data, ipos)]; +} + +static inline qreal qRadialDeterminant(qreal a, qreal b, qreal c) +{ + return (b * b) - (4 * a * c); +} + +template <class RadialFetchFunc> +const uint * QT_FASTCALL qt_fetch_radial_gradient_template(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length) +{ + // avoid division by zero + if (qFuzzyIsNull(op->radial.a)) { + extern void (*qt_memfill32)(quint32 *dest, quint32 value, int count); + qt_memfill32(buffer, 0, length); + return buffer; + } + + const uint *b = buffer; + qreal rx = data->m21 * (y + qreal(0.5)) + + data->dx + data->m11 * (x + qreal(0.5)); + qreal ry = data->m22 * (y + qreal(0.5)) + + data->dy + data->m12 * (x + qreal(0.5)); + bool affine = !data->m13 && !data->m23; + + uint *end = buffer + length; + if (affine) { + rx -= data->gradient.radial.focal.x; + ry -= data->gradient.radial.focal.y; + + qreal inv_a = 1 / qreal(2 * op->radial.a); + + const qreal delta_rx = data->m11; + const qreal delta_ry = data->m12; + + qreal b = 2*(op->radial.dr*data->gradient.radial.focal.radius + rx * op->radial.dx + ry * op->radial.dy); + qreal delta_b = 2*(delta_rx * op->radial.dx + delta_ry * op->radial.dy); + const qreal b_delta_b = 2 * b * delta_b; + const qreal delta_b_delta_b = 2 * delta_b * delta_b; + + const qreal bb = b * b; + const qreal delta_bb = delta_b * delta_b; + + b *= inv_a; + delta_b *= inv_a; + + const qreal rxrxryry = rx * rx + ry * ry; + const qreal delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry; + const qreal rx_plus_ry = 2*(rx * delta_rx + ry * delta_ry); + const qreal delta_rx_plus_ry = 2 * delta_rxrxryry; + + inv_a *= inv_a; + + qreal det = (bb - 4 * op->radial.a * (op->radial.sqrfr - rxrxryry)) * inv_a; + qreal delta_det = (b_delta_b + delta_bb + 4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) * inv_a; + const qreal delta_delta_det = (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a; + + RadialFetchFunc::fetch(buffer, end, op, data, det, delta_det, delta_delta_det, b, delta_b); + } else { + qreal rw = data->m23 * (y + qreal(0.5)) + + data->m33 + data->m13 * (x + qreal(0.5)); + + while (buffer < end) { + if (rw == 0) { + *buffer = 0; + } else { + qreal invRw = 1 / rw; + qreal gx = rx * invRw - data->gradient.radial.focal.x; + qreal gy = ry * invRw - data->gradient.radial.focal.y; + qreal b = 2*(op->radial.dr*data->gradient.radial.focal.radius + gx*op->radial.dx + gy*op->radial.dy); + qreal det = qRadialDeterminant(op->radial.a, b, op->radial.sqrfr - (gx*gx + gy*gy)); + + quint32 result = 0; + if (det >= 0) { + qreal detSqrt = qSqrt(det); + + qreal s0 = (-b - detSqrt) * op->radial.inv2a; + qreal s1 = (-b + detSqrt) * op->radial.inv2a; + + qreal s = qMax(s0, s1); + + if (data->gradient.radial.focal.radius + op->radial.dr * s >= 0) + result = qt_gradient_pixel(&data->gradient, s); + } + + *buffer = result; + } + + rx += data->m11; + ry += data->m12; + rw += data->m13; + + ++buffer; + } + } + + return b; +} + +template <class Simd> +class QRadialFetchSimd +{ +public: + static void fetch(uint *buffer, uint *end, const Operator *op, const QSpanData *data, qreal det, + qreal delta_det, qreal delta_delta_det, qreal b, qreal delta_b) + { + typename Simd::Vect_buffer_f det_vec; + typename Simd::Vect_buffer_f delta_det4_vec; + typename Simd::Vect_buffer_f b_vec; + + for (int i = 0; i < 4; ++i) { + det_vec.f[i] = det; + delta_det4_vec.f[i] = 4 * delta_det; + b_vec.f[i] = b; + + det += delta_det; + delta_det += delta_delta_det; + b += delta_b; + } + + const typename Simd::Float32x4 v_delta_delta_det16 = Simd::v_dup(16 * delta_delta_det); + const typename Simd::Float32x4 v_delta_delta_det6 = Simd::v_dup(6 * delta_delta_det); + const typename Simd::Float32x4 v_delta_b4 = Simd::v_dup(4 * delta_b); + + const typename Simd::Float32x4 v_r0 = Simd::v_dup(data->gradient.radial.focal.radius); + const typename Simd::Float32x4 v_dr = Simd::v_dup(op->radial.dr); + + const typename Simd::Float32x4 v_min = Simd::v_dup(0.0f); + const typename Simd::Float32x4 v_max = Simd::v_dup(float(GRADIENT_STOPTABLE_SIZE-1)); + const typename Simd::Float32x4 v_half = Simd::v_dup(0.5f); + + const typename Simd::Int32x4 v_repeat_mask = Simd::v_dup(~(uint(0xffffff) << GRADIENT_STOPTABLE_SIZE_SHIFT)); + const typename Simd::Int32x4 v_reflect_mask = Simd::v_dup(~(uint(0xffffff) << (GRADIENT_STOPTABLE_SIZE_SHIFT+1))); + + const typename Simd::Int32x4 v_reflect_limit = Simd::v_dup(2 * GRADIENT_STOPTABLE_SIZE - 1); + + const int extended_mask = op->radial.extended ? 0x0 : ~0x0; + +#define FETCH_RADIAL_LOOP_PROLOGUE \ + while (buffer < end) { \ + typename Simd::Vect_buffer_i v_buffer_mask; \ + v_buffer_mask.v = Simd::v_greaterOrEqual(det_vec.v, v_min); \ + const typename Simd::Float32x4 v_index_local = Simd::v_sub(Simd::v_sqrt(Simd::v_max(v_min, det_vec.v)), b_vec.v); \ + const typename Simd::Float32x4 v_index = Simd::v_add(Simd::v_mul(v_index_local, v_max), v_half); \ + v_buffer_mask.v = Simd::v_and(v_buffer_mask.v, Simd::v_greaterOrEqual(Simd::v_add(v_r0, Simd::v_mul(v_dr, v_index_local)), v_min)); \ + typename Simd::Vect_buffer_i index_vec; +#define FETCH_RADIAL_LOOP_CLAMP_REPEAT \ + index_vec.v = Simd::v_and(v_repeat_mask, Simd::v_toInt(v_index)); +#define FETCH_RADIAL_LOOP_CLAMP_REFLECT \ + const typename Simd::Int32x4 v_index_i = Simd::v_and(v_reflect_mask, Simd::v_toInt(v_index)); \ + const typename Simd::Int32x4 v_index_i_inv = Simd::v_sub(v_reflect_limit, v_index_i); \ + index_vec.v = Simd::v_min_16(v_index_i, v_index_i_inv); +#define FETCH_RADIAL_LOOP_CLAMP_PAD \ + index_vec.v = Simd::v_toInt(Simd::v_min(v_max, Simd::v_max(v_min, v_index))); +#define FETCH_RADIAL_LOOP_EPILOGUE \ + det_vec.v = Simd::v_add(Simd::v_add(det_vec.v, delta_det4_vec.v), v_delta_delta_det6); \ + delta_det4_vec.v = Simd::v_add(delta_det4_vec.v, v_delta_delta_det16); \ + b_vec.v = Simd::v_add(b_vec.v, v_delta_b4); \ + for (int i = 0; i < 4; ++i) \ + *buffer++ = (extended_mask | v_buffer_mask.i[i]) & data->gradient.colorTable[index_vec.i[i]]; \ + } + +#define FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP) \ + FETCH_RADIAL_LOOP_PROLOGUE \ + FETCH_RADIAL_LOOP_CLAMP \ + FETCH_RADIAL_LOOP_EPILOGUE + + switch (data->gradient.spread) { + case QGradient::RepeatSpread: + FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP_REPEAT) + break; + case QGradient::ReflectSpread: + FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP_REFLECT) + break; + case QGradient::PadSpread: + FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP_PAD) + break; + default: + Q_ASSERT(false); + } + } +}; + #if defined(Q_CC_RVCT) # pragma push # pragma arm diff --git a/src/gui/painting/qdrawhelper_sse2.cpp b/src/gui/painting/qdrawhelper_sse2.cpp index 42a4872..340cd71 100644 --- a/src/gui/painting/qdrawhelper_sse2.cpp +++ b/src/gui/painting/qdrawhelper_sse2.cpp @@ -112,8 +112,6 @@ void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, // First, align dest to 16 bytes: ALIGNMENT_PROLOGUE_16BYTES(dst, x, w) { - quint32 s = src[x]; - s = BYTE_MUL(s, const_alpha); dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], one_minus_const_alpha); } @@ -127,8 +125,6 @@ void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, } } for (; x<w; ++x) { - quint32 s = src[x]; - s = BYTE_MUL(s, const_alpha); dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], one_minus_const_alpha); } dst = (quint32 *)(((uchar *) dst) + dbpl); @@ -491,6 +487,58 @@ void qt_bitmapblit16_sse2(QRasterBuffer *rasterBuffer, int x, int y, } } +class QSimdSse2 +{ +public: + typedef __m128i Int32x4; + typedef __m128 Float32x4; + + union Vect_buffer_i { Int32x4 v; int i[4]; }; + union Vect_buffer_f { Float32x4 v; float f[4]; }; + + static inline Float32x4 v_dup(float x) { return _mm_set1_ps(x); } + static inline Float32x4 v_dup(double x) { return _mm_set1_ps(x); } + static inline Int32x4 v_dup(int x) { return _mm_set1_epi32(x); } + static inline Int32x4 v_dup(uint x) { return _mm_set1_epi32(x); } + + static inline Float32x4 v_add(Float32x4 a, Float32x4 b) { return _mm_add_ps(a, b); } + static inline Int32x4 v_add(Int32x4 a, Int32x4 b) { return _mm_add_epi32(a, b); } + + static inline Float32x4 v_max(Float32x4 a, Float32x4 b) { return _mm_max_ps(a, b); } + static inline Float32x4 v_min(Float32x4 a, Float32x4 b) { return _mm_min_ps(a, b); } + static inline Int32x4 v_min_16(Int32x4 a, Int32x4 b) { return _mm_min_epi16(a, b); } + + static inline Int32x4 v_and(Int32x4 a, Int32x4 b) { return _mm_and_si128(a, b); } + + static inline Float32x4 v_sub(Float32x4 a, Float32x4 b) { return _mm_sub_ps(a, b); } + static inline Int32x4 v_sub(Int32x4 a, Int32x4 b) { return _mm_sub_epi32(a, b); } + + static inline Float32x4 v_mul(Float32x4 a, Float32x4 b) { return _mm_mul_ps(a, b); } + + static inline Float32x4 v_sqrt(Float32x4 x) { return _mm_sqrt_ps(x); } + + static inline Int32x4 v_toInt(Float32x4 x) { return _mm_cvttps_epi32(x); } + + // pre-VS 2008 doesn't have cast intrinsics, whereas 2008 and later requires it +#if defined(Q_CC_MSVC) && _MSC_VER < 1500 + static inline Int32x4 v_greaterOrEqual(Float32x4 a, Float32x4 b) + { + union Convert { Int32x4 vi; Float32x4 vf; } convert; + convert.vf = _mm_cmpgt_ps(a, b); + return convert.vi; + } +#else + static inline Int32x4 v_greaterOrEqual(Float32x4 a, Float32x4 b) { return _mm_castps_si128(_mm_cmpgt_ps(a, b)); } +#endif +}; + +const uint * QT_FASTCALL qt_fetch_radial_gradient_sse2(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length) +{ + return qt_fetch_radial_gradient_template<QRadialFetchSimd<QSimdSse2> >(buffer, op, data, y, x, length); +} + + QT_END_NAMESPACE #endif // QT_HAVE_SSE2 diff --git a/src/gui/painting/qdrawingprimitive_sse2_p.h b/src/gui/painting/qdrawingprimitive_sse2_p.h index 712fbd1..4c66d90 100644 --- a/src/gui/painting/qdrawingprimitive_sse2_p.h +++ b/src/gui/painting/qdrawingprimitive_sse2_p.h @@ -78,7 +78,7 @@ QT_BEGIN_NAMESPACE pixelVectorAG = _mm_mullo_epi16(pixelVectorAG, alphaChannel); \ pixelVectorRB = _mm_mullo_epi16(pixelVectorRB, alphaChannel); \ \ - /* 3. devide by 255, that's the tricky part. \ + /* 3. divide by 255, that's the tricky part. \ we do it like for BYTE_MUL(), with bit shift: X/255 ~= (X + X/256 + rounding)/256 */ \ /** so first (X + X/256 + rounding) */\ pixelVectorRB = _mm_add_epi16(pixelVectorRB, _mm_srli_epi16(pixelVectorRB, 8)); \ @@ -86,7 +86,7 @@ QT_BEGIN_NAMESPACE pixelVectorAG = _mm_add_epi16(pixelVectorAG, _mm_srli_epi16(pixelVectorAG, 8)); \ pixelVectorAG = _mm_add_epi16(pixelVectorAG, half); \ \ - /** second devide by 256 */\ + /** second divide by 256 */\ pixelVectorRB = _mm_srli_epi16(pixelVectorRB, 8); \ /** for AG, we could >> 8 to divide followed by << 8 to put the \ bytes in the correct position. By masking instead, we execute \ diff --git a/src/gui/painting/qdrawutil.cpp b/src/gui/painting/qdrawutil.cpp index 98294cb..1e98b05 100644 --- a/src/gui/painting/qdrawutil.cpp +++ b/src/gui/painting/qdrawutil.cpp @@ -1084,7 +1084,7 @@ void qDrawItem(QPainter *p, Qt::GUIStyle gs, according to the \a margins structure. */ -typedef QVarLengthArray<QPainter::PixmapFragment, 16> QPixmapFragmentsArray; +typedef QVarLengthArray<QRectF, 16> QRectFArray; /*! \since 4.6 @@ -1105,12 +1105,8 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins, const QTileRules &rules, QDrawBorderPixmap::DrawingHints hints) { - QPainter::PixmapFragment d; - d.opacity = 1.0; - d.rotation = 0.0; - - QPixmapFragmentsArray opaqueData; - QPixmapFragmentsArray translucentData; + QRectFArray sourceData[2]; + QRectFArray targetData[2]; // source center const int sourceCenterTop = sourceRect.top() + sourceMargins.top(); @@ -1192,166 +1188,95 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin // corners if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left - d.x = (0.5 * (xTarget[1] + xTarget[0])); - d.y = (0.5 * (yTarget[1] + yTarget[0])); - d.sourceLeft = sourceRect.left(); - d.sourceTop = sourceRect.top(); - d.width = sourceMargins.left(); - d.height = sourceMargins.top(); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueTopLeft) - opaqueData.append(d); - else - translucentData.append(d); + int index = bool(hints & QDrawBorderPixmap::OpaqueTopLeft); + sourceData[index].append(QRectF(sourceRect.topLeft(), QSizeF(sourceMargins.left(), sourceMargins.top()))); + targetData[index].append(QRectF(QPointF(xTarget[0], yTarget[0]), QPointF(xTarget[1], yTarget[1]))); } if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right - d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.y = (0.5 * (yTarget[1] + yTarget[0])); - d.sourceLeft = sourceCenterRight; - d.sourceTop = sourceRect.top(); - d.width = sourceMargins.right(); - d.height = sourceMargins.top(); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueTopRight) - opaqueData.append(d); - else - translucentData.append(d); + int index = bool(hints & QDrawBorderPixmap::OpaqueTopRight); + sourceData[index].append(QRectF(QPointF(sourceCenterRight, sourceRect.top()), QSizeF(sourceMargins.right(), sourceMargins.top()))); + targetData[index].append(QRectF(QPointF(xTarget[columns-1], yTarget[0]), QPointF(xTarget[columns], yTarget[1]))); } if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left - d.x = (0.5 * (xTarget[1] + xTarget[0])); - d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.sourceLeft = sourceRect.left(); - d.sourceTop = sourceCenterBottom; - d.width = sourceMargins.left(); - d.height = sourceMargins.bottom(); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueBottomLeft) - opaqueData.append(d); - else - translucentData.append(d); + int index = bool(hints & QDrawBorderPixmap::OpaqueBottomLeft); + sourceData[index].append(QRectF(QPointF(sourceRect.left(), sourceCenterBottom), QSizeF(sourceMargins.left(), sourceMargins.bottom()))); + targetData[index].append(QRectF(QPointF(xTarget[0], yTarget[rows - 1]), QPointF(xTarget[1], yTarget[rows]))); } if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right - d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.sourceLeft = sourceCenterRight; - d.sourceTop = sourceCenterBottom; - d.width = sourceMargins.right(); - d.height = sourceMargins.bottom(); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; - if (hints & QDrawBorderPixmap::OpaqueBottomRight) - opaqueData.append(d); - else - translucentData.append(d); + int index = bool(hints & QDrawBorderPixmap::OpaqueBottomRight); + sourceData[index].append(QRectF(QPointF(sourceCenterRight, sourceCenterBottom), QSizeF(sourceMargins.right(), sourceMargins.bottom()))); + targetData[index].append(QRectF(QPointF(xTarget[columns - 1], yTarget[rows - 1]), QPointF(xTarget[columns], yTarget[rows]))); } // horizontal edges if (targetCenterWidth > 0 && sourceCenterWidth > 0) { if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData; - d.sourceLeft = sourceCenterLeft; - d.sourceTop = sourceRect.top(); - d.width = sourceCenterWidth; - d.height = sourceMargins.top(); - d.y = (0.5 * (yTarget[1] + yTarget[0])); - d.scaleX = dx / d.width; - d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + int index = bool(hints & QDrawBorderPixmap::OpaqueTop); for (int i = 1; i < columns - 1; ++i) { - d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); - data.append(d); + if (rules.horizontal == Qt::RepeatTile && i == columns - 2) { + sourceData[index].append(QRectF(QPointF(sourceCenterLeft, sourceRect.top()), QSizeF(xTarget[i + 1] - xTarget[i], sourceMargins.top()))); + } else { + sourceData[index].append(QRectF(QPointF(sourceCenterLeft, sourceRect.top()), QSizeF(sourceCenterWidth, sourceMargins.top()))); + } + targetData[index].append(QRectF(QPointF(xTarget[i], yTarget[0]), QPointF(xTarget[i + 1], yTarget[1]))); } - if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); } if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData; - d.sourceLeft = sourceCenterLeft; - d.sourceTop = sourceCenterBottom; - d.width = sourceCenterWidth; - d.height = sourceMargins.bottom(); - d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); - d.scaleX = dx / d.width; - d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + int index = bool(hints & QDrawBorderPixmap::OpaqueBottom); for (int i = 1; i < columns - 1; ++i) { - d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); - data.append(d); + if (rules.horizontal == Qt::RepeatTile && i == columns - 2) { + sourceData[index].append(QRectF(QPointF(sourceCenterLeft, sourceCenterBottom), QSizeF(xTarget[i + 1] - xTarget[i], sourceMargins.bottom()))); + } else { + sourceData[index].append(QRectF(QPointF(sourceCenterLeft, sourceCenterBottom), QSizeF(sourceCenterWidth, sourceMargins.bottom()))); + } + targetData[index].append(QRectF(QPointF(xTarget[i], yTarget[rows - 1]), QPointF(xTarget[i + 1], yTarget[rows]))); } - if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); } } // vertical edges if (targetCenterHeight > 0 && sourceCenterHeight > 0) { if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData; - d.sourceLeft = sourceRect.left(); - d.sourceTop = sourceCenterTop; - d.width = sourceMargins.left(); - d.height = sourceCenterHeight; - d.x = (0.5 * (xTarget[1] + xTarget[0])); - d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; - d.scaleY = dy / d.height; + int index = bool(hints & QDrawBorderPixmap::OpaqueLeft); for (int i = 1; i < rows - 1; ++i) { - d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); - data.append(d); + if (rules.vertical == Qt::RepeatTile && i == rows - 2) { + sourceData[index].append(QRectF(QPointF(sourceRect.left(), sourceCenterTop), QSizeF(sourceMargins.left(), yTarget[i + 1] - yTarget[i]))); + } else { + sourceData[index].append(QRectF(QPointF(sourceRect.left(), sourceCenterTop), QSizeF(sourceMargins.left(), sourceCenterHeight))); + } + targetData[index].append(QRectF(QPointF(xTarget[0], yTarget[i]), QPointF(xTarget[1], yTarget[i + 1]))); } - if (rules.vertical == Qt::RepeatTile) - data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); } if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData; - d.sourceLeft = sourceCenterRight; - d.sourceTop = sourceCenterTop; - d.width = sourceMargins.right(); - d.height = sourceCenterHeight; - d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); - d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; - d.scaleY = dy / d.height; + int index = bool(hints & QDrawBorderPixmap::OpaqueRight); for (int i = 1; i < rows - 1; ++i) { - d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); - data.append(d); + if (rules.vertical == Qt::RepeatTile && i == rows - 2) { + sourceData[index].append(QRectF(QPointF(sourceCenterRight, sourceCenterTop), QSizeF(sourceMargins.right(), yTarget[i + 1] - yTarget[i]))); + } else { + sourceData[index].append(QRectF(QPointF(sourceCenterRight, sourceCenterTop), QSizeF(sourceMargins.right(), sourceCenterHeight))); + } + targetData[index].append(QRectF(QPointF(xTarget[columns - 1], yTarget[i]), QPointF(xTarget[columns], yTarget[i + 1]))); } - if (rules.vertical == Qt::RepeatTile) - data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); } } // center if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) { - QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData; - d.sourceLeft = sourceCenterLeft; - d.sourceTop = sourceCenterTop; - d.width = sourceCenterWidth; - d.height = sourceCenterHeight; - d.scaleX = dx / d.width; - d.scaleY = dy / d.height; - - qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX; - qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY; - + int index = bool(hints & QDrawBorderPixmap::OpaqueCenter); for (int j = 1; j < rows - 1; ++j) { - d.y = (0.5 * (yTarget[j + 1] + yTarget[j])); + qreal sourceHeight = (rules.vertical == Qt::RepeatTile && j == rows - 2) ? yTarget[j + 1] - yTarget[j] : sourceCenterHeight; for (int i = 1; i < columns - 1; ++i) { - d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); - data.append(d); + qreal sourceWidth = (rules.horizontal == Qt::RepeatTile && i == columns - 2) ? xTarget[i + 1] - xTarget[i] : sourceCenterWidth; + sourceData[index].append(QRectF(QPointF(sourceCenterLeft, sourceCenterTop), QSizeF(sourceWidth, sourceHeight))); + targetData[index].append(QRectF(QPointF(xTarget[i], yTarget[j]), QPointF(xTarget[i + 1], yTarget[j + 1]))); } - if (rules.horizontal == Qt::RepeatTile) - data[data.size() - 1].width = repeatWidth; - } - if (rules.vertical == Qt::RepeatTile) { - for (int i = 1; i < columns - 1; ++i) - data[data.size() - i].height = repeatHeight; } } - if (opaqueData.size()) - painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint); - if (translucentData.size()) - painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap); + for (int i = 0; i < 2; ++i) { + if (sourceData[i].size()) + painter->drawPixmapFragments(targetData[i].data(), sourceData[i].data(), sourceData[i].size(), pixmap, i == 1 ? QPainter::OpaqueHint : QPainter::PixmapFragmentHint(0)); + } if (oldAA) painter->setRenderHint(QPainter::Antialiasing, true); diff --git a/src/gui/painting/qemulationpaintengine.cpp b/src/gui/painting/qemulationpaintengine.cpp index 903ab1f..d7ad0f6 100644 --- a/src/gui/painting/qemulationpaintengine.cpp +++ b/src/gui/painting/qemulationpaintengine.cpp @@ -222,6 +222,47 @@ void QEmulationPaintEngine::drawImage(const QRectF &r, const QImage &pm, const Q real_engine->drawImage(r, pm, sr, flags); } +void QEmulationPaintEngine::drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, + QPainter::PixmapFragmentHints hints) +{ + if (state()->bgMode == Qt::OpaqueMode && pixmap.isQBitmap()) { + qreal oldOpacity = real_engine->state()->opacity; + QTransform oldTransform = real_engine->state()->matrix; + + for (int i = 0; i < fragmentCount; ++i) { + QTransform transform = oldTransform; + transform.translate(fragments[i].x, fragments[i].y); + transform.rotate(fragments[i].rotation); + real_engine->state()->opacity = oldOpacity * fragments[i].opacity; + real_engine->state()->matrix = transform; + real_engine->opacityChanged(); + real_engine->transformChanged(); + + qreal w = fragments[i].scaleX * fragments[i].width; + qreal h = fragments[i].scaleY * fragments[i].height; + fillBGRect(QRectF(-0.5 * w, -0.5 * h, w, h)); + } + + real_engine->state()->opacity = oldOpacity; + real_engine->state()->matrix = oldTransform; + real_engine->opacityChanged(); + real_engine->transformChanged(); + } + + real_engine->drawPixmapFragments(fragments, fragmentCount, pixmap, hints); +} + +void QEmulationPaintEngine::drawPixmapFragments(const QRectF *targetRects, const QRectF *sourceRects, int fragmentCount, const QPixmap &pixmap, + QPainter::PixmapFragmentHints hints) +{ + if (state()->bgMode == Qt::OpaqueMode && pixmap.isQBitmap()) { + for (int i = 0; i < fragmentCount; ++i) + fillBGRect(targetRects[i]); + } + + real_engine->drawPixmapFragments(targetRects, sourceRects, fragmentCount, pixmap, hints); +} + void QEmulationPaintEngine::clipEnabledChanged() { real_engine->clipEnabledChanged(); diff --git a/src/gui/painting/qemulationpaintengine_p.h b/src/gui/painting/qemulationpaintengine_p.h index fdc3688..b4ed7e7 100644 --- a/src/gui/painting/qemulationpaintengine_p.h +++ b/src/gui/painting/qemulationpaintengine_p.h @@ -81,6 +81,11 @@ public: virtual void drawStaticTextItem(QStaticTextItem *item); virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags); + + virtual void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, + QPainter::PixmapFragmentHints hints); + virtual void drawPixmapFragments(const QRectF *targetRects, const QRectF *sourceRects, int fragmentCount, const QPixmap &pixmap, + QPainter::PixmapFragmentHints hints); virtual void clipEnabledChanged(); virtual void penChanged(); diff --git a/src/gui/painting/qgraphicssystem.cpp b/src/gui/painting/qgraphicssystem.cpp index 455f07b..04e45d2 100644 --- a/src/gui/painting/qgraphicssystem.cpp +++ b/src/gui/painting/qgraphicssystem.cpp @@ -50,6 +50,9 @@ #ifdef Q_WS_MAC # include <private/qpixmap_mac_p.h> #endif +#ifdef Q_WS_QPA +# include <QtGui/private/qapplication_p.h> +#endif #ifdef Q_OS_SYMBIAN # include <private/qpixmap_raster_symbian_p.h> # include <private/qgraphicssystemex_symbian_p.h> @@ -74,6 +77,8 @@ QPixmapData *QGraphicsSystem::createDefaultPixmapData(QPixmapData::PixelType typ return new QRasterPixmapData(type); #elif defined(Q_WS_MAC) return new QMacPixmapData(type); +#elif defined(Q_WS_QPA) + return QApplicationPrivate::platformIntegration()->createPixmapData(type); #elif defined(Q_OS_SYMBIAN) return new QSymbianRasterPixmapData(type); #elif !defined(Q_WS_QWS) diff --git a/src/gui/painting/qgraphicssystem_p.h b/src/gui/painting/qgraphicssystem_p.h index adc90dd..db42fa8 100644 --- a/src/gui/painting/qgraphicssystem_p.h +++ b/src/gui/painting/qgraphicssystem_p.h @@ -55,10 +55,14 @@ #include "private/qpixmapdata_p.h" #include "private/qwindowsurface_p.h" +#include "private/qpaintengine_blitter_p.h" + +#include <qdebug.h> QT_BEGIN_NAMESPACE class QPixmapFilter; +class QBlittable; class QGraphicsSystemEx; class Q_GUI_EXPORT QGraphicsSystem @@ -68,7 +72,7 @@ public: virtual QPixmapData *createPixmapData(QPixmapData *origin); virtual QWindowSurface *createWindowSurface(QWidget *widget) const = 0; - virtual ~QGraphicsSystem() = 0; + virtual ~QGraphicsSystem(); //### Remove this & change qpixmap.cpp & qbitmap.cpp once every platform is gaurenteed // to have a graphics system. diff --git a/src/gui/painting/qgraphicssystem_runtime.cpp b/src/gui/painting/qgraphicssystem_runtime.cpp index 09d4f37..19b29a1 100644 --- a/src/gui/painting/qgraphicssystem_runtime.cpp +++ b/src/gui/painting/qgraphicssystem_runtime.cpp @@ -319,6 +319,11 @@ QPoint QRuntimeWindowSurface::offset(const QWidget *widget) const return m_windowSurface->offset(widget); } +QWindowSurface::WindowSurfaceFeatures QRuntimeWindowSurface::features() const +{ + return m_windowSurface->features(); +} + QRuntimeGraphicsSystem::QRuntimeGraphicsSystem() : m_windowSurfaceDestroyPolicy(DestroyImmediately), m_graphicsSystem(0) diff --git a/src/gui/painting/qgraphicssystem_runtime_p.h b/src/gui/painting/qgraphicssystem_runtime_p.h index e011dca..a3828d4 100644 --- a/src/gui/painting/qgraphicssystem_runtime_p.h +++ b/src/gui/painting/qgraphicssystem_runtime_p.h @@ -129,6 +129,8 @@ public: virtual QPoint offset(const QWidget *widget) const; + virtual WindowSurfaceFeatures features() const; + QScopedPointer<QWindowSurface> m_windowSurface; QScopedPointer<QWindowSurface> m_pendingWindowSurface; diff --git a/src/gui/painting/qgraphicssystemfactory.cpp b/src/gui/painting/qgraphicssystemfactory.cpp index e7b583f..01ece09 100644 --- a/src/gui/painting/qgraphicssystemfactory.cpp +++ b/src/gui/painting/qgraphicssystemfactory.cpp @@ -45,6 +45,7 @@ #include "qmutex.h" #include "qapplication.h" +#include <private/qapplication_p.h> #include "qgraphicssystem_raster_p.h" #include "qgraphicssystem_runtime_p.h" #include "qdebug.h" @@ -73,12 +74,13 @@ QGraphicsSystem *QGraphicsSystemFactory::create(const QString& key) if (system.isEmpty()) { system = QLatin1String("runtime"); } -#elif defined (QT_GRAPHICSSYSTEM_RASTER) && !defined(Q_WS_WIN) && !defined(Q_OS_SYMBIAN) +#elif defined (QT_GRAPHICSSYSTEM_RASTER) && !defined(Q_WS_WIN) && !defined(Q_OS_SYMBIAN) || defined(Q_WS_X11) if (system.isEmpty()) { system = QLatin1String("raster"); } #endif + QApplicationPrivate::graphics_system_name = system; if (system == QLatin1String("raster")) return new QRasterGraphicsSystem; else if (system == QLatin1String("runtime")) diff --git a/src/gui/painting/qgrayraster.c b/src/gui/painting/qgrayraster.c index ad7b502..5334f97 100644 --- a/src/gui/painting/qgrayraster.c +++ b/src/gui/painting/qgrayraster.c @@ -408,25 +408,31 @@ /* */ /* Record the current cell in the table. */ /* */ - static PCell - gray_find_cell( RAS_ARG ) + static void + gray_record_cell( RAS_ARG ) { PCell *pcell, cell; int x = ras.ex; + if ( ras.invalid || !( ras.area | ras.cover ) ) + return; if ( x > ras.max_ex ) x = ras.max_ex; pcell = &ras.ycells[ras.ey]; + for (;;) { cell = *pcell; if ( cell == NULL || cell->x > x ) break; - if ( cell->x == x ) - goto Exit; + if ( cell->x == x ) { + cell->area += ras.area; + cell->cover += ras.cover; + return; + } pcell = &cell->next; } @@ -436,28 +442,11 @@ cell = ras.cells + ras.num_cells++; cell->x = x; - cell->area = 0; - cell->cover = 0; + cell->area = ras.area; + cell->cover = ras.cover; cell->next = *pcell; *pcell = cell; - - Exit: - return cell; - } - - - static void - gray_record_cell( RAS_ARG ) - { - if ( !ras.invalid && ( ras.area | ras.cover ) ) - { - PCell cell = gray_find_cell( RAS_VAR ); - - - cell->area += ras.area; - cell->cover += ras.cover; - } } diff --git a/src/gui/painting/qgrayraster_p.h b/src/gui/painting/qgrayraster_p.h index 7d6f8d1..d593298 100644 --- a/src/gui/painting/qgrayraster_p.h +++ b/src/gui/painting/qgrayraster_p.h @@ -91,7 +91,7 @@ /* Minimum buffer size for raster object, that accounts for TWorker and TCell sizes.*/ -#define MINIMUM_POOL_SIZE 4096 +#define MINIMUM_POOL_SIZE 8192 QT_FT_EXPORT_VAR( const QT_FT_Raster_Funcs ) qt_ft_grays_raster; diff --git a/src/gui/painting/qpaintbuffer.cpp b/src/gui/painting/qpaintbuffer.cpp index e231665..ecc0280 100644 --- a/src/gui/painting/qpaintbuffer.cpp +++ b/src/gui/painting/qpaintbuffer.cpp @@ -47,6 +47,7 @@ #include <private/qimage_p.h> #include <qstatictext.h> #include <private/qstatictext_p.h> +#include <private/qrawfont_p.h> #include <QDebug> @@ -54,8 +55,6 @@ QT_BEGIN_NAMESPACE -Q_GUI_EXPORT extern int qt_defaultDpiX(); -Q_GUI_EXPORT extern int qt_defaultDpiY(); extern void qt_format_text(const QFont &font, const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect, int tabstops, int* tabarray, int tabarraylen, @@ -130,7 +129,7 @@ QPaintBufferPrivate::~QPaintBufferPrivate() for (int i = 0; i < commands.size(); ++i) { const QPaintBufferCommand &cmd = commands.at(i); if (cmd.id == QPaintBufferPrivate::Cmd_DrawTextItem) - delete reinterpret_cast<QTextItemIntCopy *>(qVariantValue<void *>(variants.at(cmd.offset))); + delete reinterpret_cast<QTextItemIntCopy *>(qvariant_cast<void *>(variants.at(cmd.offset))); } } @@ -330,7 +329,7 @@ QString QPaintBuffer::commandDescription(int command) const break; } case QPaintBufferPrivate::Cmd_SetBrush: { - QBrush brush = qVariantValue<QBrush>(d_ptr->variants.at(cmd.offset)); + QBrush brush = qvariant_cast<QBrush>(d_ptr->variants.at(cmd.offset)); debug << "Cmd_SetBrush: " << brush; break; } @@ -354,27 +353,27 @@ QString QPaintBuffer::commandDescription(int command) const break; } case QPaintBufferPrivate::Cmd_StrokeVectorPath: { - QPen pen = qVariantValue<QPen>(d_ptr->variants.at(cmd.extra)); + QPen pen = qvariant_cast<QPen>(d_ptr->variants.at(cmd.extra)); debug << "ExCmd_StrokeVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] << "pts/elms:" << cmd.offset << cmd.offset2 << pen; break; } case QPaintBufferPrivate::Cmd_FillVectorPath: { - QBrush brush = qVariantValue<QBrush>(d_ptr->variants.at(cmd.extra)); + QBrush brush = qvariant_cast<QBrush>(d_ptr->variants.at(cmd.extra)); debug << "ExCmd_FillVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] << "pts/elms:" << cmd.offset << cmd.offset2 << brush; break; } case QPaintBufferPrivate::Cmd_FillRectBrush: { - QBrush brush = qVariantValue<QBrush>(d_ptr->variants.at(cmd.extra)); + QBrush brush = qvariant_cast<QBrush>(d_ptr->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d_ptr->floats.constData() + cmd.offset); debug << "ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush; break; } case QPaintBufferPrivate::Cmd_FillRectColor: { - QColor color = qVariantValue<QColor>(d_ptr->variants.at(cmd.extra)); + QColor color = qvariant_cast<QColor>(d_ptr->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d_ptr->floats.constData() + cmd.offset); debug << "ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color; break; } @@ -451,12 +450,12 @@ QString QPaintBuffer::commandDescription(int command) const break; } case QPaintBufferPrivate::Cmd_SetPen: { - QPen pen = qVariantValue<QPen>(d_ptr->variants.at(cmd.offset)); + QPen pen = qvariant_cast<QPen>(d_ptr->variants.at(cmd.offset)); debug << "Cmd_SetPen: " << pen; break; } case QPaintBufferPrivate::Cmd_SetTransform: { - QTransform xform = qVariantValue<QTransform>(d_ptr->variants.at(cmd.offset)); + QTransform xform = qvariant_cast<QTransform>(d_ptr->variants.at(cmd.offset)); debug << "Cmd_SetTransform, offset: " << cmd.offset << xform; break; } @@ -532,20 +531,10 @@ QString QPaintBuffer::commandDescription(int command) const case QPaintBufferPrivate::Cmd_DrawTextItem: { QPointF pos(d_ptr->floats.at(cmd.extra), d_ptr->floats.at(cmd.extra+1)); - QTextItemIntCopy *tiCopy = reinterpret_cast<QTextItemIntCopy *>(qVariantValue<void *>(d_ptr->variants.at(cmd.offset))); + QTextItemIntCopy *tiCopy = reinterpret_cast<QTextItemIntCopy *>(qvariant_cast<void *>(d_ptr->variants.at(cmd.offset))); QTextItemInt &ti = (*tiCopy)(); QString text(ti.text()); - QFont font(ti.font()); - font.setUnderline(false); - font.setStrikeOut(false); - font.setOverline(false); - - const QTextItemInt &si = static_cast<const QTextItemInt &>(ti); - qreal justificationWidth = 0; - if (si.justified) - justificationWidth = si.width.toReal(); - debug << "Cmd_DrawTextItem:" << pos << " " << text; break; } case QPaintBufferPrivate::Cmd_SystemStateChanged: { @@ -1287,7 +1276,7 @@ void QPaintBufferEngine::drawTextItem(const QPointF &pos, const QTextItem &ti) qDebug() << "QPaintBufferEngine: drawTextItem: pos:" << pos << ti.text(); #endif if (m_stream_raw_text_items) { - QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawTextItem, qVariantFromValue<void *>(new QTextItemIntCopy(ti))); + QPaintBufferCommand *cmd = buffer->addCommand(QPaintBufferPrivate::Cmd_DrawTextItem, QVariant::fromValue<void *>(new QTextItemIntCopy(ti))); QFont font(ti.font()); font.setUnderline(false); @@ -1429,7 +1418,7 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) break; } case QPaintBufferPrivate::Cmd_SetPen: { - QPen pen = qVariantValue<QPen>(d->variants.at(cmd.offset)); + QPen pen = qvariant_cast<QPen>(d->variants.at(cmd.offset)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetPen: " << pen; #endif @@ -1437,7 +1426,7 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) break; } case QPaintBufferPrivate::Cmd_SetBrush: { - QBrush brush = qVariantValue<QBrush>(d->variants.at(cmd.offset)); + QBrush brush = qvariant_cast<QBrush>(d->variants.at(cmd.offset)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetBrush: " << brush; #endif @@ -1452,7 +1441,7 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) break; } case QPaintBufferPrivate::Cmd_SetTransform: { - QTransform xform = qVariantValue<QTransform>(d->variants.at(cmd.offset)); + QTransform xform = qvariant_cast<QTransform>(d->variants.at(cmd.offset)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_SetTransform, offset: " << cmd.offset << xform; #endif @@ -1520,7 +1509,7 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) break; } case QPaintBufferPrivate::Cmd_StrokeVectorPath: { - QPen pen = qVariantValue<QPen>(d->variants.at(cmd.extra)); + QPen pen = qvariant_cast<QPen>(d->variants.at(cmd.extra)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_StrokeVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] @@ -1531,7 +1520,7 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) break; } case QPaintBufferPrivate::Cmd_FillVectorPath: { - QBrush brush = qVariantValue<QBrush>(d->variants.at(cmd.extra)); + QBrush brush = qvariant_cast<QBrush>(d->variants.at(cmd.extra)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_FillVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] @@ -1705,7 +1694,7 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) break; } case QPaintBufferPrivate::Cmd_FillRectBrush: { - QBrush brush = qVariantValue<QBrush>(d->variants.at(cmd.extra)); + QBrush brush = qvariant_cast<QBrush>(d->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush; @@ -1714,7 +1703,7 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) break; } case QPaintBufferPrivate::Cmd_FillRectColor: { - QColor color = qVariantValue<QColor>(d->variants.at(cmd.extra)); + QColor color = qvariant_cast<QColor>(d->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> Cmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color; @@ -1756,26 +1745,38 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) painter->setClipRegion(region, Qt::ClipOperation(cmd.extra)); break; } +#if !defined(QT_NO_RAWFONT) case QPaintBufferPrivate::Cmd_DrawStaticText: { QVariantList variants(d->variants.at(cmd.offset).value<QVariantList>()); QFont font = variants.at(0).value<QFont>(); - QVector<quint32> glyphs; + QVector<quint32> glyphIndexes; QVector<QPointF> positions; for (int i=0; i<(variants.size() - 1) / 2; ++i) { - glyphs.append(variants.at(i*2 + 1).toUInt()); + glyphIndexes.append(variants.at(i*2 + 1).toUInt()); positions.append(variants.at(i*2 + 2).toPointF()); } painter->setFont(font); - qt_draw_glyphs(painter, glyphs.constData(), positions.constData(), glyphs.size()); - - break; + QRawFont rawFont; + QRawFontPrivate *rawFontD = QRawFontPrivate::get(rawFont); + QFontPrivate *fontD = QFontPrivate::get(font); + rawFontD->fontEngine = fontD->engineForScript(QUnicodeTables::Common); + rawFontD->fontEngine->ref.ref(); + + QGlyphRun glyphs; + glyphs.setRawFont(rawFont); + glyphs.setGlyphIndexes(glyphIndexes); + glyphs.setPositions(positions); + + painter->drawGlyphRun(QPointF(), glyphs); + break; } +#endif case QPaintBufferPrivate::Cmd_DrawText: { QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); @@ -1790,7 +1791,7 @@ void QPainterReplayer::process(const QPaintBufferCommand &cmd) case QPaintBufferPrivate::Cmd_DrawTextItem: { QPointF pos(d->floats.at(cmd.extra), d->floats.at(cmd.extra+1)); - QTextItemIntCopy *tiCopy = reinterpret_cast<QTextItemIntCopy *>(qVariantValue<void *>(d->variants.at(cmd.offset))); + QTextItemIntCopy *tiCopy = reinterpret_cast<QTextItemIntCopy *>(qvariant_cast<void *>(d->variants.at(cmd.offset))); QTextItemInt &ti = (*tiCopy)(); QString text(ti.text()); @@ -1885,7 +1886,7 @@ void QPaintEngineExReplayer::process(const QPaintBufferCommand &cmd) break; } case QPaintBufferPrivate::Cmd_StrokeVectorPath: { - QPen pen = qVariantValue<QPen>(d->variants.at(cmd.extra)); + QPen pen = qvariant_cast<QPen>(d->variants.at(cmd.extra)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_StrokeVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] @@ -1896,7 +1897,7 @@ void QPaintEngineExReplayer::process(const QPaintBufferCommand &cmd) break; } case QPaintBufferPrivate::Cmd_FillVectorPath: { - QBrush brush = qVariantValue<QBrush>(d->variants.at(cmd.extra)); + QBrush brush = qvariant_cast<QBrush>(d->variants.at(cmd.extra)); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_FillVectorPath: size: " << cmd.size // << ", hints:" << d->ints[cmd.offset2+cmd.size] @@ -1907,7 +1908,7 @@ void QPaintEngineExReplayer::process(const QPaintBufferCommand &cmd) break; } case QPaintBufferPrivate::Cmd_FillRectBrush: { - QBrush brush = qVariantValue<QBrush>(d->variants.at(cmd.extra)); + QBrush brush = qvariant_cast<QBrush>(d->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " brush: " << brush; @@ -1916,7 +1917,7 @@ void QPaintEngineExReplayer::process(const QPaintBufferCommand &cmd) break; } case QPaintBufferPrivate::Cmd_FillRectColor: { - QColor color = qVariantValue<QColor>(d->variants.at(cmd.extra)); + QColor color = qvariant_cast<QColor>(d->variants.at(cmd.extra)); QRectF *rect = (QRectF *)(d->floats.constData() + cmd.offset); #ifdef QPAINTBUFFER_DEBUG_DRAW qDebug() << " -> ExCmd_FillRectBrush, offset: " << cmd.offset << " rect: " << *rect << " color: " << color; diff --git a/src/gui/painting/qpaintdevice.cpp b/src/gui/painting/qpaintdevice.cpp index 6a2f13f..7b3d4ad 100644 --- a/src/gui/painting/qpaintdevice.cpp +++ b/src/gui/painting/qpaintdevice.cpp @@ -59,11 +59,13 @@ QPaintDevice::~QPaintDevice() } +#ifndef Q_WS_QPA int QPaintDevice::metric(PaintDeviceMetric) const { qWarning("QPaintDevice::metrics: Device has no metric information"); return 0; } +#endif Q_GUI_EXPORT int qt_paint_device_metric(const QPaintDevice *device, QPaintDevice::PaintDeviceMetric metric) { diff --git a/src/gui/painting/qpaintdevice_qpa.cpp b/src/gui/painting/qpaintdevice_qpa.cpp new file mode 100644 index 0000000..813965f --- /dev/null +++ b/src/gui/painting/qpaintdevice_qpa.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpaintdevice.h" +#include "qpainter.h" +#include "qwidget.h" +#include "qbitmap.h" +#include "qapplication.h" + +QT_BEGIN_NAMESPACE + +extern void qt_painter_removePaintDevice(QPaintDevice *); //qpainter.cpp + +int QPaintDevice::metric(PaintDeviceMetric m) const +{ + qWarning("QPaintDevice::metrics: Device has no metric information"); + if (m == PdmDpiX) { + return 72; + } else if (m == PdmDpiY) { + return 72; + } else if (m == PdmNumColors) { + // FIXME: does this need to be a real value? + return 256; + } else { + qDebug("Unrecognised metric %d!",m); + return 0; + } +} + +QT_END_NAMESPACE diff --git a/src/gui/painting/qpaintengine.cpp b/src/gui/painting/qpaintengine.cpp index 9562d83..38ba6f9 100644 --- a/src/gui/painting/qpaintengine.cpp +++ b/src/gui/painting/qpaintengine.cpp @@ -387,6 +387,7 @@ void QPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDraw \value MaxUser Last user type ID \value OpenGL2 \value PaintBuffer + \value Blitter */ /*! @@ -755,14 +756,15 @@ void QPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) path.setFillRule(Qt::WindingFill); #endif if (ti.glyphs.numGlyphs) - ti.fontEngine->addOutlineToPath(p.x(), p.y(), ti.glyphs, &path, ti.flags); + ti.fontEngine->addOutlineToPath(0, 0, ti.glyphs, &path, ti.flags); if (!path.isEmpty()) { - bool oldAA = painter()->renderHints() & QPainter::Antialiasing; + painter()->save(); painter()->setRenderHint(QPainter::Antialiasing, bool((painter()->renderHints() & QPainter::TextAntialiasing) && !(painter()->font().styleStrategy() & QFont::NoAntialias))); + painter()->translate(p.x(), p.y()); painter()->fillPath(path, state->pen().brush()); - painter()->setRenderHint(QPainter::Antialiasing, oldAA); + painter()->restore(); } } diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index 758aa80..6befdd8 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -213,6 +213,7 @@ public: OpenVG, OpenGL2, PaintBuffer, + Blitter, User = 50, // first user type id MaxUser = 100 // last user type id @@ -270,6 +271,9 @@ private: friend class QtopiaPrintEnginePrivate; friend class QProxyFontEngine; #endif +#ifdef Q_WS_QPA + friend class QFontEngineQPA; +#endif friend class QPainter; friend class QPainterPrivate; friend class QWidget; diff --git a/src/gui/painting/qpaintengine_alpha.cpp b/src/gui/painting/qpaintengine_alpha.cpp index 35f7bc1..beda2c7 100644 --- a/src/gui/painting/qpaintengine_alpha.cpp +++ b/src/gui/painting/qpaintengine_alpha.cpp @@ -46,6 +46,7 @@ #include "private/qpaintengine_alpha_p.h" #include "private/qpicture_p.h" +#include "private/qfont_p.h" #include "QtGui/qpicture.h" QT_BEGIN_NAMESPACE @@ -93,9 +94,6 @@ bool QAlphaPaintEngine::begin(QPaintDevice *pdev) return true; } -Q_GUI_EXPORT extern int qt_defaultDpiX(); -Q_GUI_EXPORT extern int qt_defaultDpiY(); - bool QAlphaPaintEngine::end() { Q_D(QAlphaPaintEngine); diff --git a/src/gui/painting/qpaintengine_blitter.cpp b/src/gui/painting/qpaintengine_blitter.cpp new file mode 100644 index 0000000..75efa20 --- /dev/null +++ b/src/gui/painting/qpaintengine_blitter.cpp @@ -0,0 +1,663 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qpaintengine_blitter_p.h" + +#include "private/qblittable_p.h" +#include "private/qpaintengine_raster_p.h" +#include "private/qpainter_p.h" +#include "private/qapplication_p.h" +#include "private/qpixmap_blitter_p.h" + +#ifndef QT_NO_BLITTABLE +QT_BEGIN_NAMESPACE + +#define STATE_XFORM_SCALE 0x00000001 +#define STATE_XFORM_COMPLEX 0x00000002 + +#define STATE_BRUSH_PATTERN 0x00000010 +#define STATE_BRUSH_ALPHA 0x00000020 + +#define STATE_PEN_ENABLED 0x00000100 + +#define STATE_ANTIALIASING 0x00001000 +#define STATE_ALPHA 0x00002000 +#define STATE_BLENDING_COMPLEX 0x00004000 + +#define STATE_CLIPSYS_COMPLEX 0x00010000 +#define STATE_CLIP_COMPLEX 0x00020000 + + +static inline void updateStateBits(uint *state, uint mask, bool on) +{ + *state = on ? (*state | mask) : (*state & ~mask); +} + +static inline bool checkStateAgainstMask(uint state, uint mask) +{ + return !state || (state & mask && !(state & ~mask)); +} + +class CapabilitiesToStateMask +{ +public: + CapabilitiesToStateMask(QBlittable::Capabilities capabilities) + : m_capabilities(capabilities), + fillRectMask(0), + drawRectMask(0), + drawPixmapMask(0), + capabillitiesState(0) + { + if (capabilities & QBlittable::SolidRectCapability) { + setFillRectMask(); + } + if (capabilities & QBlittable::SourcePixmapCapability) { + setSourcePixmapMask(); + } + if (capabilities & QBlittable::SourceOverPixmapCapability) { + setSourceOverPixmapMask(); + } + if (capabilities & QBlittable::SourceOverScaledPixmapCapability) { + setSourceOverScaledPixmapMask(); + } + } + + inline bool canBlitterFillRect() const + { + return checkStateAgainstMask(capabillitiesState,fillRectMask); + } + + inline bool canBlitterDrawRectMask() const + { + return checkStateAgainstMask(capabillitiesState,drawRectMask); + } + + bool canBlitterDrawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) const + { + if (pm.pixmapData()->classId() != QPixmapData::BlitterClass) + return false; + if (checkStateAgainstMask(capabillitiesState,drawPixmapMask)) { + if (m_capabilities & (QBlittable::SourceOverPixmapCapability + | QBlittable::SourceOverScaledPixmapCapability)) { + if (r.size() != sr.size()) { + return m_capabilities & QBlittable::SourceOverScaledPixmapCapability; + } else { + return m_capabilities & QBlittable::SourceOverPixmapCapability; + } + } + if ((m_capabilities & QBlittable::SourcePixmapCapability) && r.size() == sr.size() && !pm.hasAlphaChannel()) { + return m_capabilities & QBlittable::SourcePixmapCapability; + } + } + return false; + } + + inline void updateState(uint mask, bool on) { + updateStateBits(&capabillitiesState,mask,on); + } + +public: + + void setFillRectMask() { + updateStateBits(&fillRectMask, STATE_XFORM_SCALE, false); + updateStateBits(&fillRectMask, STATE_XFORM_COMPLEX, false); + + updateStateBits(&fillRectMask, STATE_BRUSH_PATTERN, false); + updateStateBits(&fillRectMask, STATE_BRUSH_ALPHA, false); + + updateStateBits(&fillRectMask, STATE_PEN_ENABLED, true); + + //Sub-pixel aliasing should not be sent to the blitter + updateStateBits(&fillRectMask, STATE_ANTIALIASING, true); + updateStateBits(&fillRectMask, STATE_ALPHA, false); + updateStateBits(&fillRectMask, STATE_BLENDING_COMPLEX, false); + + updateStateBits(&fillRectMask, STATE_CLIPSYS_COMPLEX, false); + updateStateBits(&fillRectMask, STATE_CLIP_COMPLEX, false); + } + + void setSourcePixmapMask() { + updateStateBits(&drawPixmapMask, STATE_XFORM_SCALE, true); + updateStateBits(&drawPixmapMask, STATE_XFORM_COMPLEX, false); + + updateStateBits(&drawPixmapMask, STATE_BRUSH_PATTERN, true); + updateStateBits(&drawPixmapMask, STATE_BRUSH_ALPHA, false); + + updateStateBits(&drawPixmapMask, STATE_PEN_ENABLED, true); + + updateStateBits(&drawPixmapMask, STATE_ANTIALIASING, true); + updateStateBits(&drawPixmapMask, STATE_ALPHA, false); + updateStateBits(&drawPixmapMask, STATE_BLENDING_COMPLEX, false); + + updateStateBits(&drawPixmapMask, STATE_CLIPSYS_COMPLEX, false); + updateStateBits(&drawPixmapMask, STATE_CLIP_COMPLEX, false); + } + + void setSourceOverPixmapMask() { + setSourcePixmapMask(); + } + + void setSourceOverScaledPixmapMask() { + setSourceOverPixmapMask(); + updateStateBits(&drawRectMask, STATE_XFORM_SCALE, true); + } + + QBlittable::Capabilities m_capabilities; + uint fillRectMask; + uint drawRectMask; + uint drawPixmapMask; + uint capabillitiesState; +}; + +class QBlitterPaintEnginePrivate : public QPaintEngineExPrivate +{ + Q_DECLARE_PUBLIC(QBlitterPaintEngine); +public: + QBlitterPaintEnginePrivate(QBlittablePixmapData *p) + : QPaintEngineExPrivate(), + pmData(p), + isBlitterLocked(false), + hasXForm(false) + + { + raster = new QRasterPaintEngine(p->buffer()); + capabillities = new CapabilitiesToStateMask(pmData->blittable()->capabilities()); + } + + inline void lock() { + if (!isBlitterLocked) { + raster->d_func()->rasterBuffer->prepare(pmData->blittable()->lock()); + isBlitterLocked = true; + } + } + + inline void unlock() { + if (isBlitterLocked) { + pmData->blittable()->unlock(); + isBlitterLocked = false; + } + } + + void fillRect(const QRectF &rect, const QColor &color) { + Q_Q(QBlitterPaintEngine); + pmData->unmarkRasterOverlay(rect); + QRectF targetRect = rect; + if (hasXForm) { + targetRect = q->state()->matrix.mapRect(rect); + } + const QClipData *clipData = q->clip(); + if (clipData) { + if (clipData->hasRectClip) { + unlock(); + pmData->blittable()->fillRect(targetRect & clipData->clipRect, color); + } else if (clipData->hasRegionClip) { + QVector<QRect> rects = clipData->clipRegion.rects(); + for ( int i = 0; i < rects.size(); i++ ) { + QRect intersectRect = rects.at(i).intersected(targetRect.toRect()); + if (!intersectRect.isEmpty()) { + unlock(); + pmData->blittable()->fillRect(intersectRect,color); + } + } + } + } else { + if (targetRect.x() >= 0 && targetRect.y() >= 0 + && targetRect.width() <= raster->paintDevice()->width() + && targetRect.height() <= raster->paintDevice()->height()) { + unlock(); + pmData->blittable()->fillRect(targetRect,color); + } else { + QRectF deviceRect(0,0,raster->paintDevice()->width(), raster->paintDevice()->height()); + unlock(); + pmData->blittable()->fillRect(deviceRect&targetRect,color); + } + } + } + + void clipAndDrawPixmap(const QRectF &clip, const QRectF &target, const QPixmap &pm, const QRectF &sr) { + QRectF intersectedRect = clip.intersected(target); + if (intersectedRect.isEmpty()) + return; + QRectF source = sr; + if(intersectedRect.size() != target.size()) { + qreal deltaTop = target.top() - intersectedRect.top(); + qreal deltaLeft = target.left() - intersectedRect.left(); + qreal deltaBottom = target.bottom() - intersectedRect.bottom(); + qreal deltaRight = target.right() - intersectedRect.right(); + source.adjust(-deltaLeft,-deltaTop,-deltaRight,-deltaBottom); + } + pmData->unmarkRasterOverlay(intersectedRect); + pmData->blittable()->drawPixmap(intersectedRect, pm, source); + } + + void updateClip() { + Q_Q(QBlitterPaintEngine); + const QClipData *clip = q->clip(); + bool complex = clip && !(clip->hasRectClip || clip->hasRegionClip); + capabillities->updateState(STATE_CLIP_COMPLEX, complex); + } + + void systemStateChanged() { + raster->d_func()->systemStateChanged(); + } + + QRasterPaintEngine *raster; + + QBlittablePixmapData *pmData; + bool isBlitterLocked; + + CapabilitiesToStateMask *capabillities; + + uint hasXForm; +}; + +QBlitterPaintEngine::QBlitterPaintEngine(QBlittablePixmapData *p) + : QPaintEngineEx(*(new QBlitterPaintEnginePrivate(p))) +{ +} + +QBlitterPaintEngine::~QBlitterPaintEngine() +{ +} + +QPainterState *QBlitterPaintEngine::createState(QPainterState *orig) const +{ + Q_D(const QBlitterPaintEngine); + return d->raster->createState(orig); +} + +bool QBlitterPaintEngine::begin(QPaintDevice *pdev) +{ + Q_D(QBlitterPaintEngine); + + setActive(true); + bool ok = d->raster->begin(pdev); +#ifdef QT_BLITTER_RASTEROVERLAY + d->pmData->unmergeOverlay(); +#endif + return ok; +} + + +bool QBlitterPaintEngine::end() +{ + Q_D(QBlitterPaintEngine); + + setActive(false); +#ifdef QT_BLITTER_RASTEROVERLAY + d->pmData->mergeOverlay(); +#endif + return d->raster->end(); +} + + +void QBlitterPaintEngine::fill(const QVectorPath &path, const QBrush &brush) +{ + Q_D(QBlitterPaintEngine); + if (path.shape() == QVectorPath::RectangleHint) { + QRectF rect(((QPointF *) path.points())[0], ((QPointF *) path.points())[2]); + fillRect(rect, brush); + } else { + d->lock(); + d->pmData->markRasterOverlay(path); + d->raster->fill(path, brush); + } +} + +void QBlitterPaintEngine::fillRect(const QRectF &rect, const QColor &color) +{ + Q_D(QBlitterPaintEngine); + if (d->capabillities->canBlitterFillRect() && color.alpha() == 0xff) { + d->fillRect(rect, color); + } else { + d->lock(); + d->pmData->markRasterOverlay(rect); + d->raster->fillRect(rect, color); + } +} + +void QBlitterPaintEngine::fillRect(const QRectF &rect, const QBrush &brush) +{ + if(rect.size().isEmpty()) + return; + + Q_D(QBlitterPaintEngine); + + if (qbrush_style(brush) == Qt::SolidPattern + && qbrush_color(brush).alpha() == 0xff + && d->capabillities->canBlitterFillRect()) + { + d->fillRect(rect, qbrush_color(brush)); + }else if (brush.style() == Qt::TexturePattern + && d->capabillities->canBlitterDrawPixmap(rect,brush.texture(),rect)) + { + bool rectIsFilled = false; + QRectF transformedRect = state()->matrix.mapRect(rect); + qreal x = transformedRect.x(); + qreal y = transformedRect.y(); + QPixmap pm = brush.texture(); + d->unlock(); + int srcX = int(rect.x() - state()->brushOrigin.x()) % pm.width(); + if (srcX < 0) + srcX = pm.width() + srcX; + const int startX = srcX; + int srcY = int(rect.y() - state()->brushOrigin.y()) % pm.height(); + if (srcY < 0) + srcY = pm.height() + srcY; + while (!rectIsFilled) { + qreal blitWidth = (pm.width() ) - srcX; + qreal blitHeight = (pm.height() ) - srcY; + if (x + blitWidth > transformedRect.right()) + blitWidth = transformedRect.right() -x; + if (y + blitHeight > transformedRect.bottom()) + blitHeight = transformedRect.bottom() - y; + const QClipData *clipData = clip(); + if (clipData->hasRectClip) { + QRect targetRect = QRect(x,y,blitWidth,blitHeight).intersected(clipData->clipRect); + if (targetRect.isValid()) { + int tmpSrcX = srcX + (targetRect.x() - x); + int tmpSrcY = srcY + (targetRect.y() - y); + QRect srcRect(tmpSrcX,tmpSrcY,targetRect.width(),targetRect.height()); + d->pmData->blittable()->drawPixmap(targetRect,pm,srcRect); + } + } else if (clipData->hasRegionClip) { + QVector<QRect> clipRects = clipData->clipRegion.rects(); + QRect unclippedTargetRect(x,y,blitWidth,blitHeight); + QRegion intersectedRects = clipData->clipRegion.intersected(unclippedTargetRect); + + for ( int i = 0; i < intersectedRects.rects().size(); i++ ) { + QRect targetRect = intersectedRects.rects().at(i); + if (!targetRect.isValid() || targetRect.isEmpty()) + continue; + int tmpSrcX = srcX + (targetRect.x() - x); + int tmpSrcY = srcY + (targetRect.y() - y); + QRect srcRect(tmpSrcX,tmpSrcY,targetRect.width(),targetRect.height()); + d->pmData->blittable()->drawPixmap(targetRect,pm,srcRect); + } + } + x+=blitWidth; + if (x>=transformedRect.right()) { + x = transformedRect.x(); + srcX = startX; + srcY = 0; + y+=blitHeight; + if (y>=transformedRect.bottom()) + rectIsFilled = true; + } else + srcX = 0; + } + } else { + d->lock(); + d->pmData->markRasterOverlay(rect); + d->raster->fillRect(rect, brush); + } + +} + +void QBlitterPaintEngine::stroke(const QVectorPath &path, const QPen &pen) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->pmData->markRasterOverlay(path); + d->raster->stroke(path, pen); +} + +void QBlitterPaintEngine::clip(const QVectorPath &path, Qt::ClipOperation op) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->clip(path, op); + d->updateClip(); +} +void QBlitterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op){ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->clip(rect,op); + d->updateClip(); +} +void QBlitterPaintEngine::clip(const QRegion ®ion, Qt::ClipOperation op) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->clip(region,op); + d->updateClip(); +} + +void QBlitterPaintEngine::clipEnabledChanged() +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->clipEnabledChanged(); +} + +void QBlitterPaintEngine::penChanged() +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->penChanged(); + d->capabillities->updateState(STATE_PEN_ENABLED,qpen_style(state()->pen) != Qt::NoPen); +} + +void QBlitterPaintEngine::brushChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->brushChanged(); + + bool solid = qbrush_style(state()->brush) == Qt::SolidPattern; + + d->capabillities->updateState(STATE_BRUSH_PATTERN, !solid); + d->capabillities->updateState(STATE_BRUSH_ALPHA, + qbrush_color(state()->brush).alpha() < 255); +} + +void QBlitterPaintEngine::brushOriginChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->brushOriginChanged(); +} + +void QBlitterPaintEngine::opacityChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->opacityChanged(); + + bool translucent = state()->opacity < 1; + d->capabillities->updateState(STATE_ALPHA,translucent); +} + +void QBlitterPaintEngine::compositionModeChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->compositionModeChanged(); + + bool nonTrivial = state()->composition_mode != QPainter::CompositionMode_SourceOver + && state()->composition_mode != QPainter::CompositionMode_Source; + + d->capabillities->updateState(STATE_BLENDING_COMPLEX,nonTrivial); +} + +void QBlitterPaintEngine::renderHintsChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->renderHintsChanged(); + + bool aa = state()->renderHints & QPainter::Antialiasing; + d->capabillities->updateState(STATE_ANTIALIASING, aa); + +} + +void QBlitterPaintEngine::transformChanged() +{ + Q_D(QBlitterPaintEngine); + d->raster->transformChanged(); + + QTransform::TransformationType type = state()->matrix.type(); + + d->capabillities->updateState(STATE_XFORM_COMPLEX, type > QTransform::TxScale); + d->capabillities->updateState(STATE_XFORM_SCALE, type > QTransform::TxTranslate); + + d->hasXForm = type >= QTransform::TxTranslate; + +} + +void QBlitterPaintEngine::drawRects(const QRect *rects, int rectCount) +{ + Q_D(QBlitterPaintEngine); + if (d->capabillities->canBlitterDrawRectMask()) { + for (int i=0; i<rectCount; ++i) { + d->fillRect(rects[i], qbrush_color(state()->brush)); + } + } else { + d->pmData->markRasterOverlay(rects,rectCount); + QPaintEngineEx::drawRects(rects, rectCount); + } +} + +void QBlitterPaintEngine::drawRects(const QRectF *rects, int rectCount) +{ + Q_D(QBlitterPaintEngine); + if (d->capabillities->canBlitterDrawRectMask()) { + for (int i=0; i<rectCount; ++i) { + d->fillRect(rects[i], qbrush_color(state()->brush)); + } + } else { + d->pmData->markRasterOverlay(rects,rectCount); + QPaintEngineEx::drawRects(rects, rectCount); + } +} + +void QBlitterPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) +{ + Q_D(QBlitterPaintEngine); + if (d->capabillities->canBlitterDrawPixmap(r,pm,sr)) { + + d->unlock(); + QRectF targetRect = r; + if (d->hasXForm) { + targetRect = state()->matrix.mapRect(r); + } + const QClipData *clipData = clip(); + if (clipData) { + if (clipData->hasRectClip) { + d->clipAndDrawPixmap(clipData->clipRect,targetRect,pm,sr); + }else if (clipData->hasRegionClip) { + QVector<QRect>rects = clipData->clipRegion.rects(); + for (int i = 0; i<rects.size(); i++) { + d->clipAndDrawPixmap(rects.at(i),targetRect,pm,sr); + } + } + } else { + QRectF deviceRect(0,0,d->raster->paintDevice()->width(), d->raster->paintDevice()->height()); + d->clipAndDrawPixmap(deviceRect,targetRect,pm,sr); + } + }else { + d->lock(); + d->pmData->markRasterOverlay(r); + d->raster->drawPixmap(r, pm, sr); + } +} + +void QBlitterPaintEngine::drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, + Qt::ImageConversionFlags flags) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->pmData->markRasterOverlay(r); + d->raster->drawImage(r, pm, sr, flags); +} + + +void QBlitterPaintEngine::drawTextItem(const QPointF &pos, const QTextItem &ti) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->drawTextItem(pos, ti); + d->pmData->markRasterOverlay(pos,ti); +} + +void QBlitterPaintEngine::drawStaticTextItem(QStaticTextItem *sti) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->raster->drawStaticTextItem(sti); + +//#### d->pmData->markRasterOverlay(sti); + qWarning("not implemented: markRasterOverlay for QStaticTextItem"); + +} + + +void QBlitterPaintEngine::drawEllipse(const QRectF &r) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + d->pmData->markRasterOverlay(r); + d->raster->drawEllipse(r); +} + +void QBlitterPaintEngine::setState(QPainterState *s) +{ + Q_D(QBlitterPaintEngine); + d->lock(); + QPaintEngineEx::setState(s); + d->raster->setState(s); + + clipEnabledChanged(); + penChanged(); + brushChanged(); + brushOriginChanged(); + opacityChanged(); + compositionModeChanged(); + renderHintsChanged(); + transformChanged(); + + d->updateClip(); +} + +inline QRasterPaintEngine *QBlitterPaintEngine::raster() const +{ + Q_D(const QBlitterPaintEngine); + return d->raster; +} + +QT_END_NAMESPACE +#endif //QT_NO_BLITTABLE + diff --git a/src/gui/painting/qpaintengine_blitter_p.h b/src/gui/painting/qpaintengine_blitter_p.h new file mode 100644 index 0000000..afd7486 --- /dev/null +++ b/src/gui/painting/qpaintengine_blitter_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPAINTENGINE_BLITTER_P_H +#define QPAINTENGINE_BLITTER_P_H + +#include "private/qpaintengineex_p.h" +#include "private/qpaintengine_raster_p.h" + +#ifndef QT_NO_BLITTABLE +QT_BEGIN_NAMESPACE + +class QBlitterPaintEnginePrivate; +class QBlittablePixmapData; +class QBlittable; + +class Q_GUI_EXPORT QBlitterPaintEngine : public QPaintEngineEx +{ + Q_DECLARE_PRIVATE(QBlitterPaintEngine); +public: + QBlitterPaintEngine(QBlittablePixmapData *p); + ~QBlitterPaintEngine(); + + virtual QPainterState *createState(QPainterState *orig) const; + + virtual QPaintEngine::Type type() const { return Blitter; } + + virtual bool begin(QPaintDevice *pdev); + virtual bool end(); + + virtual void fill(const QVectorPath &path, const QBrush &brush); + virtual void stroke(const QVectorPath &path, const QPen &pen); + + virtual void clip(const QVectorPath &path, Qt::ClipOperation op); + virtual void clip(const QRect &rect, Qt::ClipOperation op); + virtual void clip(const QRegion ®ion, Qt::ClipOperation op); + + virtual void clipEnabledChanged(); + virtual void penChanged(); + virtual void brushChanged(); + virtual void brushOriginChanged(); + virtual void opacityChanged(); + virtual void compositionModeChanged(); + virtual void renderHintsChanged(); + virtual void transformChanged(); + + virtual void fillRect(const QRectF &rect, const QBrush &brush); + virtual void fillRect(const QRectF &rect, const QColor &color); + + virtual void drawRects(const QRect *rects, int rectCount); + virtual void drawRects(const QRectF *rects, int rectCount); + + virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); + + virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags); + + virtual void drawTextItem(const QPointF &pos, const QTextItem &ti); + virtual void drawStaticTextItem(QStaticTextItem *); + + virtual void drawEllipse(const QRectF &r); + + virtual void setState(QPainterState *s); + + inline QPainterState *state() { return raster()->state(); } + inline const QPainterState *state() const { const QPainterState *state = raster()->state(); return state;} + inline const QClipData *clip(){return raster()->d_func()->clip();} + +private: + QRasterPaintEngine *raster() const; +}; + +QT_END_NAMESPACE +#endif //QT_NO_BLITTABLE +#endif // QPAINTENGINE_BLITTER_P_H + diff --git a/src/gui/painting/qpaintengine_mac.cpp b/src/gui/painting/qpaintengine_mac.cpp index 253df78..94666e1 100644 --- a/src/gui/painting/qpaintengine_mac.cpp +++ b/src/gui/painting/qpaintengine_mac.cpp @@ -57,6 +57,8 @@ #include <private/qfont_p.h> #include <private/qfontengine_p.h> +#include <private/qfontengine_coretext_p.h> +#include <private/qfontengine_mac_p.h> #include <private/qnumeric_p.h> #include <private/qpainter_p.h> #include <private/qpainterpath_p.h> @@ -98,7 +100,7 @@ QMacCGContext::QMacCGContext(QPainter *p) int devType = p->device()->devType(); if (pe->type() == QPaintEngine::Raster - && (devType == QInternal::Widget || devType == QInternal::Pixmap)) { + && (devType == QInternal::Widget || devType == QInternal::Pixmap || devType == QInternal::Image)) { extern CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice); CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pe->paintDevice()); @@ -131,8 +133,9 @@ QMacCGContext::QMacCGContext(QPainter *p) CGContextTranslateCTM(context, native.dx(), native.dy()); } + } else { + CGContextRetain(context); } - CGContextRetain(context); } @@ -1541,8 +1544,9 @@ void QCoreGraphicsPaintEnginePrivate::setFillBrush(const QPointF &offset) QPointF center(radialGrad->center()); QPointF focal(radialGrad->focalPoint()); qreal radius = radialGrad->radius(); + qreal focalRadius = radialGrad->focalRadius(); shading = CGShadingCreateRadial(colorspace, CGPointMake(focal.x(), focal.y()), - 0.0, CGPointMake(center.x(), center.y()), radius, fill_func, false, true); + focalRadius, CGPointMake(center.x(), center.y()), radius, fill_func, false, true); } CGFunctionRelease(fill_func); diff --git a/src/gui/painting/qpaintengine_mac_p.h b/src/gui/painting/qpaintengine_mac_p.h index f42ee5a..2434011 100644 --- a/src/gui/painting/qpaintengine_mac_p.h +++ b/src/gui/painting/qpaintengine_mac_p.h @@ -57,15 +57,12 @@ #include "private/qt_mac_p.h" #include "private/qpaintengine_p.h" #include "private/qpolygonclipper_p.h" +#include "private/qfont_p.h" #include "QtCore/qhash.h" typedef struct CGColorSpace *CGColorSpaceRef; QT_BEGIN_NAMESPACE -extern int qt_defaultDpi(); -extern int qt_defaultDpiX(); -extern int qt_defaultDpiY(); - class QCoreGraphicsPaintEnginePrivate; class QCoreGraphicsPaintEngine : public QPaintEngine { @@ -124,6 +121,8 @@ public: void drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode) { QPaintEngine::drawPolygon(points, pointCount, mode); } + bool supportsTransformations(qreal, const QTransform &) const { return true; }; + protected: friend class QMacPrintEngine; friend class QMacPrintEnginePrivate; diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index cd9206c..bcc5f9d 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -69,6 +69,7 @@ // #include <private/qrasterizer_p.h> #include <private/qimage_p.h> #include <private/qstatictext_p.h> +#include <private/qcosmeticstroker_p.h> #include "qmemrotate_p.h" #include "qpaintengine_raster_p.h" @@ -96,6 +97,8 @@ # include <private/qabstractfontengine_p.h> #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) # include <private/qfontengine_s60_p.h> +#elif defined(Q_WS_QPA) +# include <private/qfontengine_ft_p.h> #endif #if defined(Q_WS_WIN64) @@ -157,16 +160,6 @@ enum LineDrawMode { LineDrawIncludeLastPixel }; -static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data, - LineDrawMode style, const QIntRect &rect); -static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2, - QPen *pen, ProcessSpans span_func, QSpanData *data, - LineDrawMode style, const QIntRect &devRect, - int *patternOffset); -// static void drawLine_midpoint_f(qreal x1, qreal y1, qreal x2, qreal y2, -// ProcessSpans span_func, QSpanData *data, -// LineDrawMode style, const QRect &devRect); - static void drawEllipse_midpoint_i(const QRect &rect, const QRect &clip, ProcessSpans pen_func, ProcessSpans brush_func, QSpanData *pen_data, QSpanData *brush_data); @@ -444,7 +437,7 @@ bool QRasterPaintEngine::begin(QPaintDevice *device) if (device->devType() == QInternal::Pixmap) { QPixmap *pixmap = static_cast<QPixmap *>(device); QPixmapData *pd = pixmap->pixmapData(); - if (pd->classId() == QPixmapData::RasterClass) + if (pd->classId() == QPixmapData::RasterClass || pd->classId() == QPixmapData::BlitterClass) d->device = pd->buffer(); } else { d->device = device; @@ -663,31 +656,23 @@ QRasterPaintEngineState::QRasterPaintEngineState() QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s) : QPainterState(s) + , lastPen(s.lastPen) + , penData(s.penData) + , stroker(s.stroker) + , strokeFlags(s.strokeFlags) + , lastBrush(s.lastBrush) + , brushData(s.brushData) + , fillFlags(s.fillFlags) + , pixmapFlags(s.pixmapFlags) + , intOpacity(s.intOpacity) + , txscale(s.txscale) + , clip(s.clip) + , dirty(s.dirty) + , flag_bits(s.flag_bits) { - stroker = s.stroker; - - lastBrush = s.lastBrush; - brushData = s.brushData; brushData.tempImage = 0; - - lastPen = s.lastPen; - penData = s.penData; penData.tempImage = 0; - - fillFlags = s.fillFlags; - strokeFlags = s.strokeFlags; - pixmapFlags = s.pixmapFlags; - - intOpacity = s.intOpacity; - - txscale = s.txscale; - - flag_bits = s.flag_bits; - - clip = s.clip; flags.has_clip_ownership = false; - - dirty = s.dirty; } /*! @@ -798,14 +783,12 @@ void QRasterPaintEngine::updatePen(const QPen &pen) s->stroker = 0; } + ensureState(); // needed because of tx_noshear... s->flags.fast_pen = pen_style > Qt::NoPen - && s->penData.blend - && !s->flags.antialiased - && (penWidth == 0 || (penWidth <= 1 - && (s->matrix.type() <= QTransform::TxTranslate - || pen.isCosmetic()))); + && s->penData.blend + && ((pen.isCosmetic() && penWidth <= 1) + || (s->flags.tx_noshear && penWidth * s->txscale <= 1)); - ensureState(); // needed because of tx_noshear... s->flags.non_complex_pen = qpen_capStyle(s->lastPen) <= Qt::SquareCap && s->flags.tx_noshear; s->strokeFlags = 0; @@ -1522,6 +1505,7 @@ void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount) qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount); #endif Q_D(QRasterPaintEngine); + ensureState(); QRasterPaintEngineState *s = state(); // Fill @@ -1550,32 +1534,14 @@ void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount) ensurePen(); if (s->penData.blend) { - if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) { - const QRect *r = rects; - const QRect *lastRect = rects + rectCount; - while (r < lastRect) { - int left = r->x(); - int right = r->x() + r->width(); - int top = r->y(); - int bottom = r->y() + r->height(); - -#ifdef Q_WS_MAC - int pts[] = { top, left, - top, right, - bottom, right, - bottom, left }; -#else - int pts[] = { left, top, - right, top, - right, bottom, - left, bottom }; -#endif - - strokePolygonCosmetic((QPoint *) pts, 4, WindingMode); - ++r; + QRectVectorPath path; + if (s->flags.fast_pen) { + QCosmeticStroker stroker(s, d->deviceRect); + for (int i = 0; i < rectCount; ++i) { + path.set(rects[i]); + stroker.drawPath(path); } } else { - QRectVectorPath path; for (int i = 0; i < rectCount; ++i) { path.set(rects[i]); stroke(path, s->pen); @@ -1590,13 +1556,13 @@ void QRasterPaintEngine::drawRects(const QRect *rects, int rectCount) void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount) { #ifdef QT_DEBUG_DRAW - qDebug(" - QRasterPaintEngine::drawRect(), rectCount=%d", rectCount); + qDebug(" - QRasterPaintEngine::drawRect(QRectF*), rectCount=%d", rectCount); #endif #ifdef QT_FAST_SPANS Q_D(QRasterPaintEngine); + ensureState(); QRasterPaintEngineState *s = state(); - ensureState(); if (s->flags.tx_noshear) { ensureBrush(); @@ -1614,59 +1580,17 @@ void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount) ensurePen(); if (s->penData.blend) { - qreal width = s->pen.isCosmetic() - ? (s->lastPen.widthF() == 0 ? 1 : s->lastPen.widthF()) - : s->lastPen.widthF() * s->txscale; - - if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) { - for (int i = 0; i < rectCount; ++i) { - const QRectF &r = rects[i]; - qreal left = r.x(); - qreal right = r.x() + r.width(); - qreal top = r.y(); - qreal bottom = r.y() + r.height(); - qreal pts[] = { left, top, - right, top, - right, bottom, - left, bottom }; - strokePolygonCosmetic((QPointF *) pts, 4, WindingMode); - } - } else if (width <= 1 && qpen_style(s->lastPen) == Qt::SolidLine) { - d->initializeRasterizer(&s->penData); - + QRectVectorPath path; + if (s->flags.fast_pen) { + QCosmeticStroker stroker(s, d->deviceRect); for (int i = 0; i < rectCount; ++i) { - const QRectF &rect = rects[i].normalized(); - if (rect.isEmpty()) { - qreal pts[] = { rect.left(), rect.top(), rect.right(), rect.bottom() }; - QVectorPath vp(pts, 2, 0, QVectorPath::LinesHint); - QPaintEngineEx::stroke(vp, s->lastPen); - } else { - const QPointF tl = s->matrix.map(rect.topLeft()); - const QPointF tr = s->matrix.map(rect.topRight()); - const QPointF bl = s->matrix.map(rect.bottomLeft()); - const QPointF br = s->matrix.map(rect.bottomRight()); - const qreal w = width / (rect.width() * s->txscale); - const qreal h = width / (rect.height() * s->txscale); - d->rasterizer->rasterizeLine(tl, tr, w); // top - d->rasterizer->rasterizeLine(bl, br, w); // bottom - d->rasterizer->rasterizeLine(bl, tl, h); // left - d->rasterizer->rasterizeLine(br, tr, h); // right - } + path.set(rects[i]); + stroker.drawPath(path); } } else { for (int i = 0; i < rectCount; ++i) { - const QRectF &r = rects[i]; - qreal left = r.x(); - qreal right = r.x() + r.width(); - qreal top = r.y(); - qreal bottom = r.y() + r.height(); - qreal pts[] = { left, top, - right, top, - right, bottom, - left, bottom, - left, top }; - QVectorPath vp(pts, 5, 0, QVectorPath::RectangleHint); - QPaintEngineEx::stroke(vp, s->lastPen); + path.set(rects[i]); + QPaintEngineEx::stroke(path, s->lastPen); } } } @@ -1683,36 +1607,16 @@ void QRasterPaintEngine::drawRects(const QRectF *rects, int rectCount) */ void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen) { + Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); + ensurePen(pen); if (!s->penData.blend) return; - if (s->flags.fast_pen && !path.isCurved() - && s->lastPen.brush().isOpaque()) { - int count = path.elementCount(); - QPointF *points = (QPointF *) path.points(); - const QPainterPath::ElementType *types = path.elements(); - if (types) { - int first = 0; - int last; - while (first < count) { - while (first < count && types[first] != QPainterPath::MoveToElement) ++first; - last = first + 1; - while (last < count && types[last] == QPainterPath::LineToElement) ++last; - strokePolygonCosmetic(points + first, last - first, - path.hasImplicitClose() && last == count // only close last one.. - ? WindingMode - : PolylineMode); - first = last; - } - } else { - strokePolygonCosmetic(points, count, - path.hasImplicitClose() - ? WindingMode - : PolylineMode); - } - + if (s->flags.fast_pen) { + QCosmeticStroker stroker(s, d->deviceRect); + stroker.drawPath(path); } else if (s->flags.non_complex_pen && path.shape() == QVectorPath::LinesHint) { qreal width = s->lastPen.isCosmetic() ? (qpen_widthf(s->lastPen) == 0 ? 1 : qpen_widthf(s->lastPen)) @@ -1827,26 +1731,6 @@ void QRasterPaintEngine::fill(const QVectorPath &path, const QBrush &brush) } } - if (path.shape() == QVectorPath::EllipseHint) { - if (!s->flags.antialiased && s->matrix.type() <= QTransform::TxScale) { - const qreal *p = path.points(); - QPointF tl = QPointF(p[0], p[1]) * s->matrix; - QPointF br = QPointF(p[4], p[5]) * s->matrix; - QRectF r = s->matrix.mapRect(QRectF(tl, br)); - - ProcessSpans penBlend = d->getPenFunc(r, &s->penData); - ProcessSpans brushBlend = d->getBrushFunc(r, &s->brushData); - const QRect brect = QRect(int(r.x()), int(r.y()), - int_dim(r.x(), r.width()), - int_dim(r.y(), r.height())); - if (brect == r) { - drawEllipse_midpoint_i(brect, d->deviceRect, penBlend, brushBlend, - &s->penData, &s->brushData); - return; - } - } - } - // ### Optimize for non transformed ellipses and rectangles... QRectF cpRect = path.controlPointRect(); const QRect deviceRect = s->matrix.mapRect(cpRect).toRect(); @@ -2058,9 +1942,9 @@ void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, Poly } ensurePen(); - ensureBrush(); if (mode != PolylineMode) { // Do the fill... + ensureBrush(); if (s->brushData.blend) { d->outlineMapper->setCoordinateRounding(s->penData.blend && s->flags.fast_pen && s->lastPen.brush().isOpaque()); fillPolygon(points, pointCount, mode); @@ -2070,10 +1954,11 @@ void QRasterPaintEngine::drawPolygon(const QPointF *points, int pointCount, Poly // Do the outline... if (s->penData.blend) { - if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) - strokePolygonCosmetic(points, pointCount, mode); - else { - QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode)); + QVectorPath vp((qreal *) points, pointCount, 0, QVectorPath::polygonFlags(mode)); + if (s->flags.fast_pen) { + QCosmeticStroker stroker(s, d->deviceRect); + stroker.drawPath(vp); + } else { QPaintEngineEx::stroke(vp, s->lastPen); } } @@ -2102,13 +1987,7 @@ void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, Polyg return; } - ensureState(); ensurePen(); - if (!(s->flags.int_xform && s->flags.fast_pen && (!s->penData.blend || s->pen.brush().isOpaque()))) { - // this calls the float version - QPaintEngineEx::drawPolygon(points, pointCount, mode); - return; - } // Do the fill if (mode != PolylineMode) { @@ -2136,232 +2015,24 @@ void QRasterPaintEngine::drawPolygon(const QPoint *points, int pointCount, Polyg // Do the outline... if (s->penData.blend) { - if (s->flags.fast_pen && s->lastPen.brush().isOpaque()) - strokePolygonCosmetic(points, pointCount, mode); - else { - int count = pointCount * 2; - QVarLengthArray<qreal> fpoints(count); -#ifdef Q_WS_MAC - for (int i=0; i<count; i+=2) { - fpoints[i] = ((int *) points)[i+1]; - fpoints[i+1] = ((int *) points)[i]; - } -#else - for (int i=0; i<count; ++i) - fpoints[i] = ((int *) points)[i]; -#endif - QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode)); - QPaintEngineEx::stroke(vp, s->lastPen); - } - } -} - -/*! - \internal -*/ -void QRasterPaintEngine::strokePolygonCosmetic(const QPointF *points, int pointCount, PolygonDrawMode mode) -{ - Q_D(QRasterPaintEngine); - QRasterPaintEngineState *s = state(); - - Q_ASSERT(s->penData.blend); - Q_ASSERT(s->flags.fast_pen); - - bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1]; - - // Use fast path for 0 width / trivial pens. - QIntRect devRect; - devRect.set(d->deviceRect); - - LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap - ? LineDrawIncludeLastPixel - : LineDrawNormal); - int dashOffset = int(s->lastPen.dashOffset()); - - const QPointF offs(aliasedCoordinateDelta, aliasedCoordinateDelta); - - // Draw all the line segments. - for (int i=1; i<pointCount; ++i) { - - QPointF lp1 = points[i-1] * s->matrix + offs; - QPointF lp2 = points[i] * s->matrix + offs; - - const QRectF brect(lp1, lp2); - ProcessSpans penBlend = d->getPenFunc(brect, &s->penData); - if (qpen_style(s->lastPen) == Qt::SolidLine) { - drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()), - qFloor(lp2.x()), qFloor(lp2.y()), - penBlend, &s->penData, - i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel, - devRect); - } else { - drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()), - qFloor(lp2.x()), qFloor(lp2.y()), - &s->lastPen, - penBlend, &s->penData, - i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel, - devRect, &dashOffset); + int count = pointCount * 2; + QVarLengthArray<qreal> fpoints(count); + #ifdef Q_WS_MAC + for (int i=0; i<count; i+=2) { + fpoints[i] = ((int *) points)[i+1]; + fpoints[i+1] = ((int *) points)[i]; } - } - - // Polygons are implicitly closed. - if (needs_closing) { - QPointF lp1 = points[pointCount-1] * s->matrix + offs; - QPointF lp2 = points[0] * s->matrix + offs; - - const QRectF brect(lp1, lp2); - ProcessSpans penBlend = d->getPenFunc(brect, &s->penData); - if (qpen_style(s->lastPen) == Qt::SolidLine) { - drawLine_midpoint_i(qFloor(lp1.x()), qFloor(lp1.y()), - qFloor(lp2.x()), qFloor(lp2.y()), - penBlend, &s->penData, - LineDrawIncludeLastPixel, - devRect); + #else + for (int i=0; i<count; ++i) + fpoints[i] = ((int *) points)[i]; + #endif + QVectorPath vp((qreal *) fpoints.data(), pointCount, 0, QVectorPath::polygonFlags(mode)); + + if (s->flags.fast_pen) { + QCosmeticStroker stroker(s, d->deviceRect); + stroker.drawPath(vp); } else { - drawLine_midpoint_dashed_i(qFloor(lp1.x()), qFloor(lp1.y()), - qFloor(lp2.x()), qFloor(lp2.y()), - &s->lastPen, - penBlend, &s->penData, - LineDrawIncludeLastPixel, - devRect, &dashOffset); - } - } - -} - -/*! - \internal -*/ -void QRasterPaintEngine::strokePolygonCosmetic(const QPoint *points, int pointCount, PolygonDrawMode mode) -{ - Q_D(QRasterPaintEngine); - QRasterPaintEngineState *s = state(); - - // We assert here because this function is called from drawRects - // and drawPolygon and they already do ensurePen(), so we skip that - // here to avoid duplicate checks.. - Q_ASSERT(s->penData.blend); - - bool needs_closing = mode != PolylineMode && points[0] != points[pointCount-1]; - - QIntRect devRect; - devRect.set(d->deviceRect); - - LineDrawMode mode_for_last = (s->lastPen.capStyle() != Qt::FlatCap - ? LineDrawIncludeLastPixel - : LineDrawNormal); - - int m11 = int(s->matrix.m11()); - int m22 = int(s->matrix.m22()); - int dx = int(s->matrix.dx()); - int dy = int(s->matrix.dy()); - int m13 = int(s->matrix.m13()); - int m23 = int(s->matrix.m23()); - bool affine = !m13 && !m23; - - int dashOffset = int(s->lastPen.dashOffset()); - - if (affine) { - // Draw all the line segments. - for (int i=1; i<pointCount; ++i) { - const QPoint lp1 = points[i-1] * s->matrix; - const QPoint lp2 = points[i] * s->matrix; - const QRect brect(lp1, lp2); - ProcessSpans penBlend = d->getPenFunc(brect, &s->penData); - - if (qpen_style(s->lastPen) == Qt::SolidLine) - drawLine_midpoint_i(lp1.x(), lp1.y(), - lp2.x(), lp2.y(), - penBlend, &s->penData, - i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel, - devRect); - else - drawLine_midpoint_dashed_i(lp1.x(), lp1.y(), - lp2.x(), lp2.y(), - &s->lastPen, - penBlend, &s->penData, - i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel, - devRect, &dashOffset); - - } - - // Polygons are implicitly closed. - if (needs_closing) { - const QPoint lp1 = points[pointCount - 1] * s->matrix; - const QPoint lp2 = points[0] * s->matrix; - const QRect brect(lp1, lp2); - ProcessSpans penBlend = d->getPenFunc(brect, &s->penData); - - if (qpen_style(s->lastPen) == Qt::SolidLine) - drawLine_midpoint_i(lp1.x(), lp1.y(), - lp2.x(), lp2.y(), - penBlend, &s->penData, LineDrawIncludeLastPixel, - devRect); - else - drawLine_midpoint_dashed_i(lp1.x(), lp1.y(), - lp2.x(), lp2.y(), - &s->lastPen, - penBlend, &s->penData, LineDrawIncludeLastPixel, - devRect, &dashOffset); - } - } else { - // Draw all the line segments. - for (int i=1; i<pointCount; ++i) { - int x1 = points[i-1].x() * m11 + dx; - int y1 = points[i-1].y() * m22 + dy; - qreal w = m13*points[i-1].x() + m23*points[i-1].y() + 1.; - w = 1/w; - x1 = int(x1*w); - y1 = int(y1*w); - int x2 = points[i].x() * m11 + dx; - int y2 = points[i].y() * m22 + dy; - w = m13*points[i].x() + m23*points[i].y() + 1.; - w = 1/w; - x2 = int(x2*w); - y2 = int(y2*w); - - const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); - ProcessSpans penBlend = d->getPenFunc(brect, &s->penData); - if (qpen_style(s->lastPen) == Qt::SolidLine) - drawLine_midpoint_i(x1, y1, x2, y2, - penBlend, &s->penData, - i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel, - devRect); - else - drawLine_midpoint_dashed_i(x1, y1, x2, y2, - &s->lastPen, - penBlend, &s->penData, - i == pointCount - 1 ? mode_for_last : LineDrawIncludeLastPixel, - devRect, &dashOffset); - - } - - int x1 = points[pointCount-1].x() * m11 + dx; - int y1 = points[pointCount-1].y() * m22 + dy; - qreal w = m13*points[pointCount-1].x() + m23*points[pointCount-1].y() + 1.; - w = 1/w; - x1 = int(x1*w); - y1 = int(y1*w); - int x2 = points[0].x() * m11 + dx; - int y2 = points[0].y() * m22 + dy; - w = m13*points[0].x() + m23*points[0].y() + 1.; - w = 1/w; - x2 = int(x2 * w); - y2 = int(y2 * w); - // Polygons are implicitly closed. - - if (needs_closing) { - const QRect brect(x1, y1, x2 - x1 + 1, y2 - y1 + 1); - ProcessSpans penBlend = d->getPenFunc(brect, &s->penData); - if (qpen_style(s->lastPen) == Qt::SolidLine) - drawLine_midpoint_i(x1, y1, x2, y2, - penBlend, &s->penData, LineDrawIncludeLastPixel, - devRect); - else - drawLine_midpoint_dashed_i(x1, y1, x2, y2, - &s->lastPen, - penBlend, &s->penData, LineDrawIncludeLastPixel, - devRect, &dashOffset); + QPaintEngineEx::stroke(vp, s->lastPen); } } } @@ -2506,7 +2177,7 @@ void QRasterPaintEngine::drawImage(const QPointF &p, const QImage &img) const QClipData *clip = d->clip(); QPointF pt(p.x() + s->matrix.dx(), p.y() + s->matrix.dy()); - if (s->flags.fast_images) { + if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) { SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()]; if (func) { if (!clip) { @@ -2690,7 +2361,7 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe bool exceedsPrecision = targetBounds.width() > 0xffff || targetBounds.height() > 0xffff; - if (s->flags.fast_images && !exceedsPrecision) { + if (!exceedsPrecision && d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) { if (s->matrix.type() > QTransform::TxScale) { SrcOverTransformFunc func = qTransformFunctions[d->rasterBuffer->format][img.format()]; if (func && (!clip || clip->hasRectClip)) { @@ -2725,12 +2396,18 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe d->image_filler_xform.setupMatrix(copy, s->flags.bilinear); if (!s->flags.antialiased && s->matrix.type() == QTransform::TxScale) { - QRectF rr = s->matrix.mapRect(r); + QPointF rr_tl = s->matrix.map(r.topLeft()); + QPointF rr_br = s->matrix.map(r.bottomRight()); + + int x1 = qRound(rr_tl.x()); + int y1 = qRound(rr_tl.y()); + int x2 = qRound(rr_br.x()); + int y2 = qRound(rr_br.y()); - const int x1 = qRound(rr.x()); - const int y1 = qRound(rr.y()); - const int x2 = qRound(rr.right()); - const int y2 = qRound(rr.bottom()); + if (x1 > x2) + qSwap(x1, x2); + if (y1 > y2) + qSwap(y1, y2); fillRect_normalized(QRect(x1, y1, x2-x1, y2-y1), &d->image_filler_xform, d); return; @@ -2765,8 +2442,7 @@ void QRasterPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRe fillPath(path, &d->image_filler_xform); s->matrix = m; } else { - - if (s->flags.fast_images) { + if (d->canUseFastImageBlending(d->rasterBuffer->compositionMode, img)) { SrcOverBlendFunc func = qBlendFunctions[d->rasterBuffer->format][img.format()]; if (func) { QPointF pt(r.x() + s->matrix.dx(), r.y() + s->matrix.dy()); @@ -3099,64 +2775,149 @@ void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx blend(current, spans, &s->penData); } -void QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, +bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, QFontEngine *fontEngine) { Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); + const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta); - QFontEngineGlyphCache::Type glyphType; - if (fontEngine->glyphFormat >= 0) { - glyphType = QFontEngineGlyphCache::Type(fontEngine->glyphFormat); - } else if (s->matrix.type() > QTransform::TxTranslate - && d->glyphCacheType == QFontEngineGlyphCache::Raster_RGBMask) { - glyphType = QFontEngineGlyphCache::Raster_A8; - } else { - glyphType = d->glyphCacheType; - } +#if !defined(QT_NO_FREETYPE) + if (fontEngine->type() == QFontEngine::Freetype) { + QFontEngineFT *fe = static_cast<QFontEngineFT *>(fontEngine); + const QFixed xOffs = fe->supportsSubPixelPositions() ? 0 : offs; + QFontEngineFT::GlyphFormat neededFormat = + painter()->device()->devType() == QInternal::Widget + ? fe->defaultGlyphFormat() + : QFontEngineFT::Format_A8; + + if (d_func()->mono_surface + || fe->isBitmapFont() // alphaPenBlt can handle mono, too + ) + neededFormat = QFontEngineFT::Format_Mono; + + if (neededFormat == QFontEngineFT::Format_None) + neededFormat = QFontEngineFT::Format_A8; + + QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs(); + if (s->matrix.type() >= QTransform::TxScale) { + if (s->matrix.isAffine()) + gset = fe->loadTransformedGlyphSet(s->matrix); + else + gset = 0; + } - QImageTextureGlyphCache *cache = - static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix)); - if (!cache) { - cache = new QImageTextureGlyphCache(glyphType, s->matrix); - fontEngine->setGlyphCache(0, cache); - } + if (!gset || gset->outline_drawing + || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat)) + return false; + + FT_Face lockedFace = 0; + + int depth; + switch (neededFormat) { + case QFontEngineFT::Format_Mono: + depth = 1; + break; + case QFontEngineFT::Format_A8: + depth = 8; + break; + case QFontEngineFT::Format_A32: + depth = 32; + break; + default: + Q_ASSERT(false); + depth = 0; + }; - cache->populate(fontEngine, numGlyphs, glyphs, positions); + for (int i = 0; i < numGlyphs; i++) { + QFixed spp = fe->subPixelPositionForX(positions[i].x); + QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp); - const QImage &image = cache->image(); - int bpl = image.bytesPerLine(); + if (!glyph || glyph->format != neededFormat) { + if (!lockedFace) + lockedFace = fe->lockFace(); + glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat); + } - int depth = image.depth(); - int rightShift = 0; - int leftShift = 0; - if (depth == 32) - leftShift = 2; // multiply by 4 - else if (depth == 1) - rightShift = 3; // divide by 8 + if (!glyph || !glyph->data) + continue; - int margin = cache->glyphMargin(); + int pitch; + switch (neededFormat) { + case QFontEngineFT::Format_Mono: + pitch = ((glyph->width + 31) & ~31) >> 3; + break; + case QFontEngineFT::Format_A8: + pitch = (glyph->width + 3) & ~3; + break; + case QFontEngineFT::Format_A32: + pitch = glyph->width * 4; + break; + default: + Q_ASSERT(false); + pitch = 0; + }; + + alphaPenBlt(glyph->data, pitch, depth, + qFloor(positions[i].x + xOffs) + glyph->x, + qFloor(positions[i].y + offs) - glyph->y, + glyph->width, glyph->height); + } + if (lockedFace) + fe->unlockFace(); + } else +#endif + { + QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 + ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) + : d->glyphCacheType; + + QImageTextureGlyphCache *cache = + static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphType, s->matrix)); + if (!cache) { + cache = new QImageTextureGlyphCache(glyphType, s->matrix); + fontEngine->setGlyphCache(0, cache); + } - const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta); + cache->populate(fontEngine, numGlyphs, glyphs, positions); + cache->fillInPendingGlyphs(); - const uchar *bits = image.bits(); - for (int i=0; i<numGlyphs; ++i) { - const QTextureGlyphCache::Coord &c = cache->coords.value(glyphs[i]); - int x = qFloor(positions[i].x + offs) + c.baseLineX - margin; - int y = qFloor(positions[i].y + offs) - c.baseLineY - margin; + const QImage &image = cache->image(); + int bpl = image.bytesPerLine(); -// printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n", -// c.x, c.y, -// c.w, c.h, -// c.baseLineX, c.baseLineY, -// glyphs[i], -// x, y, -// positions[i].x.toInt(), positions[i].y.toInt()); + int depth = image.depth(); + int rightShift = 0; + int leftShift = 0; + if (depth == 32) + leftShift = 2; // multiply by 4 + else if (depth == 1) + rightShift = 3; // divide by 8 - alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h); - } + int margin = cache->glyphMargin(); + const uchar *bits = image.bits(); + for (int i=0; i<numGlyphs; ++i) { - return; + QFixed subPixelPosition = cache->subPixelPositionForX(positions[i].x); + QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition); + const QTextureGlyphCache::Coord &c = cache->coords[glyph]; + if (c.isNull()) + continue; + + int x = qFloor(positions[i].x) + c.baseLineX - margin; + int y = qFloor(positions[i].y + offs) - c.baseLineY - margin; + + // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n", + // c.x, c.y, + // c.w, c.h, + // c.baseLineX, c.baseLineY, + // glyphs[i], + // x, y, + // positions[i].x.toInt(), positions[i].y.toInt()); + + alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h); + } + } + return true; } #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) @@ -3290,19 +3051,6 @@ QRasterPaintEnginePrivate::getBrushFunc(const QRectF &rect, } inline ProcessSpans -QRasterPaintEnginePrivate::getPenFunc(const QRect &rect, - const QSpanData *data) const -{ - Q_Q(const QRasterPaintEngine); - const QRasterPaintEngineState *s = q->state(); - - if (!s->flags.fast_pen && s->matrix.type() > QTransform::TxTranslate) - return data->blend; - const int penWidth = s->flags.fast_pen ? 1 : qCeil(s->pen.widthF()); - return isUnclipped(rect, penWidth) ? data->unclipped_blend : data->blend; -} - -inline ProcessSpans QRasterPaintEnginePrivate::getPenFunc(const QRectF &rect, const QSpanData *data) const { @@ -3323,8 +3071,13 @@ void QRasterPaintEngine::drawStaticTextItem(QStaticTextItem *textItem) ensurePen(); ensureState(); - drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions, - textItem->fontEngine()); + QFontEngine *fontEngine = textItem->fontEngine(); + if (!supportsTransformations(fontEngine)) { + drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions, + fontEngine); + } else { + QPaintEngineEx::drawStaticTextItem(textItem); + } } /*! @@ -3345,38 +3098,9 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte ensurePen(); ensureState(); -#if defined (Q_WS_WIN) || defined(Q_WS_MAC) - - bool drawCached = true; - - if (s->matrix.type() >= QTransform::TxProject) - drawCached = false; - - // don't try to cache huge fonts - const qreal pixelSize = ti.fontEngine->fontDef.pixelSize; - if (pixelSize * pixelSize * qAbs(s->matrix.determinant()) >= 64 * 64) - drawCached = false; - - // ### Remove the TestFontEngine and Box engine crap, in these - // ### cases we should delegate painting to the font engine - // ### directly... - -#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) - QFontEngine::Type fontEngineType = ti.fontEngine->type(); - // qDebug() << "type" << fontEngineType << s->matrix.type(); - if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) ti.fontEngine)->ttf && s->matrix.type() > QTransform::TxTranslate) - || (s->matrix.type() <= QTransform::TxTranslate - && (fontEngineType == QFontEngine::TestFontEngine - || fontEngineType == QFontEngine::Box))) { - drawCached = false; - } -#else - if (s->matrix.type() > QTransform::TxTranslate) - drawCached = false; -#endif - if (drawCached) { - QRasterPaintEngineState *s = state(); +#if defined (Q_WS_WIN) || defined(Q_WS_MAC) || (defined(Q_OS_MAC) && defined(Q_WS_QPA)) + if (!supportsTransformations(ti.fontEngine)) { QVarLengthArray<QFixedPoint> positions; QVarLengthArray<glyph_t> glyphs; @@ -3402,7 +3126,7 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte #if defined(Q_WS_QWS) if (fontEngine->type() == QFontEngine::Box) { - fontEngine->draw(this, qFloor(p.x() + aliasedCoordinateDelta), qFloor(p.y() + aliasedCoordinateDelta), ti); + fontEngine->draw(this, qFloor(p.x()), qFloor(p.y()), ti); return; } @@ -3416,6 +3140,34 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte } #endif // Q_WS_QWS +#ifdef Q_WS_QPA + if (s->matrix.type() < QTransform::TxScale) { + + QVarLengthArray<QFixedPoint> positions; + QVarLengthArray<glyph_t> glyphs; + QTransform matrix = state()->transform(); + + qreal _x = qFloor(p.x()); + qreal _y = qFloor(p.y()); + matrix.translate(_x, _y); + + fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); + if (glyphs.size() == 0) + return; + + for(int i = 0; i < glyphs.size(); i++) { + QImage img = fontEngine->alphaMapForGlyph(glyphs[i]); + glyph_metrics_t metrics = fontEngine->boundingBox(glyphs[i]); + // ### hm, perhaps an QFixed offs = QFixed::fromReal(aliasedCoordinateDelta) is needed here? + alphaPenBlt(img.bits(), img.bytesPerLine(), img.depth(), + qRound(positions[i].x + metrics.x), + qRound(positions[i].y + metrics.y), + img.width(), img.height()); + } + return; + } +#endif //Q_WS_QPA + #if (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE) #if defined(Q_WS_QWS) && !defined(QT_NO_QWS_QPF2) @@ -3442,92 +3194,10 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte if (glyphs.size() == 0) return; - // only use subpixel antialiasing when drawing to widgets - QFontEngineFT::GlyphFormat neededFormat = - painter()->device()->devType() == QInternal::Widget - ? fe->defaultGlyphFormat() - : QFontEngineFT::Format_A8; - - if (d_func()->mono_surface - || fe->isBitmapFont() // alphaPenBlt can handle mono, too - ) - neededFormat = QFontEngineFT::Format_Mono; - - if (neededFormat == QFontEngineFT::Format_None) - neededFormat = QFontEngineFT::Format_A8; - - QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs(); - if (s->matrix.type() >= QTransform::TxScale) { - if (s->matrix.isAffine()) - gset = fe->loadTransformedGlyphSet(s->matrix); - else - gset = 0; - - } - - if (!gset || gset->outline_drawing - || !fe->loadGlyphs(gset, glyphs.data(), glyphs.size(), neededFormat)) - { + if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine)) QPaintEngine::drawTextItem(p, ti); - return; - } - - QFixed offs = QFixed::fromReal(aliasedCoordinateDelta); - FT_Face lockedFace = 0; - int depth; - switch (neededFormat) { - case QFontEngineFT::Format_Mono: - depth = 1; - break; - case QFontEngineFT::Format_A8: - depth = 8; - break; - case QFontEngineFT::Format_A32: - depth = 32; - break; - default: - Q_ASSERT(false); - depth = 0; - }; - - for(int i = 0; i < glyphs.size(); i++) { - QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i]); - - if (!glyph || glyph->format != neededFormat) { - if (!lockedFace) - lockedFace = fe->lockFace(); - glyph = fe->loadGlyph(gset, glyphs[i], neededFormat); - } - - if (!glyph || !glyph->data) - continue; - - int pitch; - switch (neededFormat) { - case QFontEngineFT::Format_Mono: - pitch = ((glyph->width + 31) & ~31) >> 3; - break; - case QFontEngineFT::Format_A8: - pitch = (glyph->width + 3) & ~3; - break; - case QFontEngineFT::Format_A32: - pitch = glyph->width * 4; - break; - default: - Q_ASSERT(false); - pitch = 0; - }; - - alphaPenBlt(glyph->data, pitch, depth, - qFloor(positions[i].x + offs) + glyph->x, - qFloor(positions[i].y + offs) - glyph->y, - glyph->width, glyph->height); - } - if (lockedFace) - fe->unlockFace(); return; - #endif #endif @@ -3543,48 +3213,16 @@ void QRasterPaintEngine::drawPoints(const QPointF *points, int pointCount) QRasterPaintEngineState *s = state(); ensurePen(); - qreal pw = s->lastPen.widthF(); - if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) { - QPaintEngineEx::drawPoints(points, pointCount); - - } else { - if (!s->penData.blend) - return; - - QVarLengthArray<QT_FT_Span, 4096> array(pointCount); - QT_FT_Span span = { 0, 1, 0, 255 }; - const QPointF *end = points + pointCount; - qreal trans_x, trans_y; - int x, y; - int left = d->deviceRect.x(); - int right = left + d->deviceRect.width(); - int top = d->deviceRect.y(); - int bottom = top + d->deviceRect.height(); - int count = 0; - while (points < end) { - s->matrix.map(points->x(), points->y(), &trans_x, &trans_y); - x = qFloor(trans_x); - y = qFloor(trans_y); - if (x >= left && x < right && y >= top && y < bottom) { - if (count > 0) { - const QT_FT_Span &last = array[count - 1]; - // spans must be sorted on y (primary) and x (secondary) - if (y < last.y || (y == last.y && x < last.x)) { - s->penData.blend(count, array.constData(), &s->penData); - count = 0; - } - } - - span.x = x; - span.y = y; - array[count++] = span; - } - ++points; - } + if (!s->penData.blend) + return; - if (count > 0) - s->penData.blend(count, array.constData(), &s->penData); + if (!s->flags.fast_pen) { + QPaintEngineEx::drawPoints(points, pointCount); + return; } + + QCosmeticStroker stroker(s, d->deviceRect); + stroker.drawPoints(points, pointCount); } @@ -3594,48 +3232,16 @@ void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount) QRasterPaintEngineState *s = state(); ensurePen(); - double pw = s->lastPen.widthF(); - if (!s->flags.fast_pen && (s->matrix.type() > QTransform::TxTranslate || pw > 1)) { - QPaintEngineEx::drawPoints(points, pointCount); - - } else { - if (!s->penData.blend) - return; - - QVarLengthArray<QT_FT_Span, 4096> array(pointCount); - QT_FT_Span span = { 0, 1, 0, 255 }; - const QPoint *end = points + pointCount; - qreal trans_x, trans_y; - int x, y; - int left = d->deviceRect.x(); - int right = left + d->deviceRect.width(); - int top = d->deviceRect.y(); - int bottom = top + d->deviceRect.height(); - int count = 0; - while (points < end) { - s->matrix.map(points->x(), points->y(), &trans_x, &trans_y); - x = qFloor(trans_x); - y = qFloor(trans_y); - if (x >= left && x < right && y >= top && y < bottom) { - if (count > 0) { - const QT_FT_Span &last = array[count - 1]; - // spans must be sorted on y (primary) and x (secondary) - if (y < last.y || (y == last.y && x < last.x)) { - s->penData.blend(count, array.constData(), &s->penData); - count = 0; - } - } - - span.x = x; - span.y = y; - array[count++] = span; - } - ++points; - } + if (!s->penData.blend) + return; - if (count > 0) - s->penData.blend(count, array.constData(), &s->penData); + if (!s->flags.fast_pen) { + QPaintEngineEx::drawPoints(points, pointCount); + return; } + + QCosmeticStroker stroker(s, d->deviceRect); + stroker.drawPoints(points, pointCount); } /*! @@ -3644,59 +3250,22 @@ void QRasterPaintEngine::drawPoints(const QPoint *points, int pointCount) void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount) { #ifdef QT_DEBUG_DRAW - qDebug() << " - QRasterPaintEngine::drawLine()"; + qDebug() << " - QRasterPaintEngine::drawLines(QLine*)" << lineCount; #endif Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); ensurePen(); + if (!s->penData.blend) + return; + if (s->flags.fast_pen) { - QIntRect bounds; bounds.set(d->deviceRect); - LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap - ? LineDrawNormal - : LineDrawIncludeLastPixel; - - int m11 = int(s->matrix.m11()); - int m22 = int(s->matrix.m22()); - int dx = qFloor(s->matrix.dx() + aliasedCoordinateDelta); - int dy = qFloor(s->matrix.dy() + aliasedCoordinateDelta); + QCosmeticStroker stroker(s, d->deviceRect); for (int i=0; i<lineCount; ++i) { - int dashOffset = int(s->lastPen.dashOffset()); - if (s->flags.int_xform) { - const QLine &l = lines[i]; - int x1 = l.x1() * m11 + dx; - int y1 = l.y1() * m22 + dy; - int x2 = l.x2() * m11 + dx; - int y2 = l.y2() * m22 + dy; - - const QRect brect(QPoint(x1, y1), QPoint(x2, y2)); - ProcessSpans penBlend = d->getPenFunc(brect, &s->penData); - if (qpen_style(s->lastPen) == Qt::SolidLine) - drawLine_midpoint_i(x1, y1, x2, y2, - penBlend, &s->penData, mode, bounds); - else - drawLine_midpoint_dashed_i(x1, y1, x2, y2, - &s->lastPen, penBlend, - &s->penData, mode, bounds, - &dashOffset); - } else { - QLineF line = lines[i] * s->matrix; - const QRectF brect(QPointF(line.x1(), line.y1()), - QPointF(line.x2(), line.y2())); - ProcessSpans penBlend = d->getPenFunc(brect, &s->penData); - if (qpen_style(s->lastPen) == Qt::SolidLine) - drawLine_midpoint_i(int(line.x1()), int(line.y1()), - int(line.x2()), int(line.y2()), - penBlend, &s->penData, mode, bounds); - else - drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()), - int(line.x2()), int(line.y2()), - &s->lastPen, penBlend, - &s->penData, mode, bounds, - &dashOffset); - } + const QLine &l = lines[i]; + stroker.drawLine(l.p1(), l.p2()); } - } else if (s->penData.blend) { + } else { QPaintEngineEx::drawLines(lines, lineCount); } } @@ -3753,7 +3322,7 @@ void QRasterPaintEnginePrivate::rasterizeLine_dashed(QLineF line, void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount) { #ifdef QT_DEBUG_DRAW - qDebug() << " - QRasterPaintEngine::drawLine()"; + qDebug() << " - QRasterPaintEngine::drawLines(QLineF *)" << lineCount; #endif Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); @@ -3762,28 +3331,10 @@ void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount) if (!s->penData.blend) return; if (s->flags.fast_pen) { - QIntRect bounds; - bounds.set(d->deviceRect); - LineDrawMode mode = s->lastPen.capStyle() == Qt::FlatCap - ? LineDrawNormal - : LineDrawIncludeLastPixel; - + QCosmeticStroker stroker(s, d->deviceRect); for (int i=0; i<lineCount; ++i) { - int dashOffset = int(s->lastPen.dashOffset()); - QLineF line = (lines[i] * s->matrix).translated(aliasedCoordinateDelta, aliasedCoordinateDelta); - const QRectF brect(QPointF(line.x1(), line.y1()), - QPointF(line.x2(), line.y2())); - ProcessSpans penBlend = d->getPenFunc(brect, &s->penData); - if (qpen_style(s->lastPen) == Qt::SolidLine) - drawLine_midpoint_i(int(line.x1()), int(line.y1()), - int(line.x2()), int(line.y2()), - penBlend, &s->penData, mode, bounds); - else - drawLine_midpoint_dashed_i(int(line.x1()), int(line.y1()), - int(line.x2()), int(line.y2()), - &s->lastPen, - penBlend, &s->penData, mode, - bounds, &dashOffset); + QLineF line = lines[i]; + stroker.drawLine(line.p1(), line.p2()); } } else { QPaintEngineEx::drawLines(lines, lineCount); @@ -3801,7 +3352,8 @@ void QRasterPaintEngine::drawEllipse(const QRectF &rect) ensurePen(); if (((qpen_style(s->lastPen) == Qt::SolidLine && s->flags.fast_pen) - || (qpen_style(s->lastPen) == Qt::NoPen && !s->flags.antialiased)) + || (qpen_style(s->lastPen) == Qt::NoPen)) + && !s->flags.antialiased && qMax(rect.width(), rect.height()) < QT_RASTER_COORD_LIMIT && !rect.isEmpty() && s->matrix.type() <= QTransform::TxScale) // no shear @@ -3869,6 +3421,37 @@ void QRasterPaintEngine::releaseDC(HDC) const #endif +bool QRasterPaintEngine::supportsTransformations(const QFontEngine *fontEngine) const +{ + const QTransform &m = state()->matrix; +#if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) + QFontEngine::Type fontEngineType = fontEngine->type(); + if ((fontEngineType == QFontEngine::Win && !((QFontEngineWin *) fontEngine)->ttf && m.type() > QTransform::TxTranslate) + || (m.type() <= QTransform::TxTranslate + && (fontEngineType == QFontEngine::TestFontEngine + || fontEngineType == QFontEngine::Box))) { + return true; + } +#endif + return supportsTransformations(fontEngine->fontDef.pixelSize, m); +} + +bool QRasterPaintEngine::supportsTransformations(qreal pixelSize, const QTransform &m) const +{ +#if defined(Q_WS_MAC) || (defined(Q_OS_MAC) && defined(Q_WS_QPA)) + // Mac font engines don't support scaling and rotation + if (m.type() > QTransform::TxTranslate) +#else + if (m.type() >= QTransform::TxProject) +#endif + return true; + + if (pixelSize * pixelSize * qAbs(m.determinant()) >= 64 * 64) + return true; + + return false; +} + /*! \internal */ @@ -3969,7 +3552,7 @@ void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSp spans[n].y = y; spans[n].coverage = 255; int len = 1; - while (src_x < w-1 && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) { + while (src_x+1 < w && src[(src_x+1) >> 3] & (0x1 << ((src_x+1) & 7))) { ++src_x; ++len; } @@ -3995,7 +3578,7 @@ void QRasterPaintEngine::drawBitmap(const QPointF &pos, const QImage &image, QSp spans[n].y = y; spans[n].coverage = 255; int len = 1; - while (src_x < w-1 && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) { + while (src_x+1 < w && src[(src_x+1) >> 3] & (0x80 >> ((src_x+1) & 7))) { ++src_x; ++len; } @@ -4181,6 +3764,11 @@ extern "C" { int q_gray_rendered_spans(QT_FT_Raster raster); } +static inline uchar *alignAddress(uchar *address, quintptr alignmentMask) +{ + return (uchar *)(((quintptr)address + alignmentMask) & ~alignmentMask); +} + void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline, ProcessSpans callback, void *userData, QRasterBuffer *) @@ -4208,19 +3796,10 @@ void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline, // minimize memory reallocations. However if initial size for // raster pool is changed for lower value, reallocations will // occur normally. - const int rasterPoolInitialSize = MINIMUM_POOL_SIZE; - int rasterPoolSize = rasterPoolInitialSize; - unsigned char *rasterPoolBase; -#if defined(Q_WS_WIN64) - rasterPoolBase = - // We make use of setjmp and longjmp in qgrayraster.c which requires - // 16-byte alignment, hence we hardcode this requirement here.. - (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2); -#else - unsigned char rasterPoolOnStack[rasterPoolInitialSize]; - rasterPoolBase = rasterPoolOnStack; -#endif - Q_CHECK_PTR(rasterPoolBase); + int rasterPoolSize = MINIMUM_POOL_SIZE; + uchar rasterPoolOnStack[MINIMUM_POOL_SIZE + 0xf]; + uchar *rasterPoolBase = alignAddress(rasterPoolOnStack, 0xf); + uchar *rasterPoolOnHeap = 0; qt_ft_grays_raster.raster_reset(*grayRaster.data(), rasterPoolBase, rasterPoolSize); @@ -4256,31 +3835,20 @@ void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline, // Out of memory, reallocate some more and try again... if (error == -6) { // ErrRaster_OutOfMemory from qgrayraster.c - int new_size = rasterPoolSize * 2; - if (new_size > 1024 * 1024) { + rasterPoolSize *= 2; + if (rasterPoolSize > 1024 * 1024) { qWarning("QPainter: Rasterization of primitive failed"); break; } rendered_spans += q_gray_rendered_spans(*grayRaster.data()); -#if defined(Q_WS_WIN64) - _aligned_free(rasterPoolBase); -#else - if (rasterPoolBase != rasterPoolOnStack) // initially on the stack - free(rasterPoolBase); -#endif + free(rasterPoolOnHeap); + rasterPoolOnHeap = (uchar *)malloc(rasterPoolSize + 0xf); - rasterPoolSize = new_size; - rasterPoolBase = -#if defined(Q_WS_WIN64) - // We make use of setjmp and longjmp in qgrayraster.c which requires - // 16-byte alignment, hence we hardcode this requirement here.. - (unsigned char *) _aligned_malloc(rasterPoolSize, sizeof(void*) * 2); -#else - (unsigned char *) malloc(rasterPoolSize); -#endif - Q_CHECK_PTR(rasterPoolBase); // note: we just freed the old rasterPoolBase. I hope it's not fatal. + Q_CHECK_PTR(rasterPoolOnHeap); // note: we just freed the old rasterPoolBase. I hope it's not fatal. + + rasterPoolBase = alignAddress(rasterPoolOnHeap, 0xf); qt_ft_grays_raster.raster_done(*grayRaster.data()); qt_ft_grays_raster.raster_new(grayRaster.data()); @@ -4290,12 +3858,7 @@ void QRasterPaintEnginePrivate::rasterize(QT_FT_Outline *outline, } } -#if defined(Q_WS_WIN64) - _aligned_free(rasterPoolBase); -#else - if (rasterPoolBase != rasterPoolOnStack) // initially on the stack - free(rasterPoolBase); -#endif + free(rasterPoolOnHeap); } void QRasterPaintEnginePrivate::recalculateFastImages() @@ -4304,11 +3867,19 @@ void QRasterPaintEnginePrivate::recalculateFastImages() QRasterPaintEngineState *s = q->state(); s->flags.fast_images = !(s->renderHints & QPainter::SmoothPixmapTransform) - && rasterBuffer->compositionMode == QPainter::CompositionMode_SourceOver && s->matrix.type() <= QTransform::TxShear; } +bool QRasterPaintEnginePrivate::canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const +{ + Q_Q(const QRasterPaintEngine); + const QRasterPaintEngineState *s = q->state(); + return s->flags.fast_images + && (mode == QPainter::CompositionMode_SourceOver + || (mode == QPainter::CompositionMode_Source + && !image.hasAlphaChannel())); +} QImage QRasterBuffer::colorizeBitmap(const QImage &image, const QColor &color) { @@ -4635,38 +4206,40 @@ void QClipData::fixup() return; } -// qDebug("QClipData::fixup: count=%d",count); int y = -1; ymin = m_spans[0].y; ymax = m_spans[count-1].y + 1; xmin = INT_MAX; xmax = 0; + const int firstLeft = m_spans[0].x; + const int firstRight = m_spans[0].x + m_spans[0].len; bool isRect = true; - int left = m_spans[0].x; - int right = m_spans[0].x + m_spans[0].len; for (int i = 0; i < count; ++i) { - if (m_spans[i].y != y) { - if (m_spans[i].y != y + 1 && y != -1) { + QT_FT_Span_& span = m_spans[i]; + + if (span.y != y) { + if (span.y != y + 1 && y != -1) isRect = false; - } - y = m_spans[i].y; - m_clipLines[y].spans = m_spans+i; - m_clipLines[y].count = 0; -// qDebug() << " new line: y=" << y; - } - ++m_clipLines[y].count; - int sl = (int) m_spans[i].x; - int sr = sl + m_spans[i].len; + y = span.y; + m_clipLines[y].spans = &span; + m_clipLines[y].count = 1; + } else + ++m_clipLines[y].count; + + const int spanLeft = span.x; + const int spanRight = spanLeft + span.len; - xmin = qMin(xmin, (int)m_spans[i].x); - xmax = qMax(xmax, (int)m_spans[i].x + m_spans[i].len); + if (spanLeft < xmin) + xmin = spanLeft; - if (sl != left || sr != right) + if (spanRight > xmax) + xmax = spanRight; + + if (spanLeft != firstLeft || spanRight != firstRight) isRect = false; } -// qDebug("xmin=%d,xmax=%d,ymin=%d,ymax=%d %s", xmin, xmax, ymin, ymax, isRect ? "rectangular" : ""); if (isRect) { hasRectClip = true; @@ -4810,7 +4383,7 @@ static void qt_span_fill_clipped(int spanCount, const QSpan *spans, void *userDa while (spans < end) { QSpan *clipped = cspans; spans = qt_intersect_spans(fillData->clip, ¤tClip, spans, end, &clipped, NSPANS); -// qDebug() << "processed " << processed << "clipped" << clipped-cspans +// qDebug() << "processed " << spanCount - (end - spans) << "clipped" << clipped-cspans // << "span:" << cspans->x << cspans->y << cspans->len << spans->coverage; if (clipped - cspans) @@ -5002,8 +4575,8 @@ protected: int size, int opacity) const; uint *addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) { if (cache.size() == maxCacheSize()) { - int elem_to_remove = qrand() % maxCacheSize(); - cache.remove(cache.keys()[elem_to_remove]); // may remove more than 1, but OK + // may remove more than 1, but OK + cache.erase(cache.begin() + (qrand() % maxCacheSize())); } CacheInfo cache_entry(gradient.stops(), opacity, gradient.interpolationMode()); generateGradientColorTable(gradient, cache_entry.buffer, paletteSize(), opacity); @@ -5022,6 +4595,84 @@ void QGradientCache::generateGradientColorTable(const QGradient& gradient, uint bool colorInterpolation = (gradient.interpolationMode() == QGradient::ColorInterpolation); + if (stopCount == 2) { + uint first_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity); + uint second_color = ARGB_COMBINE_ALPHA(stops[1].second.rgba(), opacity); + + qreal first_stop = stops[0].first; + qreal second_stop = stops[1].first; + + if (second_stop < first_stop) { + qSwap(first_color, second_color); + qSwap(first_stop, second_stop); + } + + if (colorInterpolation) { + first_color = PREMUL(first_color); + second_color = PREMUL(second_color); + } + + int first_index = qRound(first_stop * (GRADIENT_STOPTABLE_SIZE-1)); + int second_index = qRound(second_stop * (GRADIENT_STOPTABLE_SIZE-1)); + + uint red_first = qRed(first_color) << 16; + uint green_first = qGreen(first_color) << 16; + uint blue_first = qBlue(first_color) << 16; + uint alpha_first = qAlpha(first_color) << 16; + + uint red_second = qRed(second_color) << 16; + uint green_second = qGreen(second_color) << 16; + uint blue_second = qBlue(second_color) << 16; + uint alpha_second = qAlpha(second_color) << 16; + + int i = 0; + for (; i <= qMin(GRADIENT_STOPTABLE_SIZE, first_index); ++i) { + if (colorInterpolation) + colorTable[i] = first_color; + else + colorTable[i] = PREMUL(first_color); + } + + if (i < second_index) { + qreal reciprocal = qreal(1) / (second_index - first_index); + + int red_delta = qRound(int(red_second - red_first) * reciprocal); + int green_delta = qRound(int(green_second - green_first) * reciprocal); + int blue_delta = qRound(int(blue_second - blue_first) * reciprocal); + int alpha_delta = qRound(int(alpha_second - alpha_first) * reciprocal); + + // rounding + red_first += 1 << 15; + green_first += 1 << 15; + blue_first += 1 << 15; + alpha_first += 1 << 15; + + for (; i < qMin(GRADIENT_STOPTABLE_SIZE, second_index); ++i) { + red_first += red_delta; + green_first += green_delta; + blue_first += blue_delta; + alpha_first += alpha_delta; + + const uint color = ((alpha_first << 8) & 0xff000000) | (red_first & 0xff0000) + | ((green_first >> 8) & 0xff00) | (blue_first >> 16); + + if (colorInterpolation) + colorTable[i] = color; + else + colorTable[i] = PREMUL(color); + } + } + + for (; i < GRADIENT_STOPTABLE_SIZE; ++i) { + if (colorInterpolation) + colorTable[i] = second_color; + else + colorTable[i] = PREMUL(second_color); + } + + return; + } + uint current_color = ARGB_COMBINE_ALPHA(stops[0].second.rgba(), opacity); if (stopCount == 1) { current_color = PREMUL(current_color); @@ -5154,7 +4805,8 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode case Qt::SolidPattern: { type = Solid; QColor c = qbrush_color(brush); - solid.color = PREMUL(ARGB_COMBINE_ALPHA(c.rgba(), alpha)); + QRgb rgba = c.rgba(); + solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha)); if ((solid.color & 0xff000000) == 0 && compositionMode == QPainter::CompositionMode_SourceOver) { type = None; @@ -5192,10 +4844,11 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode QPointF center = g->center(); radialData.center.x = center.x(); radialData.center.y = center.y(); + radialData.center.radius = g->centerRadius(); QPointF focal = g->focalPoint(); radialData.focal.x = focal.x(); radialData.focal.y = focal.y(); - radialData.radius = g->radius(); + radialData.focal.radius = g->focalRadius(); } break; @@ -5382,759 +5035,6 @@ void QSpanData::initTexture(const QImage *image, int alpha, QTextureData::Type _ adjustSpanMethods(); } -#ifdef Q_WS_WIN - - -#endif - - -/*! - \internal - - Draws a line using the floating point midpoint algorithm. The line - \a line is already in device coords at this point. -*/ - -static void drawLine_midpoint_i(int x1, int y1, int x2, int y2, ProcessSpans span_func, QSpanData *data, - LineDrawMode style, const QIntRect &devRect) -{ -#ifdef QT_DEBUG_DRAW - qDebug() << " - drawLine_midpoint_i" << QLine(QPoint(x1, y1), QPoint(x2, y2)); -#endif - - int x, y; - int dx, dy, d, incrE, incrNE; - - dx = x2 - x1; - dy = y2 - y1; - - const int NSPANS = 256; - QT_FT_Span spans[NSPANS]; - int current = 0; - bool ordered = true; - - if (dy == 0) { - // specialcase horizontal lines - if (y1 >= devRect.y1 && y1 < devRect.y2) { - int start = qMax(devRect.x1, qMin(x1, x2)); - int stop = qMax(x1, x2) + 1; - int stop_clipped = qMin(devRect.x2, stop); - int len = stop_clipped - start; - if (style == LineDrawNormal && stop == stop_clipped) - len--; - if (len > 0) { - spans[0].x = ushort(start); - spans[0].len = ushort(len); - spans[0].y = y1; - spans[0].coverage = 255; - span_func(1, spans, data); - } - } - return; - } else if (dx == 0) { - // specialcase vertical lines - if (x1 >= devRect.x1 && x1 < devRect.x2) { - int start = qMax(devRect.y1, qMin(y1, y2)); - int stop = qMax(y1, y2) + 1; - int stop_clipped = qMin(devRect.y2, stop); - int len = stop_clipped - start; - if (style == LineDrawNormal && stop == stop_clipped) - len--; - // hw: create spans directly instead to possibly avoid clipping - if (len > 0) - fillRect_normalized(QRect(x1, start, 1, len).normalized(), data, 0); - } - return; - } - - - if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */ - - if (x2 < x1) { /* if coordinates are out of order */ - qt_swap_int(x1, x2); - dx = -dx; - - qt_swap_int(y1, y2); - dy = -dy; - } - - int x_lower_limit = - 128; - if (x1 < x_lower_limit) { - int cy = dy * (x_lower_limit - x1) / dx + y1; - drawLine_midpoint_i(x_lower_limit, cy, x2, y2, span_func, data, style, devRect); - return; - } - - if (style == LineDrawNormal) - --x2; - - // In the loops below we increment before call the span function so - // we need to stop one pixel before - x2 = qMin(x2, devRect.x2 - 1); - - // completely clipped, so abort - if (x2 <= x1) { - return; - } - - int x = x1; - int y = y1; - - if (y2 <= y1) - ordered = false; - - { - const int index = (ordered ? current : NSPANS - 1 - current); - spans[index].coverage = 255; - spans[index].x = x; - spans[index].y = y; - - if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2) - spans[index].len = 1; - else - spans[index].len = 0; - } - - if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees) - y2 = qMin(y2, devRect.y2 - 1); - - incrE = dy * 2; - d = incrE - dx; - incrNE = (dy - dx) * 2; - - if (y > y2) - goto flush_and_return; - - while (x < x2) { - ++x; - if (d > 0) { - if (spans[current].len > 0) - ++current; - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - - ++y; - d += incrNE; - if (y > y2) - goto flush_and_return; - - spans[current].len = 0; - spans[current].coverage = 255; - spans[current].x = x; - spans[current].y = y; - } else { - d += incrE; - if (x == devRect.x1) - spans[current].x = devRect.x1; - } - - if (x < devRect.x1 || y < devRect.y1) - continue; - - Q_ASSERT(x<devRect.x2); - Q_ASSERT(y<devRect.y2); - Q_ASSERT(spans[current].y == y); - spans[current].len++; - } - if (spans[current].len > 0) { - ++current; - } - } else { // 0-45 and 180->225 (unit circle degrees) - - y1 = qMin(y1, devRect.y2 - 1); - - incrE = dy * 2; - d = incrE + dx; - incrNE = (dy + dx) * 2; - - if (y < devRect.y1) - goto flush_and_return; - - while (x < x2) { - ++x; - if (d < 0) { - if (spans[NSPANS - 1 - current].len > 0) - ++current; - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - - --y; - d += incrNE; - if (y < devRect.y1) - goto flush_and_return; - - const int index = NSPANS - 1 - current; - spans[index].len = 0; - spans[index].coverage = 255; - spans[index].x = x; - spans[index].y = y; - } else { - d += incrE; - if (x == devRect.x1) - spans[NSPANS - 1 - current].x = devRect.x1; - } - - if (x < devRect.x1 || y > y1) - continue; - - Q_ASSERT(x<devRect.x2 && y<devRect.y2); - Q_ASSERT(spans[NSPANS - 1 - current].y == y); - spans[NSPANS - 1 - current].len++; - } - if (spans[NSPANS - 1 - current].len > 0) { - ++current; - } - } - - } else { - - // if y is the major axis: - - if (y2 < y1) { /* if coordinates are out of order */ - qt_swap_int(y1, y2); - dy = -dy; - - qt_swap_int(x1, x2); - dx = -dx; - } - - int y_lower_limit = - 128; - if (y1 < y_lower_limit) { - int cx = dx * (y_lower_limit - y1) / dy + x1; - drawLine_midpoint_i(cx, y_lower_limit, x2, y2, span_func, data, style, devRect); - return; - } - - if (style == LineDrawNormal) - --y2; - - // In the loops below we increment before call the span function so - // we need to stop one pixel before - y2 = qMin(y2, devRect.y2 - 1); - - // completely clipped, so abort - if (y2 <= y1) { - return; - } - - x = x1; - y = y1; - - if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) { - Q_ASSERT(x >= devRect.x1 && y >= devRect.y1 && x < devRect.x2 && y < devRect.y2); - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - spans[current].len = 1; - spans[current].coverage = 255; - spans[current].x = x; - spans[current].y = y; - ++current; - } - - if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees) - x2 = qMin(x2, devRect.x2 - 1); - incrE = dx * 2; - d = incrE - dy; - incrNE = (dx - dy) * 2; - - if (x > x2) - goto flush_and_return; - - while (y < y2) { - if (d > 0) { - ++x; - d += incrNE; - if (x > x2) - goto flush_and_return; - } else { - d += incrE; - } - ++y; - if (x < devRect.x1 || y < devRect.y1) - continue; - Q_ASSERT(x<devRect.x2 && y<devRect.y2); - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - spans[current].len = 1; - spans[current].coverage = 255; - spans[current].x = x; - spans[current].y = y; - ++current; - } - } else { // 45 -> 90 and 225 -> 270 (unit circle degrees) - x1 = qMin(x1, devRect.x2 - 1); - incrE = dx * 2; - d = incrE + dy; - incrNE = (dx + dy) * 2; - - if (x < devRect.x1) - goto flush_and_return; - - while (y < y2) { - if (d < 0) { - --x; - d += incrNE; - if (x < devRect.x1) - goto flush_and_return; - } else { - d += incrE; - } - ++y; - if (y < devRect.y1 || x > x1) - continue; - Q_ASSERT(x>=devRect.x1 && x<devRect.x2 && y>=devRect.y1 && y<devRect.y2); - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - spans[current].len = 1; - spans[current].coverage = 255; - spans[current].x = x; - spans[current].y = y; - ++current; - } - } - } -flush_and_return: - if (current > 0) - span_func(current, ordered ? spans : spans + (NSPANS - current), data); -} - -static void offset_pattern(int offset, bool *inDash, int *dashIndex, int *currentOffset, const QVarLengthArray<qreal> &pattern) -{ - while (offset--) { - if (--*currentOffset == 0) { - *inDash = !*inDash; - *dashIndex = ((*dashIndex + 1) % pattern.size()); - *currentOffset = int(pattern[*dashIndex]); - } - } -} - -static void drawLine_midpoint_dashed_i(int x1, int y1, int x2, int y2, - QPen *pen, - ProcessSpans span_func, QSpanData *data, - LineDrawMode style, const QIntRect &devRect, - int *patternOffset) -{ -#ifdef QT_DEBUG_DRAW - qDebug() << " - drawLine_midpoint_dashed_i" << x1 << y1 << x2 << y2 << *patternOffset; -#endif - - int x, y; - int dx, dy, d, incrE, incrNE; - - dx = x2 - x1; - dy = y2 - y1; - - Q_ASSERT(*patternOffset >= 0); - - const QVector<qreal> penPattern = pen->dashPattern(); - QVarLengthArray<qreal> pattern(penPattern.size()); - - int patternLength = 0; - for (int i = 0; i < penPattern.size(); ++i) - patternLength += qMax<qreal>(1.0, (penPattern.at(i))); - - // pattern must be reversed if coordinates are out of order - int reverseLength = -1; - if (dy == 0 && x1 > x2) - reverseLength = x1 - x2; - else if (dx == 0 && y1 > y2) - reverseLength = y1 - y2; - else if (qAbs(dx) >= qAbs(dy) && x2 < x1) // x major axis - reverseLength = qAbs(dx); - else if (qAbs(dy) >= qAbs(dx) && y2 < y1) // y major axis - reverseLength = qAbs(dy); - - const bool reversed = (reverseLength > -1); - if (reversed) { // reverse pattern - for (int i = 0; i < penPattern.size(); ++i) - pattern[penPattern.size() - 1 - i] = qMax<qreal>(1.0, penPattern.at(i)); - - *patternOffset = (patternLength - 1 - *patternOffset); - *patternOffset += patternLength - (reverseLength % patternLength); - *patternOffset = *patternOffset % patternLength; - } else { - for (int i = 0; i < penPattern.size(); ++i) - pattern[i] = qMax<qreal>(1.0, penPattern.at(i)); - } - - int dashIndex = 0; - bool inDash = !reversed; - int currPattern = int(pattern[dashIndex]); - - // adjust pattern for offset - offset_pattern(*patternOffset, &inDash, &dashIndex, &currPattern, pattern); - - const int NSPANS = 256; - QT_FT_Span spans[NSPANS]; - int current = 0; - bool ordered = true; - - if (dy == 0) { - // specialcase horizontal lines - if (y1 >= devRect.y1 && y1 < devRect.y2) { - int start_unclipped = qMin(x1, x2); - int start = qMax(devRect.x1, start_unclipped); - int stop = qMax(x1, x2) + 1; - int stop_clipped = qMin(devRect.x2, stop); - int len = stop_clipped - start; - if (style == LineDrawNormal && stop == stop_clipped) - len--; - - // adjust pattern for starting offset - offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern); - - if (len > 0) { - int x = start; - while (x < stop_clipped) { - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - const int dash = qMin(currPattern, stop_clipped - x); - if (inDash) { - spans[current].x = ushort(x); - spans[current].len = ushort(dash); - spans[current].y = y1; - spans[current].coverage = 255; - ++current; - } - if (dash < currPattern) { - currPattern -= dash; - } else { - dashIndex = (dashIndex + 1) % pattern.size(); - currPattern = int(pattern[dashIndex]); - inDash = !inDash; - } - x += dash; - } - } - } - goto flush_and_return; - } else if (dx == 0) { - if (x1 >= devRect.x1 && x1 < devRect.x2) { - int start_unclipped = qMin(y1, y2); - int start = qMax(devRect.y1, start_unclipped); - int stop = qMax(y1, y2) + 1; - int stop_clipped = qMin(devRect.y2, stop); - if (style == LineDrawNormal && stop == stop_clipped) - --stop; - else - stop = stop_clipped; - - // adjust pattern for starting offset - offset_pattern(start - start_unclipped, &inDash, &dashIndex, &currPattern, pattern); - - // loop over dashes - int y = start; - while (y < stop) { - const int dash = qMin(currPattern, stop - y); - if (inDash) { - for (int i = 0; i < dash; ++i) { - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - spans[current].x = x1; - spans[current].len = 1; - spans[current].coverage = 255; - spans[current].y = ushort(y + i); - ++current; - } - } - if (dash < currPattern) { - currPattern -= dash; - } else { - dashIndex = (dashIndex + 1) % pattern.size(); - currPattern = int(pattern[dashIndex]); - inDash = !inDash; - } - y += dash; - } - } - goto flush_and_return; - } - - if (qAbs(dx) >= qAbs(dy)) { /* if x is the major axis: */ - - if (x2 < x1) { /* if coordinates are out of order */ - qt_swap_int(x1, x2); - dx = -dx; - - qt_swap_int(y1, y2); - dy = -dy; - } - - if (style == LineDrawNormal) - --x2; - - // In the loops below we increment before call the span function so - // we need to stop one pixel before - x2 = qMin(x2, devRect.x2 - 1); - - // completely clipped, so abort - if (x2 <= x1) - goto flush_and_return; - - int x = x1; - int y = y1; - - if (x >= devRect.x1 && y >= devRect.y1 && y < devRect.y2) { - Q_ASSERT(x < devRect.x2); - if (inDash) { - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - spans[current].len = 1; - spans[current].coverage = 255; - spans[current].x = x; - spans[current].y = y; - ++current; - } - if (--currPattern <= 0) { - inDash = !inDash; - dashIndex = (dashIndex + 1) % pattern.size(); - currPattern = int(pattern[dashIndex]); - } - } - - if (y2 > y1) { // 315 -> 360 and 135 -> 180 (unit circle degrees) - y2 = qMin(y2, devRect.y2 - 1); - - incrE = dy * 2; - d = incrE - dx; - incrNE = (dy - dx) * 2; - - if (y > y2) - goto flush_and_return; - - while (x < x2) { - if (d > 0) { - ++y; - d += incrNE; - if (y > y2) - goto flush_and_return; - } else { - d += incrE; - } - ++x; - - const bool skip = x < devRect.x1 || y < devRect.y1; - Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2)); - if (inDash && !skip) { - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - spans[current].len = 1; - spans[current].coverage = 255; - spans[current].x = x; - spans[current].y = y; - ++current; - } - if (--currPattern <= 0) { - inDash = !inDash; - dashIndex = (dashIndex + 1) % pattern.size(); - currPattern = int(pattern[dashIndex]); - } - } - } else { // 0-45 and 180->225 (unit circle degrees) - y1 = qMin(y1, devRect.y2 - 1); - - incrE = dy * 2; - d = incrE + dx; - incrNE = (dy + dx) * 2; - - if (y < devRect.y1) - goto flush_and_return; - - while (x < x2) { - if (d < 0) { - if (current > 0) { - span_func(current, spans, data); - current = 0; - } - - --y; - d += incrNE; - if (y < devRect.y1) - goto flush_and_return; - } else { - d += incrE; - } - ++x; - - const bool skip = x < devRect.x1 || y > y1; - Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2)); - if (inDash && !skip) { - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - spans[current].len = 1; - spans[current].coverage = 255; - spans[current].x = x; - spans[current].y = y; - ++current; - } - if (--currPattern <= 0) { - inDash = !inDash; - dashIndex = (dashIndex + 1) % pattern.size(); - currPattern = int(pattern[dashIndex]); - } - } - } - } else { - - // if y is the major axis: - - if (y2 < y1) { /* if coordinates are out of order */ - qt_swap_int(y1, y2); - dy = -dy; - - qt_swap_int(x1, x2); - dx = -dx; - } - - if (style == LineDrawNormal) - --y2; - - // In the loops below we increment before call the span function so - // we need to stop one pixel before - y2 = qMin(y2, devRect.y2 - 1); - - // completely clipped, so abort - if (y2 <= y1) - goto flush_and_return; - - x = x1; - y = y1; - - if (x>=devRect.x1 && y>=devRect.y1 && x < devRect.x2) { - Q_ASSERT(x < devRect.x2); - if (inDash) { - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - spans[current].len = 1; - spans[current].coverage = 255; - spans[current].x = x; - spans[current].y = y; - ++current; - } - if (--currPattern <= 0) { - inDash = !inDash; - dashIndex = (dashIndex + 1) % pattern.size(); - currPattern = int(pattern[dashIndex]); - } - } - - if (x2 > x1) { // 90 -> 135 and 270 -> 315 (unit circle degrees) - x2 = qMin(x2, devRect.x2 - 1); - incrE = dx * 2; - d = incrE - dy; - incrNE = (dx - dy) * 2; - - if (x > x2) - goto flush_and_return; - - while (y < y2) { - if (d > 0) { - ++x; - d += incrNE; - if (x > x2) - goto flush_and_return; - } else { - d += incrE; - } - ++y; - const bool skip = x < devRect.x1 || y < devRect.y1; - Q_ASSERT(skip || (x < devRect.x2 && y < devRect.y2)); - if (inDash && !skip) { - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - spans[current].len = 1; - spans[current].coverage = 255; - spans[current].x = x; - spans[current].y = y; - ++current; - } - if (--currPattern <= 0) { - inDash = !inDash; - dashIndex = (dashIndex + 1) % pattern.size(); - currPattern = int(pattern[dashIndex]); - } - } - } else { // 45 -> 90 and 225 -> 270 (unit circle degrees) - x1 = qMin(x1, devRect.x2 - 1); - incrE = dx * 2; - d = incrE + dy; - incrNE = (dx + dy) * 2; - - if (x < devRect.x1) - goto flush_and_return; - - while (y < y2) { - if (d < 0) { - --x; - d += incrNE; - if (x < devRect.x1) - goto flush_and_return; - } else { - d += incrE; - } - ++y; - const bool skip = y < devRect.y1 || x > x1; - Q_ASSERT(skip || (x >= devRect.x1 && x < devRect.x2 && y < devRect.y2)); - if (inDash && !skip) { - if (current == NSPANS) { - span_func(NSPANS, spans, data); - current = 0; - } - spans[current].len = 1; - spans[current].coverage = 255; - spans[current].x = x; - spans[current].y = y; - ++current; - } - if (--currPattern <= 0) { - inDash = !inDash; - dashIndex = (dashIndex + 1) % pattern.size(); - currPattern = int(pattern[dashIndex]); - } - } - } - } -flush_and_return: - if (current > 0) - span_func(current, ordered ? spans : spans + (NSPANS - current), data); - - // adjust offset - if (reversed) { - *patternOffset = (patternLength - 1 - *patternOffset); - } else { - *patternOffset = 0; - for (int i = 0; i <= dashIndex; ++i) - *patternOffset += int(pattern[i]); - *patternOffset += patternLength - currPattern - 1; - *patternOffset = (*patternOffset % patternLength); - } -} - /*! \internal \a x and \a y is relative to the midpoint of \a rect. diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h index 4abb18c..8774fda 100644 --- a/src/gui/painting/qpaintengine_raster_p.h +++ b/src/gui/painting/qpaintengine_raster_p.h @@ -196,9 +196,6 @@ public: void stroke(const QVectorPath &path, const QPen &pen); void fill(const QVectorPath &path, const QBrush &brush); - void strokePolygonCosmetic(const QPoint *pts, int pointCount, PolygonDrawMode mode); - void strokePolygonCosmetic(const QPointF *pt, int pointCount, PolygonDrawMode mode); - void clip(const QVectorPath &path, Qt::ClipOperation op); void clip(const QRect &rect, Qt::ClipOperation op); void clip(const QRegion ®ion, Qt::ClipOperation op); @@ -249,17 +246,21 @@ public: virtual void drawBufferSpan(const uint *buffer, int bufsize, int x, int y, int length, uint const_alpha); #endif + bool supportsTransformations(const QFontEngine *fontEngine) const; + bool supportsTransformations(qreal pixelSize, const QTransform &m) const; protected: QRasterPaintEngine(QRasterPaintEnginePrivate &d, QPaintDevice *); private: friend struct QSpanData; + friend class QBlitterPaintEngine; + friend class QBlitterPaintEnginePrivate; void init(); void fillRect(const QRectF &rect, QSpanData *data); void drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fill); - void drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, + bool drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, QFontEngine *fontEngine); #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) @@ -326,7 +327,6 @@ public: bool isUnclipped_normalized(const QRect &rect) const; bool isUnclipped(const QRect &rect, int penWidth) const; bool isUnclipped(const QRectF &rect, int penWidth) const; - ProcessSpans getPenFunc(const QRect &rect, const QSpanData *data) const; ProcessSpans getPenFunc(const QRectF &rect, const QSpanData *data) const; ProcessSpans getBrushFunc(const QRect &rect, const QSpanData *data) const; ProcessSpans getBrushFunc(const QRectF &rect, const QSpanData *data) const; @@ -340,6 +340,7 @@ public: void initializeRasterizer(QSpanData *data); void recalculateFastImages(); + bool canUseFastImageBlending(QPainter::CompositionMode mode, const QImage &image) const; QPaintDevice *device; QScopedPointer<QOutlineMapper> outlineMapper; diff --git a/src/gui/painting/qpaintengine_x11.cpp b/src/gui/painting/qpaintengine_x11.cpp index 5616c6d..17d141c 100644 --- a/src/gui/painting/qpaintengine_x11.cpp +++ b/src/gui/painting/qpaintengine_x11.cpp @@ -1611,8 +1611,8 @@ void QX11PaintEnginePrivate::fillPolygon_dev(const QPointF *polygonPoints, int p && (fill.style() != Qt::NoBrush) && ((has_fill_texture && fill.texture().hasAlpha()) || antialias || !solid_fill || has_alpha_pen != has_alpha_brush)) { - QRect br = tessellator->tessellate((QPointF *)clippedPoints, clippedCount, - mode == QPaintEngine::WindingMode); + tessellator->tessellate((QPointF *)clippedPoints, clippedCount, + mode == QPaintEngine::WindingMode); if (tessellator->size > 0) { XRenderPictureAttributes attrs; attrs.poly_edge = antialias ? PolyEdgeSmooth : PolyEdgeSharp; @@ -1771,7 +1771,6 @@ void QX11PaintEngine::drawPath(const QPainterPath &path) Q_D(QX11PaintEngine); if (path.isEmpty()) return; - QTransform old_matrix = d->matrix; if (d->has_brush) d->fillPath(path, QX11PaintEnginePrivate::BrushGC, true); @@ -2334,7 +2333,7 @@ static QPainterPath path_for_glyphs(const QVarLengthArray<glyph_t> &glyphs, bool set = src[x >> 3] & (0x80 >> (x & 7)); if (set) { QRect r(xp + x, yp - h, 1, 1); - while (x < glyph->width-1 && src[(x+1) >> 3] & (0x80 >> ((x+1) & 7))) { + while (x+1 < glyph->width && src[(x+1) >> 3] & (0x80 >> ((x+1) & 7))) { ++x; r.setRight(r.right()+1); } @@ -2385,7 +2384,7 @@ void QX11PaintEngine::drawFreetype(const QPointF &p, const QTextItemInt &ti) set = ft->loadTransformedGlyphSet(d->matrix); if (!set || set->outline_drawing - || !ft->loadGlyphs(set, glyphs.data(), glyphs.size(), QFontEngineFT::Format_Render)) + || !ft->loadGlyphs(set, glyphs.constData(), glyphs.size(), positions.constData(), QFontEngineFT::Format_Render)) { QPaintEngine::drawTextItem(p, ti); return; diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index ce4ff2a..6df410b 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -44,6 +44,8 @@ #include "qstroker_p.h" #include "qbezier_p.h" #include <private/qpainterpath_p.h> +#include <private/qfontengine_p.h> +#include <private/qstatictext_p.h> #include <qvarlengtharray.h> #include <qdebug.h> @@ -831,7 +833,7 @@ void QPaintEngineEx::drawEllipse(const QRectF &r) int point_count = 0; x.points[0] = qt_curves_for_arc(r, 0, -360, x.points + 1, &point_count); - QVectorPath vp((qreal *) pts, point_count, qpaintengineex_ellipse_types, QVectorPath::EllipseHint); + QVectorPath vp((qreal *) pts, point_count + 1, qpaintengineex_ellipse_types, QVectorPath::EllipseHint); draw(vp); } @@ -1001,6 +1003,22 @@ void QPaintEngineEx::drawPixmapFragments(const QPainter::PixmapFragment *fragmen transformChanged(); } +void QPaintEngineEx::drawPixmapFragments(const QRectF *targetRects, const QRectF *sourceRects, int fragmentCount, + const QPixmap &pixmap, QPainter::PixmapFragmentHints /*hints*/) +{ + if (pixmap.isNull()) + return; + + if (sourceRects) { + for (int i = 0; i < fragmentCount; ++i) + drawPixmap(targetRects[i], pixmap, sourceRects[i]); + } else { + QRectF sourceRect = pixmap.rect(); + for (int i = 0; i < fragmentCount; ++i) + drawPixmap(targetRects[i], pixmap, sourceRect); + } +} + void QPaintEngineEx::setState(QPainterState *s) { QPaintEngine::state = s; @@ -1012,4 +1030,93 @@ void QPaintEngineEx::updateState(const QPaintEngineState &) // do nothing... } +Q_GUI_EXPORT QPainterPath qt_painterPathFromVectorPath(const QVectorPath &path) +{ + const qreal *points = path.points(); + const QPainterPath::ElementType *types = path.elements(); + + QPainterPath p; + if (types) { + int id = 0; + for (int i=0; i<path.elementCount(); ++i) { + switch(types[i]) { + case QPainterPath::MoveToElement: + p.moveTo(QPointF(points[id], points[id+1])); + id+=2; + break; + case QPainterPath::LineToElement: + p.lineTo(QPointF(points[id], points[id+1])); + id+=2; + break; + case QPainterPath::CurveToElement: { + QPointF p1(points[id], points[id+1]); + QPointF p2(points[id+2], points[id+3]); + QPointF p3(points[id+4], points[id+5]); + p.cubicTo(p1, p2, p3); + id+=6; + break; + } + case QPainterPath::CurveToDataElement: + ; + break; + } + } + } else { + p.moveTo(QPointF(points[0], points[1])); + int id = 2; + for (int i=1; i<path.elementCount(); ++i) { + p.lineTo(QPointF(points[id], points[id+1])); + id+=2; + } + } + if (path.hints() & QVectorPath::WindingFill) + p.setFillRule(Qt::WindingFill); + + return p; +} + +void QPaintEngineEx::drawStaticTextItem(QStaticTextItem *staticTextItem) +{ + QPainterPath path; +#ifndef Q_WS_MAC + path.setFillRule(Qt::WindingFill); +#endif + + if (staticTextItem->numGlyphs == 0) + return; + + QFontEngine *fontEngine = staticTextItem->fontEngine(); + fontEngine->addGlyphsToPath(staticTextItem->glyphs, staticTextItem->glyphPositions, + staticTextItem->numGlyphs, &path, 0); + if (!path.isEmpty()) { + QPainterState *s = state(); + QPainter::RenderHints oldHints = s->renderHints; + bool changedHints = false; + if (bool(oldHints & QPainter::TextAntialiasing) + && !bool(fontEngine->fontDef.styleStrategy & QFont::NoAntialias) + && !bool(oldHints & QPainter::Antialiasing)) { + s->renderHints |= QPainter::Antialiasing; + renderHintsChanged(); + changedHints = true; + } + + fill(qtVectorPathForPath(path), s->pen.color()); + + if (changedHints) { + s->renderHints = oldHints; + renderHintsChanged(); + } + } +} + +bool QPaintEngineEx::supportsTransformations(qreal pixelSize, const QTransform &m) const +{ + Q_UNUSED(pixelSize); + + if (!m.isAffine()) + return true; + + return false; +} + QT_END_NAMESPACE diff --git a/src/gui/painting/qpaintengineex_p.h b/src/gui/painting/qpaintengineex_p.h index 9730033..275a6e0 100644 --- a/src/gui/painting/qpaintengineex_p.h +++ b/src/gui/painting/qpaintengineex_p.h @@ -198,11 +198,13 @@ public: virtual void drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPointF &s); virtual void drawPixmapFragments(const QPainter::PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, - QFlags<QPainter::PixmapFragmentHint> hints); + QPainter::PixmapFragmentHints hints); + virtual void drawPixmapFragments(const QRectF *targetRects, const QRectF *sourceRects, int fragmentCount, const QPixmap &pixmap, + QPainter::PixmapFragmentHints hints); virtual void updateState(const QPaintEngineState &state); - virtual void drawStaticTextItem(QStaticTextItem *) = 0; + virtual void drawStaticTextItem(QStaticTextItem *); virtual void setState(QPainterState *s); inline QPainterState *state() { return static_cast<QPainterState *>(QPaintEngine::state); } @@ -227,6 +229,7 @@ public: IsEmulationEngine = 0x02 // If set, this object is a QEmulationEngine. }; virtual uint flags() const {return 0;} + virtual bool supportsTransformations(qreal pixelSize, const QTransform &m) const; protected: QPaintEngineEx(QPaintEngineExPrivate &data); diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 754b16e..efb016e 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -61,6 +61,8 @@ #include "qstyle.h" #include "qthread.h" #include "qvarlengtharray.h" +#include "qstatictext.h" +#include "qglyphrun.h" #include <private/qfontengine_p.h> #include <private/qpaintengine_p.h> @@ -70,9 +72,10 @@ #include <private/qwidget_p.h> #include <private/qpaintengine_raster_p.h> #include <private/qmath_p.h> -#include <qstatictext.h> #include <private/qstatictext_p.h> +#include <private/qglyphrun_p.h> #include <private/qstylehelper_p.h> +#include <private/qrawfont_p.h> QT_BEGIN_NAMESPACE @@ -92,7 +95,7 @@ void qt_format_text(const QFont &font, QPainter *painter); static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe, QTextCharFormat::UnderlineStyle underlineStyle, - const QTextItem::RenderFlags flags, qreal width, + QTextItem::RenderFlags flags, qreal width, const QTextCharFormat &charFormat); // Helper function to calculate left most position, width and flags for decoration drawing Q_GUI_EXPORT void qt_draw_decoration_for_glyphs(QPainter *painter, const glyph_t *glyphArray, @@ -162,6 +165,10 @@ static bool qt_painter_thread_test(int devType, const char *what, bool extraCond #endif break; default: +#ifdef Q_WS_X11 + if (QApplication::testAttribute(Qt::AA_X11InitThreads)) + return true; +#endif if (!extraCondition && QThread::currentThread() != qApp->thread()) { qWarning("QPainter: It is not safe to use %s outside the GUI thread", what); return false; @@ -489,8 +496,12 @@ void QPainterPrivate::draw_helper(const QPainterPath &originalPath, DrawOperatio q->save(); state->matrix = QTransform(); - state->dirtyFlags |= QPaintEngine::DirtyTransform; - updateState(state); + if (extended) { + extended->transformChanged(); + } else { + state->dirtyFlags |= QPaintEngine::DirtyTransform; + updateState(state); + } engine->drawImage(absPathRect, image, QRectF(0, 0, absPathRect.width(), absPathRect.height()), @@ -673,11 +684,14 @@ void QPainterPrivate::updateInvMatrix() invMatrix = state->matrix.inverted(); } +Q_GUI_EXPORT bool qt_isExtendedRadialGradient(const QBrush &brush); + void QPainterPrivate::updateEmulationSpecifier(QPainterState *s) { bool alpha = false; bool linearGradient = false; bool radialGradient = false; + bool extendedRadialGradient = false; bool conicalGradient = false; bool patternBrush = false; bool xform = false; @@ -709,6 +723,7 @@ void QPainterPrivate::updateEmulationSpecifier(QPainterState *s) (brushStyle == Qt::LinearGradientPattern)); radialGradient = ((penBrushStyle == Qt::RadialGradientPattern) || (brushStyle == Qt::RadialGradientPattern)); + extendedRadialGradient = radialGradient && (qt_isExtendedRadialGradient(penBrush) || qt_isExtendedRadialGradient(s->brush)); conicalGradient = ((penBrushStyle == Qt::ConicalGradientPattern) || (brushStyle == Qt::ConicalGradientPattern)); patternBrush = (((penBrushStyle > Qt::SolidPattern @@ -792,7 +807,7 @@ void QPainterPrivate::updateEmulationSpecifier(QPainterState *s) s->emulationSpecifier &= ~QPaintEngine::LinearGradientFill; // Radial gradient emulation - if (radialGradient && !engine->hasFeature(QPaintEngine::RadialGradientFill)) + if (extendedRadialGradient || (radialGradient && !engine->hasFeature(QPaintEngine::RadialGradientFill))) s->emulationSpecifier |= QPaintEngine::RadialGradientFill; else s->emulationSpecifier &= ~QPaintEngine::RadialGradientFill; @@ -2701,6 +2716,63 @@ QPainterPath QPainter::clipPath() const } /*! + Returns the bounding rectangle of the current clip if there is a clip; + otherwise returns an empty rectangle. Note that the clip region is + given in logical coordinates. + + The bounding rectangle is not guaranteed to be tight. + + \sa setClipRect(), setClipPath(), setClipRegion() + + \since 4.8 + */ + +QRectF QPainter::clipBoundingRect() const +{ + Q_D(const QPainter); + + if (!d->engine) { + qWarning("QPainter::clipBoundingRect: Painter not active"); + return QRectF(); + } + + // Accumulate the bounding box in device space. This is not 100% + // precise, but it fits within the guarantee and it is reasonably + // fast. + QRectF bounds; + for (int i=0; i<d->state->clipInfo.size(); ++i) { + QRectF r; + const QPainterClipInfo &info = d->state->clipInfo.at(i); + + if (info.clipType == QPainterClipInfo::RectClip) + r = info.rect; + else if (info.clipType == QPainterClipInfo::RectFClip) + r = info.rectf; + else if (info.clipType == QPainterClipInfo::RegionClip) + r = info.region.boundingRect(); + else + r = info.path.boundingRect(); + + r = info.matrix.mapRect(r); + + if (i == 0) + bounds = r; + else if (info.operation == Qt::IntersectClip) + bounds &= r; + else if (info.operation == Qt::UniteClip) + bounds |= r; + } + + + // Map the rectangle back into logical space using the inverse + // matrix. + if (!d->txinv) + const_cast<QPainter *>(this)->d_ptr->updateInvMatrix(); + + return d->invMatrix.mapRect(bounds); +} + +/*! \fn void QPainter::setClipRect(const QRectF &rectangle, Qt::ClipOperation operation) Enables clipping, and sets the clip region to the given \a @@ -5265,7 +5337,7 @@ void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm) return; #ifndef QT_NO_DEBUG - qt_painter_thread_test(d->device->devType(), "drawPixmap()"); + qt_painter_thread_test(d->device->devType(), "drawPixmap()", true); #endif if (d->extended) { @@ -5335,7 +5407,7 @@ void QPainter::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) if (!d->engine || pm.isNull()) return; #ifndef QT_NO_DEBUG - qt_painter_thread_test(d->device->devType(), "drawPixmap()"); + qt_painter_thread_test(d->device->devType(), "drawPixmap()", true); #endif qreal x = r.x(); @@ -5720,50 +5792,101 @@ void QPainter::drawImage(const QRectF &targetRect, const QImage &image, const QR d->engine->drawImage(QRectF(x, y, w, h), image, QRectF(sx, sy, sw, sh), flags); } +#if !defined(QT_NO_RAWFONT) +/*! + \fn void QPainter::drawGlyphRun(const QPointF &position, const QGlyphRun &glyphs) + + Draws the specified \a glyphs at the given \a position. + The \a position gives the edge of the baseline for the string of glyphs. + The glyphs will be retrieved from the font selected by \a glyphs and at + offsets given by the positions in \a glyphs. -void qt_draw_glyphs(QPainter *painter, const quint32 *glyphArray, const QPointF *positionArray, - int glyphCount) + \since 4.8 + + \sa QGlyphRun::setRawFont(), QGlyphRun::setPositions(), QGlyphRun::setGlyphIndexes() +*/ +void QPainter::drawGlyphRun(const QPointF &position, const QGlyphRun &glyphRun) { - QPainterPrivate *painter_d = QPainterPrivate::get(painter); - painter_d->drawGlyphs(glyphArray, positionArray, glyphCount); + Q_D(QPainter); + + QRawFont font = glyphRun.rawFont(); + if (!font.isValid()) + return; + + QGlyphRunPrivate *glyphRun_d = QGlyphRunPrivate::get(glyphRun); + + const quint32 *glyphIndexes = glyphRun_d->glyphIndexData; + const QPointF *glyphPositions = glyphRun_d->glyphPositionData; + + int count = qMin(glyphRun_d->glyphIndexDataSize, glyphRun_d->glyphPositionDataSize); + QVarLengthArray<QFixedPoint, 128> fixedPointPositions(count); + + QRawFontPrivate *fontD = QRawFontPrivate::get(font); + bool supportsTransformations; + if (d->extended != 0) { + supportsTransformations = d->extended->supportsTransformations(fontD->fontEngine->fontDef.pixelSize, + d->state->matrix); + } else { + supportsTransformations = d->engine->type() == QPaintEngine::CoreGraphics + || d->state->matrix.isAffine(); + } + + for (int i=0; i<count; ++i) { + QPointF processedPosition = position + glyphPositions[i]; + if (!supportsTransformations) + processedPosition = d->state->transform().map(processedPosition); + fixedPointPositions[i] = QFixedPoint::fromPointF(processedPosition); + } + + d->drawGlyphs(glyphIndexes, fixedPointPositions.data(), count, font, glyphRun.overline(), + glyphRun.underline(), glyphRun.strikeOut()); } -void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, const QPointF *positionArray, - int glyphCount) +void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, QFixedPoint *positions, + int glyphCount, + const QRawFont &font, bool overline, bool underline, + bool strikeOut) { + Q_Q(QPainter); + updateState(state); - QFontEngine *fontEngine = state->font.d->engineForScript(QUnicodeTables::Common); + QRawFontPrivate *fontD = QRawFontPrivate::get(font); + QFontEngine *fontEngine = fontD->fontEngine; - while (fontEngine->type() == QFontEngine::Multi) { - // Pick engine based on first glyph in array if we are using a multi engine. - // (all glyphs must be for same font) - int engineIdx = 0; - if (glyphCount > 0) - engineIdx = glyphArray[0] >> 24; + QFixed leftMost; + QFixed rightMost; + QFixed baseLine; + for (int i=0; i<glyphCount; ++i) { + glyph_metrics_t gm = fontEngine->boundingBox(glyphArray[i]); + if (i == 0 || leftMost > positions[i].x) + leftMost = positions[i].x; - fontEngine = static_cast<QFontEngineMulti *>(fontEngine)->engine(engineIdx); - } + // We don't support glyphs that do not share a common baseline. If this turns out to + // be a relevant use case, then we need to find clusters of glyphs that share a baseline + // and do a drawTextItemDecorations call per cluster. + if (i == 0 || baseLine < positions[i].y) + baseLine = positions[i].y; - QVarLengthArray<QFixedPoint, 128> positions; - for (int i=0; i<glyphCount; ++i) { - QFixedPoint fp = QFixedPoint::fromPointF(positionArray[i]); - positions.append(fp); + // We use the advance rather than the actual bounds to match the algorithm in drawText() + if (i == 0 || rightMost < positions[i].x + gm.xoff) + rightMost = positions[i].x + gm.xoff; } - if (extended != 0) { + QFixed width = rightMost - leftMost; + + if (extended != 0 && state->matrix.isAffine()) { QStaticTextItem staticTextItem; staticTextItem.color = state->pen.color(); staticTextItem.font = state->font; staticTextItem.setFontEngine(fontEngine); staticTextItem.numGlyphs = glyphCount; staticTextItem.glyphs = reinterpret_cast<glyph_t *>(const_cast<glyph_t *>(glyphArray)); - staticTextItem.glyphPositions = positions.data(); + staticTextItem.glyphPositions = positions; extended->drawStaticTextItem(&staticTextItem); } else { QTextItemInt textItem; - textItem.f = &state->font; textItem.fontEngine = fontEngine; QVarLengthArray<QFixed, 128> advances(glyphCount); @@ -5775,7 +5898,7 @@ void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, const QPointF *posit textItem.glyphs.numGlyphs = glyphCount; textItem.glyphs.glyphs = reinterpret_cast<HB_Glyph *>(const_cast<quint32 *>(glyphArray)); - textItem.glyphs.offsets = positions.data(); + textItem.glyphs.offsets = positions; textItem.glyphs.advances_x = advances.data(); textItem.glyphs.advances_y = advances.data(); textItem.glyphs.justifications = glyphJustifications.data(); @@ -5783,7 +5906,23 @@ void QPainterPrivate::drawGlyphs(const quint32 *glyphArray, const QPointF *posit engine->drawTextItem(QPointF(0, 0), textItem); } + + QTextItemInt::RenderFlags flags; + if (underline) + flags |= QTextItemInt::Underline; + if (overline) + flags |= QTextItemInt::Overline; + if (strikeOut) + flags |= QTextItemInt::StrikeOut; + + drawTextItemDecoration(q, QPointF(leftMost.toReal(), baseLine.toReal()), + fontEngine, + (underline + ? QTextCharFormat::SingleUnderline + : QTextCharFormat::NoUnderline), + flags, width.toReal(), QTextCharFormat()); } +#endif // QT_NO_RAWFONT /*! @@ -5871,14 +6010,12 @@ void QPainter::drawStaticText(const QPointF &topLeftPosition, const QStaticText return; } - bool paintEngineSupportsTransformations = d->extended->type() == QPaintEngine::OpenGL2 - || d->extended->type() == QPaintEngine::OpenVG - || d->extended->type() == QPaintEngine::OpenGL; - - if (paintEngineSupportsTransformations && !staticText_d->untransformedCoordinates) { + bool supportsTransformations = d->extended->supportsTransformations(staticText_d->font.pixelSize(), + d->state->matrix); + if (supportsTransformations && !staticText_d->untransformedCoordinates) { staticText_d->untransformedCoordinates = true; staticText_d->needsRelayout = true; - } else if (!paintEngineSupportsTransformations && staticText_d->untransformedCoordinates) { + } else if (!supportsTransformations && staticText_d->untransformedCoordinates) { staticText_d->untransformedCoordinates = false; staticText_d->needsRelayout = true; } @@ -6318,7 +6455,7 @@ static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen) static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QFontEngine *fe, QTextCharFormat::UnderlineStyle underlineStyle, - const QTextItem::RenderFlags flags, qreal width, + QTextItem::RenderFlags flags, qreal width, const QTextCharFormat &charFormat) { if (underlineStyle == QTextCharFormat::NoUnderline @@ -6333,7 +6470,7 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const pen.setWidthF(fe->lineThickness().toReal()); pen.setCapStyle(Qt::FlatCap); - QLineF line(pos.x(), pos.y(), pos.x() + width, pos.y()); + QLineF line(pos.x(), pos.y(), pos.x() + qFloor(width), pos.y()); qreal underlineOffset = fe->underlinePosition().toReal(); qreal y = pos.y(); @@ -6724,7 +6861,7 @@ void QPainter::drawTiledPixmap(const QRectF &r, const QPixmap &pixmap, const QPo return; #ifndef QT_NO_DEBUG - qt_painter_thread_test(d->device->devType(), "drawTiledPixmap()"); + qt_painter_thread_test(d->device->devType(), "drawTiledPixmap()", true); #endif qreal sw = pixmap.width(); @@ -7904,7 +8041,7 @@ void qt_format_text(const QFont &fnt, const QRectF &_r, } void qt_format_text(const QFont &fnt, const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect, - int tabstops, int *, int tabarraylen, + int tabstops, int *ta, int tabarraylen, QPainter *painter) { @@ -8024,6 +8161,16 @@ start_lengthVariant: engine.option = *option; } + if (engine.option.tabStop() < 0 && tabstops > 0) + engine.option.setTabStop(tabstops); + + if (engine.option.tabs().isEmpty() && ta) { + QList<qreal> tabs; + for (int i = 0; i < tabarraylen; i++) + tabs.append(qreal(ta[i])); + engine.option.setTabArray(tabs); + } + engine.option.setTextDirection(layout_direction); if (tf & Qt::AlignJustify) engine.option.setAlignment(Qt::AlignJustify); @@ -9104,6 +9251,52 @@ void QPainter::drawPixmapFragments(const PixmapFragment *fragments, int fragment } /*! + \since 4.8 + + This function is used to draw the same \a pixmap with multiple target + and source rectangles specified by \a targetRects. If \a sourceRects is 0, + the whole pixmap will be rendered at each of the target rectangles. + The \a hints parameter can be used to pass in drawing hints. + + This function is potentially faster than multiple calls to drawPixmap(), + since the backend can optimize state changes. + + \sa QPainter::PixmapFragmentHint +*/ + +void QPainter::drawPixmapFragments(const QRectF *targetRects, const QRectF *sourceRects, int fragmentCount, + const QPixmap &pixmap, PixmapFragmentHints hints) +{ + Q_D(QPainter); + + if (!d->engine || pixmap.isNull()) + return; + +#ifndef QT_NO_DEBUG + if (sourceRects) { + for (int i = 0; i < fragmentCount; ++i) { + QRectF sourceRect = sourceRects[i]; + if (!(QRectF(pixmap.rect()).contains(sourceRect))) + qWarning("QPainter::drawPixmapFragments - the source rect is not contained by the pixmap's rectangle"); + } + } +#endif + + if (d->engine->isExtended()) { + d->extended->drawPixmapFragments(targetRects, sourceRects, fragmentCount, pixmap, hints); + } else { + if (sourceRects) { + for (int i = 0; i < fragmentCount; ++i) + drawPixmap(targetRects[i], pixmap, sourceRects[i]); + } else { + QRectF sourceRect = pixmap.rect(); + for (int i = 0; i < fragmentCount; ++i) + drawPixmap(targetRects[i], pixmap, sourceRect); + } + } +} + +/*! \since 4.7 \class QPainter::PixmapFragment diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 1542e85..12a14a2 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -79,6 +79,7 @@ class QTextItem; class QMatrix; class QTransform; class QStaticText; +class QGlyphRun; class QPainterPrivateDeleter; @@ -221,6 +222,8 @@ public: void setClipping(bool enable); bool hasClipping() const; + QRectF clipBoundingRect() const; + void save(); void restore(); @@ -377,6 +380,8 @@ public: void drawPixmapFragments(const PixmapFragment *fragments, int fragmentCount, const QPixmap &pixmap, PixmapFragmentHints hints = 0); + void drawPixmapFragments(const QRectF *targetRects, const QRectF *sourceRects, int fragmentCount, + const QPixmap &pixmap, PixmapFragmentHints hints = 0); void drawImage(const QRectF &targetRect, const QImage &image, const QRectF &sourceRect, Qt::ImageConversionFlags flags = Qt::AutoColor); @@ -396,6 +401,10 @@ public: void setLayoutDirection(Qt::LayoutDirection direction); Qt::LayoutDirection layoutDirection() const; +#if !defined(QT_NO_RAWFONT) + void drawGlyphRun(const QPointF &position, const QGlyphRun &glyphRun); +#endif + void drawStaticText(const QPointF &topLeftPosition, const QStaticText &staticText); inline void drawStaticText(const QPoint &topLeftPosition, const QStaticText &staticText); inline void drawStaticText(int left, int top, const QStaticText &staticText); @@ -546,6 +555,7 @@ private: friend class QPaintEngine; friend class QPaintEngineExPrivate; friend class QOpenGLPaintEngine; + friend class QVGPaintEngine; friend class QX11PaintEngine; friend class QX11PaintEnginePrivate; friend class QWin32PaintEngine; diff --git a/src/gui/painting/qpainter_p.h b/src/gui/painting/qpainter_p.h index 2326640..79d4b4b 100644 --- a/src/gui/painting/qpainter_p.h +++ b/src/gui/painting/qpainter_p.h @@ -70,6 +70,7 @@ QT_BEGIN_NAMESPACE class QPaintEngine; class QEmulationPaintEngine; class QPaintEngineEx; +struct QFixedPoint; struct QTLWExtra; @@ -183,6 +184,7 @@ struct QPainterDummyState QTransform transform; }; +class QRawFont; class QPainterPrivate { Q_DECLARE_PUBLIC(QPainter) @@ -228,7 +230,12 @@ public: void draw_helper(const QPainterPath &path, DrawOperation operation = StrokeAndFillDraw); void drawStretchedGradient(const QPainterPath &path, DrawOperation operation); void drawOpaqueBackground(const QPainterPath &path, DrawOperation operation); - void drawGlyphs(const quint32 *glyphArray, const QPointF *positionArray, int glyphCount); + +#if !defined(QT_NO_RAWFONT) + void drawGlyphs(const quint32 *glyphArray, QFixedPoint *positionArray, int glyphCount, + const QRawFont &font, bool overline = false, bool underline = false, + bool strikeOut = false); +#endif void updateMatrix(); void updateInvMatrix(); @@ -258,8 +265,6 @@ public: }; Q_GUI_EXPORT void qt_draw_helper(QPainterPrivate *p, const QPainterPath &path, QPainterPrivate::DrawOperation operation); -Q_GUI_EXPORT void qt_draw_glyphs(QPainter *painter, const quint32 *glyphArray, - const QPointF *positionArray, int glyphCount); QString qt_generate_brush_key(const QBrush &brush); diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index b0515cc..c238578 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -576,6 +576,14 @@ QPainterPath &QPainterPath::operator=(const QPainterPath &other) } /*! + \fn void QPainterPath::swap(QPainterPath &other) + \since 4.8 + + Swaps painter path \a other with this painter path. This operation is very + fast and never fails. +*/ + +/*! Destroys this QPainterPath object. */ QPainterPath::~QPainterPath() @@ -866,7 +874,7 @@ void QPainterPath::arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength); #endif - if (!qt_is_finite(rect.x()) && !qt_is_finite(rect.y()) || !qt_is_finite(rect.width()) || !qt_is_finite(rect.height()) + if ((!qt_is_finite(rect.x()) && !qt_is_finite(rect.y())) || !qt_is_finite(rect.width()) || !qt_is_finite(rect.height()) || !qt_is_finite(startAngle) || !qt_is_finite(sweepLength)) { #ifndef QT_NO_DEBUG qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN or Inf, ignoring call"); @@ -1118,6 +1126,7 @@ void QPainterPath::addText(const QPointF &point, const QFont &f, const QString & QTextEngine *eng = layout.engine(); layout.beginLayout(); QTextLine line = layout.createLine(); + Q_UNUSED(line); layout.endLayout(); const QScriptLine &sl = eng->lines[0]; if (!sl.length || !eng->layoutData) @@ -1896,7 +1905,7 @@ static bool qt_isect_curve_horizontal(const QBezier &bezier, qreal y, qreal x1, if (y >= bounds.top() && y < bounds.bottom() && bounds.right() >= x1 && bounds.left() < x2) { const qreal lower_bound = qreal(.01); - if (depth == 32 || bounds.width() < lower_bound && bounds.height() < lower_bound) + if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) return true; QBezier first_half, second_half; @@ -1915,7 +1924,7 @@ static bool qt_isect_curve_vertical(const QBezier &bezier, qreal x, qreal y1, qr if (x >= bounds.left() && x < bounds.right() && bounds.bottom() >= y1 && bounds.top() < y2) { const qreal lower_bound = qreal(.01); - if (depth == 32 || bounds.width() < lower_bound && bounds.height() < lower_bound) + if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) return true; QBezier first_half, second_half; @@ -2915,9 +2924,12 @@ QPointF QPainterPath::pointAtPercent(qreal t) const return QPointF(); } - if (isEmpty()) + if (!d_ptr || d_ptr->elements.size() == 0) return QPointF(); + if (d_ptr->elements.size() == 1) + return d_ptr->elements.at(0); + qreal totalLength = length(); qreal curLen = 0; qreal bezierLen = 0; diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h index 07852e5..a2dfed6 100644 --- a/src/gui/painting/qpainterpath.h +++ b/src/gui/painting/qpainterpath.h @@ -95,7 +95,12 @@ public: explicit QPainterPath(const QPointF &startPoint); QPainterPath(const QPainterPath &other); QPainterPath &operator=(const QPainterPath &other); +#ifdef Q_COMPILER_RVALUE_REFS + inline QPainterPath &operator=(QPainterPath &&other) + { qSwap(d_ptr, other.d_ptr); return *this; } +#endif ~QPainterPath(); + inline void swap(QPainterPath &other) { d_ptr.swap(other.d_ptr); } void closeSubpath(); diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 81a784d..958e499 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -47,6 +47,7 @@ #include "private/qcups_p.h" #include "qprinterinfo.h" #include <qnumeric.h> +#include "private/qfont_p.h" #ifdef Q_OS_UNIX #include "private/qcore_unix_p.h" // overrides QT_OPEN @@ -54,8 +55,6 @@ QT_BEGIN_NAMESPACE -Q_GUI_EXPORT extern int qt_defaultDpi(); - #ifndef QT_NO_PRINTER extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size); @@ -915,7 +914,6 @@ const char *QPdf::paperSizeToString(QPrinter::PaperSize paperSize) return psToStr[paperSize]; } - // -------------------------- base engine, shared code between PS and PDF ----------------------- QPdfBaseEngine::QPdfBaseEngine(QPdfBaseEnginePrivate &dd, PaintEngineFeatures f) @@ -1157,6 +1155,8 @@ void QPdfBaseEngine::updateState(const QPaintEngineState &state) } if (flags & DirtyBrush) { d->brush = state.brush(); + if (d->brush.color().alpha() == 0 && d->brush.style() == Qt::SolidPattern) + d->brush.setStyle(Qt::NoBrush); d->hasBrush = d->brush.style() != Qt::NoBrush; } if (flags & DirtyBrushOrigin) { diff --git a/src/gui/painting/qpen.cpp b/src/gui/painting/qpen.cpp index a4e0421..cc652d8 100644 --- a/src/gui/painting/qpen.cpp +++ b/src/gui/painting/qpen.cpp @@ -383,6 +383,14 @@ QPen &QPen::operator=(const QPen &p) } /*! + \fn void QPen::swap(QPen &other) + \since 4.8 + + Swaps pen \a other with this pen. This operation is very + fast and never fails. +*/ + +/*! Returns the pen as a QVariant. */ QPen::operator QVariant() const diff --git a/src/gui/painting/qpen.h b/src/gui/painting/qpen.h index 7741d4d..a751c2f 100644 --- a/src/gui/painting/qpen.h +++ b/src/gui/painting/qpen.h @@ -74,6 +74,11 @@ public: ~QPen(); QPen &operator=(const QPen &pen); +#ifdef Q_COMPILER_RVALUE_REFS + inline QPen &operator=(QPen &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QPen &other) { qSwap(d, other.d); } Qt::PenStyle style() const; void setStyle(Qt::PenStyle); diff --git a/src/gui/painting/qpolygon.cpp b/src/gui/painting/qpolygon.cpp index 692c870..fc22aed 100644 --- a/src/gui/painting/qpolygon.cpp +++ b/src/gui/painting/qpolygon.cpp @@ -700,6 +700,22 @@ QPolygon QPolygonF::toPolygon() const } /*! + \fn void QPolygon::swap(QPolygon &other) + \since 4.8 + + Swaps polygon \a other with this polygon. This operation is very + fast and never fails. +*/ + +/*! + \fn void QPolygonF::swap(QPolygonF &other) + \since 4.8 + + Swaps polygon \a other with this polygon. This operation is very + fast and never fails. +*/ + +/*! Returns the polygon as a QVariant */ QPolygon::operator QVariant() const diff --git a/src/gui/painting/qpolygon.h b/src/gui/painting/qpolygon.h index 5918d03..cf3bf47 100644 --- a/src/gui/painting/qpolygon.h +++ b/src/gui/painting/qpolygon.h @@ -67,6 +67,8 @@ public: inline QPolygon(const QVector<QPoint> &v) : QVector<QPoint>(v) {} QPolygon(const QRect &r, bool closed=false); QPolygon(int nPoints, const int *points); + inline void swap(QPolygon &other) { QVector<QPoint>::swap(other); } // prevent QVector<QPoint><->QPolygon swaps + operator QVariant() const; void translate(int dx, int dy); @@ -139,6 +141,7 @@ public: inline QPolygonF(const QVector<QPointF> &v) : QVector<QPointF>(v) {} QPolygonF(const QRectF &r); QPolygonF(const QPolygon &a); + inline void swap(QPolygonF &other) { QVector<QPointF>::swap(other); } // prevent QVector<QPointF><->QPolygonF swaps inline void translate(qreal dx, qreal dy); void translate(const QPointF &offset); diff --git a/src/gui/painting/qprintengine_mac.mm b/src/gui/painting/qprintengine_mac.mm index 22ed25a..fb40677 100644 --- a/src/gui/painting/qprintengine_mac.mm +++ b/src/gui/painting/qprintengine_mac.mm @@ -49,7 +49,6 @@ QT_BEGIN_NAMESPACE extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size); -extern int qt_defaultDpi(); QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode) : QPaintEngine(*(new QMacPrintEnginePrivate)) { diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index ab1642b..2b0bca8 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -534,7 +534,10 @@ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, qint64 serial_n QImage image = img; QImage::Format format = image.format(); - if (image.depth() == 1 && *bitmap && img.colorTable().size() == 0) { + if (image.depth() == 1 && *bitmap && img.colorTable().size() == 2 + && img.colorTable().at(0) == QColor(Qt::black).rgba() + && img.colorTable().at(1) == QColor(Qt::white).rgba()) + { if (format == QImage::Format_MonoLSB) image = image.convertToFormat(QImage::Format_Mono); format = QImage::Format_Mono; @@ -936,7 +939,7 @@ void QPdfEnginePrivate::writeInfo() xprintf("\n/Creator "); printString(creator); xprintf("\n/Producer "); - printString(QString::fromLatin1("Qt " QT_VERSION_STR " (C) 2010 Nokia Corporation and/or its subsidiary(-ies)")); + printString(QString::fromLatin1("Qt " QT_VERSION_STR " (C) 2011 Nokia Corporation and/or its subsidiary(-ies)")); QDateTime now = QDateTime::currentDateTime().toUTC(); QTime t = now.time(); QDate d = now.date(); diff --git a/src/gui/painting/qprinterinfo.qdoc b/src/gui/painting/qprinterinfo.cpp index fa995ff..c00a1cc 100644 --- a/src/gui/painting/qprinterinfo.qdoc +++ b/src/gui/painting/qprinterinfo.cpp @@ -25,6 +25,16 @@ ** ****************************************************************************/ +#include "qprinterinfo.h" +#include "qprinterinfo_p.h" + +#ifndef QT_NO_PRINTER + +QT_BEGIN_NAMESPACE + +QPrinterInfoPrivate QPrinterInfoPrivate::shared_null; + + /*! \class QPrinterInfo @@ -59,60 +69,98 @@ */ /*! - \fn QPrinterInfo::QPrinterInfo() - Constructs an empty QPrinterInfo object. \sa isNull() */ +QPrinterInfo::QPrinterInfo() + : d_ptr(&QPrinterInfoPrivate::shared_null) +{ +} /*! - \fn QPrinterInfo::QPrinterInfo(const QPrinterInfo& src) + \since 4.8 - Constructs a copy of \a src. + Constructs a copy of \a other. */ +QPrinterInfo::QPrinterInfo(const QPrinterInfo &other) + : d_ptr(new QPrinterInfoPrivate(*other.d_ptr)) +{ +} /*! - \fn QPrinterInfo::QPrinterInfo(const QPrinter& printer) - Constructs a QPrinterInfo object from \a printer. */ +QPrinterInfo::QPrinterInfo(const QPrinter &printer) + : d_ptr(&QPrinterInfoPrivate::shared_null) +{ + foreach (const QPrinterInfo &printerInfo, availablePrinters()) { + if (printerInfo.printerName() == printer.printerName()) { + d_ptr.reset(new QPrinterInfoPrivate(*printerInfo.d_ptr)); + break; + } + } +} /*! - \fn QPrinterInfo::~QPrinterInfo() + \internal +*/ +QPrinterInfo::QPrinterInfo(const QString &name) + : d_ptr(new QPrinterInfoPrivate(name)) +{ +} +/*! Destroys the QPrinterInfo object. References to the values in the object become invalid. */ +QPrinterInfo::~QPrinterInfo() +{ +} /*! - \fn QPrinterInfo& QPrinterInfo::operator=(const QPrinterInfo& src) + \since 4.8 - Sets the QPrinterInfo object to be equal to \a src. + Sets the QPrinterInfo object to be equal to \a other. */ +QPrinterInfo &QPrinterInfo::operator=(const QPrinterInfo &other) +{ + Q_ASSERT(d_ptr); + d_ptr.reset(new QPrinterInfoPrivate(*other.d_ptr)); + return *this; +} /*! - \fn QString QPrinterInfo::printerName() const - Returns the name of the printer. \sa QPrinter::setPrinterName() */ +QString QPrinterInfo::printerName() const +{ + const Q_D(QPrinterInfo); + return d->name; +} /*! - \fn bool QPrinterInfo::isNull() const - Returns whether this QPrinterInfo object holds a printer definition. An empty QPrinterInfo object could result for example from calling defaultPrinter() when there are no printers on the system. */ +bool QPrinterInfo::isNull() const +{ + const Q_D(QPrinterInfo); + return d == &QPrinterInfoPrivate::shared_null; +} /*! - \fn bool QPrinterInfo::isDefault() const - Returns whether this printer is the default printer. */ +bool QPrinterInfo::isDefault() const +{ + const Q_D(QPrinterInfo); + return d->isDefault; +} /*! \fn QList< QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const @@ -123,3 +171,7 @@ Not all printer drivers support this query, so the list may be empty. On Mac OS X 10.3, this function always returns an empty list. */ + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER diff --git a/src/gui/painting/qprinterinfo.h b/src/gui/painting/qprinterinfo.h index b4cf05c..9ff1bd1 100644 --- a/src/gui/painting/qprinterinfo.h +++ b/src/gui/painting/qprinterinfo.h @@ -42,9 +42,10 @@ #ifndef QPRINTERINFO_H #define QPRINTERINFO_H -#include <QtGui/QPrinter> #include <QtCore/QList> +#include <QtGui/QPrinter> + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -56,15 +57,13 @@ class QPrinterInfoPrivate; class QPrinterInfoPrivateDeleter; class Q_GUI_EXPORT QPrinterInfo { -Q_DECLARE_PRIVATE(QPrinterInfo) - public: QPrinterInfo(); - QPrinterInfo(const QPrinterInfo& src); - QPrinterInfo(const QPrinter& printer); + QPrinterInfo(const QPrinterInfo &other); + QPrinterInfo(const QPrinter &printer); ~QPrinterInfo(); - QPrinterInfo& operator=(const QPrinterInfo& src); + QPrinterInfo &operator=(const QPrinterInfo &other); QString printerName() const; bool isNull() const; @@ -75,8 +74,10 @@ public: static QPrinterInfo defaultPrinter(); private: - QPrinterInfo(const QString& name); + QPrinterInfo(const QString &name); +private: + Q_DECLARE_PRIVATE(QPrinterInfo) QScopedPointer<QPrinterInfoPrivate, QPrinterInfoPrivateDeleter> d_ptr; }; diff --git a/src/gui/painting/qprinterinfo_mac.cpp b/src/gui/painting/qprinterinfo_mac.cpp index 9c8f180..455510d 100644 --- a/src/gui/painting/qprinterinfo_mac.cpp +++ b/src/gui/painting/qprinterinfo_mac.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qprinterinfo.h" +#include "qprinterinfo_p.h" #include "private/qt_mac_p.h" @@ -47,189 +48,71 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_PRINTER -class QPrinterInfoPrivate -{ -Q_DECLARE_PUBLIC(QPrinterInfo) -public: - ~QPrinterInfoPrivate(); - QPrinterInfoPrivate(); - QPrinterInfoPrivate(const QString& name); - -private: - QPrinterInfo* q_ptr; - - QString m_name; - bool m_default; - bool m_isNull; -}; - -static QPrinterInfoPrivate nullQPrinterInfoPrivate; - -class QPrinterInfoPrivateDeleter -{ -public: - static inline void cleanup(QPrinterInfoPrivate *d) - { - if (d != &nullQPrinterInfoPrivate) - delete d; - } -}; - -extern QPrinter::PaperSize qSizeFTopaperSize(const QSizeF& size); - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// +extern QPrinter::PaperSize qSizeFTopaperSize(const QSizeF &size); QList<QPrinterInfo> QPrinterInfo::availablePrinters() { QList<QPrinterInfo> printers; - OSStatus status = noErr; - QCFType<CFArrayRef> printerList; - status = PMServerCreatePrinterList(kPMServerLocal, &printerList); - if (status == noErr) { - CFIndex count = CFArrayGetCount(printerList); - for (CFIndex i=0; i<count; ++i) { - PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(printerList, i))); - QString name = QCFString::toQString(PMPrinterGetName(printer)); - printers.append(QPrinterInfo(name)); - if (PMPrinterIsDefault(printer)) { - printers[i].d_ptr->m_default = true; - } + QCFType<CFArrayRef> array; + if (PMServerCreatePrinterList(kPMServerLocal, &array) == noErr) { + CFIndex count = CFArrayGetCount(array); + for (int i = 0; i < count; ++i) { + PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(array, i))); + QString printerName = QCFString::toQString(PMPrinterGetName(printer)); + + QPrinterInfo printerInfo(printerName); + if (PMPrinterIsDefault(printer)) + printerInfo.d_ptr->isDefault = true; + printers.append(printerInfo); } } return printers; } -QPrinterInfo QPrinterInfo::defaultPrinter(){ - QList<QPrinterInfo> printers = availablePrinters(); - for (int c = 0; c < printers.size(); ++c) { - if (printers[c].isDefault()) { - return printers[c]; - } - } - return QPrinterInfo(); -} - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - -QPrinterInfo::QPrinterInfo(const QPrinter& prn) - : d_ptr(&nullQPrinterInfoPrivate) +QPrinterInfo QPrinterInfo::defaultPrinter() { - QList<QPrinterInfo> list = availablePrinters(); - for (int c = 0; c < list.size(); ++c) { - if (prn.printerName() == list[c].printerName()) { - *this = list[c]; - return; - } + QList<QPrinterInfo> printers = availablePrinters(); + foreach (const QPrinterInfo &printerInfo, printers) { + if (printerInfo.isDefault()) + return printerInfo; } -} - -QPrinterInfo::~QPrinterInfo() -{ -} - -QPrinterInfo::QPrinterInfo() - : d_ptr(&nullQPrinterInfoPrivate) -{ -} - -QPrinterInfo::QPrinterInfo(const QString& name) - : d_ptr(new QPrinterInfoPrivate(name)) -{ - d_ptr->q_ptr = this; -} - -QPrinterInfo::QPrinterInfo(const QPrinterInfo& src) - : d_ptr(&nullQPrinterInfoPrivate) -{ - *this = src; -} - -QPrinterInfo& QPrinterInfo::operator=(const QPrinterInfo& src) -{ - Q_ASSERT(d_ptr); - d_ptr.reset(new QPrinterInfoPrivate(*src.d_ptr)); - d_ptr->q_ptr = this; - return *this; -} - -QString QPrinterInfo::printerName() const -{ - const Q_D(QPrinterInfo); - return d->m_name; -} -bool QPrinterInfo::isNull() const -{ - const Q_D(QPrinterInfo); - return d->m_isNull; -} - -bool QPrinterInfo::isDefault() const -{ - const Q_D(QPrinterInfo); - return d->m_default; + return printers.value(0); } QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const { const Q_D(QPrinterInfo); - PMPrinter cfPrn = PMPrinterCreateFromPrinterID(QCFString::toCFStringRef(d->m_name)); + QList<QPrinter::PaperSize> paperSizes; + if (isNull()) + return paperSizes; - if (!cfPrn) return QList<QPrinter::PaperSize>(); + PMPrinter cfPrn = PMPrinterCreateFromPrinterID(QCFString::toCFStringRef(d->name)); + if (!cfPrn) + return paperSizes; CFArrayRef array; - OSStatus status = PMPrinterGetPaperList(cfPrn, &array); - - if (status != 0) { + if (PMPrinterGetPaperList(cfPrn, &array) != noErr) { PMRelease(cfPrn); - return QList<QPrinter::PaperSize>(); + return paperSizes; } - QList<QPrinter::PaperSize> paperList; int count = CFArrayGetCount(array); - for (int c = 0; c < count; c++) { - PMPaper paper = static_cast<PMPaper>( - const_cast<void*>( - CFArrayGetValueAtIndex(array, c))); + for (int i = 0; i < count; ++i) { + PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(array, i))); double width, height; - status = PMPaperGetWidth(paper, &width); - status |= PMPaperGetHeight(paper, &height); - if (status != 0) continue; - - QSizeF size(width * 0.3527, height * 0.3527); - paperList.append(qSizeFTopaperSize(size)); + if (PMPaperGetWidth(paper, &width) == noErr && PMPaperGetHeight(paper, &height) == noErr) { + QSizeF size(width * 0.3527, height * 0.3527); + paperSizes.append(qSizeFTopaperSize(size)); + } } PMRelease(cfPrn); - return paperList; -} - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - -QPrinterInfoPrivate::QPrinterInfoPrivate() : - q_ptr(NULL), - m_default(false), - m_isNull(true) -{ -} - -QPrinterInfoPrivate::QPrinterInfoPrivate(const QString& name) : - q_ptr(NULL), - m_name(name), - m_default(false), - m_isNull(false) -{ -} - -QPrinterInfoPrivate::~QPrinterInfoPrivate() -{ + return paperSizes; } #endif // QT_NO_PRINTER diff --git a/src/gui/painting/qprinterinfo_p.h b/src/gui/painting/qprinterinfo_p.h new file mode 100644 index 0000000..a3f7d5d --- /dev/null +++ b/src/gui/painting/qprinterinfo_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPRINTERINFO_P_H +#define QPRINTERINFO_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qglobal.h" + +#ifndef QT_NO_PRINTER + +#include "QtCore/qlist.h" + +QT_BEGIN_NAMESPACE + +class QPrinterInfoPrivate +{ +public: + QPrinterInfoPrivate(const QString& name = QString()) : + name(name), isDefault(false) +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_SYMBIAN)) || defined(Q_WS_QPA) +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + , cupsPrinterIndex(0), hasPaperSizes(false) +#endif +#endif + {} + ~QPrinterInfoPrivate() + {} + + static QPrinterInfoPrivate shared_null; + + QString name; + bool isDefault; + +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_SYMBIAN)) || defined(Q_WS_QPA) +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + int cupsPrinterIndex; + mutable bool hasPaperSizes; + mutable QList<QPrinter::PaperSize> paperSizes; +#endif +#endif +}; + + +class QPrinterInfoPrivateDeleter +{ +public: + static inline void cleanup(QPrinterInfoPrivate *d) + { + if (d != &QPrinterInfoPrivate::shared_null) + delete d; + } +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER + +#endif // QPRINTERINFO_P_H diff --git a/src/gui/painting/qprinterinfo_unix.cpp b/src/gui/painting/qprinterinfo_unix.cpp index b848579..aa220aa 100644 --- a/src/gui/painting/qprinterinfo_unix.cpp +++ b/src/gui/painting/qprinterinfo_unix.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qprinterinfo.h" +#include "qprinterinfo_p.h" #include <qfile.h> #include <qfileinfo.h> @@ -60,42 +61,66 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_PRINTER -class QPrinterInfoPrivate -{ -Q_DECLARE_PUBLIC(QPrinterInfo) -public: - QPrinterInfoPrivate(); - QPrinterInfoPrivate(const QString& name); - ~QPrinterInfoPrivate(); - - static QPrinter::PaperSize string2PaperSize(const QString& str); - static QString pageSize2String(QPrinter::PaperSize size); - -private: - QString m_name; - bool m_isNull; - bool m_default; - mutable bool m_mustGetPaperSizes; - mutable QList<QPrinter::PaperSize> m_paperSizes; - int m_cupsPrinterIndex; - - QPrinterInfo* q_ptr; +#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) +// preserver names in ascending order for the binary search +static const struct NamedPaperSize { + const char *const name; + QPrinter::PaperSize size; +} named_sizes_map[QPrinter::NPageSize] = { + { "A0", QPrinter::A0 }, + { "A1", QPrinter::A1 }, + { "A2", QPrinter::A2 }, + { "A3", QPrinter::A3 }, + { "A4", QPrinter::A4 }, + { "A5", QPrinter::A5 }, + { "A6", QPrinter::A6 }, + { "A7", QPrinter::A7 }, + { "A8", QPrinter::A8 }, + { "A9", QPrinter::A9 }, + { "B0", QPrinter::B0 }, + { "B1", QPrinter::B1 }, + { "B10", QPrinter::B10 }, + { "B2", QPrinter::B2 }, + { "B4", QPrinter::B4 }, + { "B5", QPrinter::B5 }, + { "B6", QPrinter::B6 }, + { "B7", QPrinter::B7 }, + { "B8", QPrinter::B8 }, + { "B9", QPrinter::B9 }, + { "C5E", QPrinter::C5E }, + { "Comm10E", QPrinter::Comm10E }, + { "Custom", QPrinter::Custom }, + { "DLE", QPrinter::DLE }, + { "Executive", QPrinter::Executive }, + { "Folio", QPrinter::Folio }, + { "Ledger", QPrinter::Ledger }, + { "Legal", QPrinter::Legal }, + { "Letter", QPrinter::Letter }, + { "Tabloid", QPrinter::Tabloid } }; -static QPrinterInfoPrivate nullQPrinterInfoPrivate; +inline bool operator<(const char *name, const NamedPaperSize &data) +{ return qstrcmp(name, data.name) < 0; } +inline bool operator<(const NamedPaperSize &data, const char *name) +{ return qstrcmp(data.name, name) < 0; } -class QPrinterInfoPrivateDeleter +static inline QPrinter::PaperSize string2PaperSize(const char *name) { -public: - static inline void cleanup(QPrinterInfoPrivate *d) - { - if (d != &nullQPrinterInfoPrivate) - delete d; - } -}; + const NamedPaperSize *r = qBinaryFind(named_sizes_map, named_sizes_map + QPrinter::NPageSize, name); + if (r - named_sizes_map != QPrinter::NPageSize) + return r->size; + return QPrinter::Custom; +} -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// +static inline const char *paperSize2String(QPrinter::PaperSize size) +{ + for (int i = 0; i < QPrinter::NPageSize; ++i) { + if (size == named_sizes_map[i].size) + return named_sizes_map[i].name; + } + return 0; +} +#endif void qt_perhapsAddPrinter(QList<QPrinterDescription> *printers, const QString &name, QString host, QString comment, @@ -784,12 +809,12 @@ int qt_getLprPrinters(QList<QPrinterDescription>& printers) #endif } + QRegExp ps(QLatin1String("[^a-z]ps(?:[^a-z]|$)")); + QRegExp lp(QLatin1String("[^a-z]lp(?:[^a-z]|$)")); + int quality = 0; int best = 0; for (int i = 0; i < printers.size(); ++i) { - QRegExp ps(QLatin1String("[^a-z]ps(?:[^a-z]|$)")); - QRegExp lp(QLatin1String("[^a-z]lp(?:[^a-z]|$)")); - QString name = printers.at(i).name; QString comment = printers.at(i).comment; if (quality < 5 && name == dollarPrinter) { @@ -824,331 +849,77 @@ int qt_getLprPrinters(QList<QPrinterDescription>& printers) QList<QPrinterInfo> QPrinterInfo::availablePrinters() { - QList<QPrinterInfo> list; + QList<QPrinterInfo> printers; #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) - QCUPSSupport cups; if (QCUPSSupport::isAvailable()) { - //const ppd_file_t* cupsPPD = cups.currentPPD(); + QCUPSSupport cups; int cupsPrinterCount = cups.availablePrintersCount(); const cups_dest_t* cupsPrinters = cups.availablePrinters(); - for (int i = 0; i < cupsPrinterCount; ++i) { QString printerName(QString::fromLocal8Bit(cupsPrinters[i].name)); if (cupsPrinters[i].instance) printerName += QLatin1Char('/') + QString::fromLocal8Bit(cupsPrinters[i].instance); - list.append(QPrinterInfo(printerName)); + + QPrinterInfo printerInfo(printerName); if (cupsPrinters[i].is_default) - list[i].d_ptr->m_default = true; - list[i].d_ptr->m_cupsPrinterIndex = i; + printerInfo.d_ptr->isDefault = true; + printerInfo.d_ptr->cupsPrinterIndex = i; + printers.append(printerInfo); } - } else { + } else #endif + { QList<QPrinterDescription> lprPrinters; int defprn = qt_getLprPrinters(lprPrinters); // populating printer combo - QList<QPrinterDescription>::const_iterator i = lprPrinters.constBegin(); - for(; i != lprPrinters.constEnd(); ++i) { - list.append(QPrinterInfo((*i).name)); - } - if (defprn >= 0 && defprn < lprPrinters.size()) { - list[defprn].d_ptr->m_default = true; - } -#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) + foreach (const QPrinterDescription &description, lprPrinters) + printers.append(QPrinterInfo(description.name)); + if (defprn >= 0 && defprn < printers.size()) + printers[defprn].d_ptr->isDefault = true; } -#endif - return list; + return printers; } QPrinterInfo QPrinterInfo::defaultPrinter() { - QList<QPrinterInfo> prnList = availablePrinters(); - for (int i = 0; i < prnList.size(); ++i) { - if (prnList[i].isDefault()) - return prnList[i]; + QList<QPrinterInfo> printers = availablePrinters(); + foreach (const QPrinterInfo &printerInfo, printers) { + if (printerInfo.isDefault()) + return printerInfo; } - return (prnList.size() > 0) ? prnList[0] : QPrinterInfo(); -} - -QPrinterInfo::QPrinterInfo() - : d_ptr(&nullQPrinterInfoPrivate) -{ -} -QPrinterInfo::QPrinterInfo(const QPrinterInfo& src) - : d_ptr(&nullQPrinterInfoPrivate) -{ - *this = src; + return printers.value(0); } -QPrinterInfo::QPrinterInfo(const QPrinter& printer) - : d_ptr(new QPrinterInfoPrivate(printer.printerName())) +QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const { - - Q_D(QPrinterInfo); - d->q_ptr = this; - #if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) - QCUPSSupport cups; - if (QCUPSSupport::isAvailable()) { - int cupsPrinterCount = cups.availablePrintersCount(); - const cups_dest_t* cupsPrinters = cups.availablePrinters(); - - for (int i = 0; i < cupsPrinterCount; ++i) { - QString printerName(QString::fromLocal8Bit(cupsPrinters[i].name)); - if (cupsPrinters[i].instance) - printerName += QLatin1Char('/') + QString::fromLocal8Bit(cupsPrinters[i].instance); - if (printerName == printer.printerName()) { - if (cupsPrinters[i].is_default) - d->m_default = true; - d->m_cupsPrinterIndex = i; - return; - } - } - } else { -#endif - QList<QPrinterDescription> lprPrinters; - int defprn = qt_getLprPrinters(lprPrinters); - // populating printer combo - QList<QPrinterDescription>::const_iterator i = lprPrinters.constBegin(); - int c; - for(c = 0; i != lprPrinters.constEnd(); ++i, ++c) { - if (i->name == printer.printerName()) { - if (defprn == c) - d->m_default = true; - return; - } - } -#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) - } -#endif - - // Printer not found. - d_ptr.reset(&nullQPrinterInfoPrivate); -} - -QPrinterInfo::QPrinterInfo(const QString& name) - : d_ptr(new QPrinterInfoPrivate(name)) -{ - d_ptr->q_ptr = this; -} - -QPrinterInfo::~QPrinterInfo() -{ -} - -QPrinterInfo& QPrinterInfo::operator=(const QPrinterInfo& src) -{ - Q_ASSERT(d_ptr); - d_ptr.reset(new QPrinterInfoPrivate(*src.d_ptr)); - d_ptr->q_ptr = this; - return *this; -} - -QString QPrinterInfo::printerName() const -{ const Q_D(QPrinterInfo); - return d->m_name; -} - -bool QPrinterInfo::isNull() const -{ - const Q_D(QPrinterInfo); - return d->m_isNull; -} -bool QPrinterInfo::isDefault() const -{ - const Q_D(QPrinterInfo); - return d->m_default; -} + if (isNull()) + return d->paperSizes; -QList< QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const -{ - const Q_D(QPrinterInfo); - if (d->m_mustGetPaperSizes) { - d->m_mustGetPaperSizes = false; + if (!d->hasPaperSizes) { + d->hasPaperSizes = true; -#if !defined(QT_NO_CUPS) && !defined(QT_NO_LIBRARY) - QCUPSSupport cups; if (QCUPSSupport::isAvailable()) { // Find paper sizes from CUPS. - cups.setCurrentPrinter(d->m_cupsPrinterIndex); + QCUPSSupport cups; + cups.setCurrentPrinter(d->cupsPrinterIndex); const ppd_option_t* sizes = cups.pageSizes(); if (sizes) { - for (int j = 0; j < sizes->num_choices; ++j) { - d->m_paperSizes.append( - QPrinterInfoPrivate::string2PaperSize( - QLatin1String(sizes->choices[j].choice))); - } + for (int j = 0; j < sizes->num_choices; ++j) + d->paperSizes.append(string2PaperSize(sizes->choices[j].choice)); } } -#endif - } - return d->m_paperSizes; -} - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - -QPrinterInfoPrivate::QPrinterInfoPrivate() -{ - m_isNull = true; - m_default = false; - m_mustGetPaperSizes = true; - m_cupsPrinterIndex = 0; - q_ptr = 0; -} - -QPrinterInfoPrivate::QPrinterInfoPrivate(const QString& name) -{ - m_name = name; - m_isNull = false; - m_default = false; - m_mustGetPaperSizes = true; - m_cupsPrinterIndex = 0; - q_ptr = 0; -} -QPrinterInfoPrivate::~QPrinterInfoPrivate() -{ -} - -QPrinter::PaperSize QPrinterInfoPrivate::string2PaperSize(const QString& str) -{ - if (str == QLatin1String("A4")) { - return QPrinter::A4; - } else if (str == QLatin1String("B5")) { - return QPrinter::B5; - } else if (str == QLatin1String("Letter")) { - return QPrinter::Letter; - } else if (str == QLatin1String("Legal")) { - return QPrinter::Legal; - } else if (str == QLatin1String("Executive")) { - return QPrinter::Executive; - } else if (str == QLatin1String("A0")) { - return QPrinter::A0; - } else if (str == QLatin1String("A1")) { - return QPrinter::A1; - } else if (str == QLatin1String("A2")) { - return QPrinter::A2; - } else if (str == QLatin1String("A3")) { - return QPrinter::A3; - } else if (str == QLatin1String("A5")) { - return QPrinter::A5; - } else if (str == QLatin1String("A6")) { - return QPrinter::A6; - } else if (str == QLatin1String("A7")) { - return QPrinter::A7; - } else if (str == QLatin1String("A8")) { - return QPrinter::A8; - } else if (str == QLatin1String("A9")) { - return QPrinter::A9; - } else if (str == QLatin1String("B0")) { - return QPrinter::B0; - } else if (str == QLatin1String("B1")) { - return QPrinter::B1; - } else if (str == QLatin1String("B10")) { - return QPrinter::B10; - } else if (str == QLatin1String("B2")) { - return QPrinter::B2; - } else if (str == QLatin1String("B3")) { - return QPrinter::B3; - } else if (str == QLatin1String("B4")) { - return QPrinter::B4; - } else if (str == QLatin1String("B6")) { - return QPrinter::B6; - } else if (str == QLatin1String("B7")) { - return QPrinter::B7; - } else if (str == QLatin1String("B8")) { - return QPrinter::B8; - } else if (str == QLatin1String("B9")) { - return QPrinter::B9; - } else if (str == QLatin1String("C5E")) { - return QPrinter::C5E; - } else if (str == QLatin1String("Comm10E")) { - return QPrinter::Comm10E; - } else if (str == QLatin1String("DLE")) { - return QPrinter::DLE; - } else if (str == QLatin1String("Folio")) { - return QPrinter::Folio; - } else if (str == QLatin1String("Ledger")) { - return QPrinter::Ledger; - } else if (str == QLatin1String("Tabloid")) { - return QPrinter::Tabloid; - } else { - return QPrinter::Custom; - } -} - -QString QPrinterInfoPrivate::pageSize2String(QPrinter::PaperSize size) -{ - switch (size) { - case QPrinter::A4: - return QLatin1String("A4"); - case QPrinter::B5: - return QLatin1String("B5"); - case QPrinter::Letter: - return QLatin1String("Letter"); - case QPrinter::Legal: - return QLatin1String("Legal"); - case QPrinter::Executive: - return QLatin1String("Executive"); - case QPrinter::A0: - return QLatin1String("A0"); - case QPrinter::A1: - return QLatin1String("A1"); - case QPrinter::A2: - return QLatin1String("A2"); - case QPrinter::A3: - return QLatin1String("A3"); - case QPrinter::A5: - return QLatin1String("A5"); - case QPrinter::A6: - return QLatin1String("A6"); - case QPrinter::A7: - return QLatin1String("A7"); - case QPrinter::A8: - return QLatin1String("A8"); - case QPrinter::A9: - return QLatin1String("A9"); - case QPrinter::B0: - return QLatin1String("B0"); - case QPrinter::B1: - return QLatin1String("B1"); - case QPrinter::B10: - return QLatin1String("B10"); - case QPrinter::B2: - return QLatin1String("B2"); - case QPrinter::B3: - return QLatin1String("B3"); - case QPrinter::B4: - return QLatin1String("B4"); - case QPrinter::B6: - return QLatin1String("B6"); - case QPrinter::B7: - return QLatin1String("B7"); - case QPrinter::B8: - return QLatin1String("B8"); - case QPrinter::B9: - return QLatin1String("B9"); - case QPrinter::C5E: - return QLatin1String("C5E"); - case QPrinter::Comm10E: - return QLatin1String("Comm10E"); - case QPrinter::DLE: - return QLatin1String("DLE"); - case QPrinter::Folio: - return QLatin1String("Folio"); - case QPrinter::Ledger: - return QLatin1String("Ledger"); - case QPrinter::Tabloid: - return QLatin1String("Tabloid"); - default: - return QLatin1String("Custom"); - } + return d->paperSizes; +#else + return QList<QPrinter::PaperSize>(); +#endif } #endif // QT_NO_PRINTER diff --git a/src/gui/painting/qprinterinfo_win.cpp b/src/gui/painting/qprinterinfo_win.cpp index 22e4c78..cc0cd02 100644 --- a/src/gui/painting/qprinterinfo_win.cpp +++ b/src/gui/painting/qprinterinfo_win.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qprinterinfo.h" +#include "qprinterinfo_p.h" #include <qstringlist.h> @@ -51,59 +52,25 @@ QT_BEGIN_NAMESPACE extern QPrinter::PaperSize mapDevmodePaperSize(int s); -class QPrinterInfoPrivate -{ -Q_DECLARE_PUBLIC(QPrinterInfo) -public: - ~QPrinterInfoPrivate(); - QPrinterInfoPrivate(); - QPrinterInfoPrivate(const QString& name); - -private: - QString m_name; - bool m_default; - bool m_isNull; - - QPrinterInfo* q_ptr; -}; - -static QPrinterInfoPrivate nullQPrinterInfoPrivate; - -class QPrinterInfoPrivateDeleter -{ -public: - static inline void cleanup(QPrinterInfoPrivate *d) - { - if (d != &nullQPrinterInfoPrivate) - delete d; - } -}; - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - QList<QPrinterInfo> QPrinterInfo::availablePrinters() { QList<QPrinterInfo> printers; - LPBYTE buffer; + DWORD needed = 0; DWORD returned = 0; - - if ( !EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, 0, 0, &needed, &returned)) - { - buffer = new BYTE[needed]; - if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS , NULL, - 4, buffer, needed, &needed, &returned)) - { - delete [] buffer; - return printers; - } - PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer); - QPrinterInfo defPrn = defaultPrinter(); - for (uint i = 0; i < returned; ++i) { - printers.append(QPrinterInfo(QString::fromWCharArray(infoList[i].pPrinterName))); - if (printers.at(i).printerName() == defPrn.printerName()) - printers[i].d_ptr->m_default = true; + if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, 0, 0, &needed, &returned)) { + LPBYTE buffer = new BYTE[needed]; + if (EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, buffer, needed, &needed, &returned)) { + PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer); + QPrinterInfo defPrn = defaultPrinter(); + for (uint i = 0; i < returned; ++i) { + QString printerName(QString::fromWCharArray(infoList[i].pPrinterName)); + + QPrinterInfo printerInfo(printerName); + if (printerInfo.printerName() == defPrn.printerName()) + printerInfo.d_ptr->isDefault = true; + printers.append(printerInfo); + } } delete [] buffer; } @@ -117,127 +84,37 @@ QPrinterInfo QPrinterInfo::defaultPrinter() wchar_t buffer[256]; GetProfileString(L"windows", L"device", (wchar_t*)noPrinters.utf16(), buffer, 256); QString output = QString::fromWCharArray(buffer); - - // Filter out the name of the printer, which should be everything - // before a comma. - bool noConfiguredPrinters = (output == noPrinters); - QStringList info = output.split(QLatin1Char(',')); - QString printerName = noConfiguredPrinters ? QString() : info.at(0); - - QPrinterInfo prn(printerName); - prn.d_ptr->m_default = true; - if (noConfiguredPrinters) - prn.d_ptr->m_isNull = true; - return prn; -} - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - -QPrinterInfo::QPrinterInfo() - : d_ptr(&nullQPrinterInfoPrivate) -{ -} - -QPrinterInfo::QPrinterInfo(const QString& name) - : d_ptr(new QPrinterInfoPrivate(name)) -{ - d_ptr->q_ptr = this; -} - -QPrinterInfo::QPrinterInfo(const QPrinterInfo& src) - : d_ptr(&nullQPrinterInfoPrivate) -{ - *this = src; -} - -QPrinterInfo::QPrinterInfo(const QPrinter& prn) - : d_ptr(&nullQPrinterInfoPrivate) -{ - QList<QPrinterInfo> list = availablePrinters(); - for (int c = 0; c < list.size(); ++c) { - if (prn.printerName() == list[c].printerName()) { - *this = list[c]; - return; - } + if (output != noPrinters) { + // Filter out the name of the printer, which should be everything before a comma. + QString printerName = output.split(QLatin1Char(',')).value(0); + QPrinterInfo printerInfo(printerName); + printerInfo.d_ptr->isDefault = true; + return printerInfo; } - *this = QPrinterInfo(); -} - -QPrinterInfo::~QPrinterInfo() -{ -} - -QPrinterInfo& QPrinterInfo::operator=(const QPrinterInfo& src) -{ - Q_ASSERT(d_ptr); - d_ptr.reset(new QPrinterInfoPrivate(*src.d_ptr)); - d_ptr->q_ptr = this; - return *this; -} - -QString QPrinterInfo::printerName() const -{ - const Q_D(QPrinterInfo); - return d->m_name; -} - -bool QPrinterInfo::isNull() const -{ - const Q_D(QPrinterInfo); - return d->m_isNull; -} - -bool QPrinterInfo::isDefault() const -{ - const Q_D(QPrinterInfo); - return d->m_default; + return QPrinterInfo(); } QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const { const Q_D(QPrinterInfo); - QList<QPrinter::PaperSize> paperList; - DWORD size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->m_name.utf16()), - NULL, DC_PAPERS, NULL, NULL); - if ((int)size == -1) - return paperList; - - wchar_t *papers = new wchar_t[size]; - size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->m_name.utf16()), - NULL, DC_PAPERS, papers, NULL); + QList<QPrinter::PaperSize> paperSizes; + if (isNull()) + return paperSizes; - for (int c = 0; c < (int)size; ++c) { - paperList.append(mapDevmodePaperSize(papers[c])); + DWORD size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()), + NULL, DC_PAPERS, NULL, NULL); + if ((int)size != -1) { + wchar_t *papers = new wchar_t[size]; + size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()), + NULL, DC_PAPERS, papers, NULL); + for (int c = 0; c < (int)size; ++c) + paperSizes.append(mapDevmodePaperSize(papers[c])); + delete [] papers; } - delete [] papers; - - return paperList; -} - -///////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// - -QPrinterInfoPrivate::QPrinterInfoPrivate() : - m_default(false), - m_isNull(true), - q_ptr(NULL) -{ -} - -QPrinterInfoPrivate::QPrinterInfoPrivate(const QString& name) : - m_name(name), - m_default(false), - m_isNull(false), - q_ptr(NULL) -{ -} - -QPrinterInfoPrivate::~QPrinterInfoPrivate() -{ + return paperSizes; } #endif // QT_NO_PRINTER diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp index 821705c..eb9fbf7 100644 --- a/src/gui/painting/qregion.cpp +++ b/src/gui/painting/qregion.cpp @@ -391,6 +391,14 @@ void QRegion::exec(const QByteArray &buffer, int ver, QDataStream::ByteOrder byt */ /*! + \fn void QRegion::swap(QRegion &other) + \since 4.8 + + Swaps region \a other with this region. This operation is very + fast and never fails. +*/ + +/*! \relates QRegion Writes the region \a r to the stream \a s and returns a reference @@ -1620,7 +1628,7 @@ QT_END_INCLUDE_NAMESPACE QT_BEGIN_INCLUDE_NAMESPACE # include "qregion_win.cpp" QT_END_INCLUDE_NAMESPACE -#elif defined(Q_WS_QWS) +#elif defined(Q_WS_QWS) || defined(Q_WS_QPA) static QRegionPrivate qrp; QRegion::QRegionData QRegion::shared_empty = {Q_BASIC_ATOMIC_INITIALIZER(1), &qrp}; #endif @@ -4231,7 +4239,7 @@ QRect QRegion::boundingRect() const Returns true if \a rect is guaranteed to be fully contained in \a region. A false return value does not guarantee the opposite. */ -#ifdef Q_WS_QWS +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) Q_GUI_EXPORT #endif bool qt_region_strictContains(const QRegion ®ion, const QRect &rect) diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h index f24f6fd..f4c68a9 100644 --- a/src/gui/painting/qregion.h +++ b/src/gui/painting/qregion.h @@ -59,7 +59,7 @@ QT_MODULE(Gui) template <class T> class QVector; class QVariant; -#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) struct QRegionPrivate; #endif @@ -81,7 +81,11 @@ public: QRegion(const QBitmap &bitmap); ~QRegion(); QRegion &operator=(const QRegion &); - +#ifdef Q_COMPILER_RVALUE_REFS + inline QRegion &operator=(QRegion &&other) + { qSwap(d, other.d); return *this; } +#endif + inline void swap(QRegion &other) { qSwap(d, other.d); } #ifdef QT3_SUPPORT inline QT3_SUPPORT bool isNull() const { return isEmpty(); } #endif @@ -163,7 +167,7 @@ public: #endif HIMutableShapeRef toHIMutableShape() const; static QRegion fromHIShapeRef(HIShapeRef shape); -#elif defined(Q_WS_QWS) +#elif defined(Q_WS_QWS) || defined(Q_WS_QPA) inline void *handle() const { return d->qt_rgn; } #endif #endif @@ -203,7 +207,7 @@ private: #elif defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA) mutable RgnHandle unused; // Here for binary compatibility reasons. ### Qt 5 remove. #endif -#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) QRegionPrivate *qt_rgn; #endif }; diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index 448c972..bbb951e 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -552,6 +552,7 @@ void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine // // line to the beginning of the arc segment, (should not be needed). // emitLineTo(qt_real_to_fixed(curve_start.x()), qt_real_to_fixed(curve_start.y())); + Q_UNUSED(curve_start); for (int i=0; i<point_count; i+=3) { emitCubicTo(qt_real_to_fixed(curves[i].x()), @@ -668,26 +669,28 @@ template <class Iterator> bool qt_stroke_side(Iterator *it, #endif QLineF line(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y), qt_fixed_to_real(e.x), qt_fixed_to_real(e.y)); - QLineF normal = line.normalVector(); - normal.setLength(offset); - line.translate(normal.dx(), normal.dy()); - - // If we are starting a new subpath, move to correct starting point. - if (first) { - if (capFirst) - stroker->joinPoints(prev.x, prev.y, line, stroker->capStyleMode()); - else - stroker->emitMoveTo(qt_real_to_fixed(line.x1()), qt_real_to_fixed(line.y1())); - *startTangent = line; - first = false; - } else { - stroker->joinPoints(prev.x, prev.y, line, stroker->joinStyleMode()); - } + if (line.p1() != line.p2()) { + QLineF normal = line.normalVector(); + normal.setLength(offset); + line.translate(normal.dx(), normal.dy()); + + // If we are starting a new subpath, move to correct starting point. + if (first) { + if (capFirst) + stroker->joinPoints(prev.x, prev.y, line, stroker->capStyleMode()); + else + stroker->emitMoveTo(qt_real_to_fixed(line.x1()), qt_real_to_fixed(line.y1())); + *startTangent = line; + first = false; + } else { + stroker->joinPoints(prev.x, prev.y, line, stroker->joinStyleMode()); + } - // Add the stroke for this line. - stroker->emitLineTo(qt_real_to_fixed(line.x2()), - qt_real_to_fixed(line.y2())); - prev = e; + // Add the stroke for this line. + stroker->emitLineTo(qt_real_to_fixed(line.x2()), + qt_real_to_fixed(line.y2())); + prev = e; + } // CurveToElement } else if (e.isCurveTo()) { diff --git a/src/gui/painting/qtessellator.cpp b/src/gui/painting/qtessellator.cpp index c469438..94a5128 100644 --- a/src/gui/painting/qtessellator.cpp +++ b/src/gui/painting/qtessellator.cpp @@ -893,7 +893,7 @@ void QTessellatorPrivate::processIntersections() QDEBUG() << " adding edge on left"; --min; } - while (max < scanline.size - 1 && scanline.edges[max + 1]->positionAt(y) <= xmax) { + while (max + 1 < scanline.size && scanline.edges[max + 1]->positionAt(y) <= xmax) { QDEBUG() << " adding edge on right"; ++max; } diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index 2c1da24..3973abd 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -65,8 +65,60 @@ static inline int qt_next_power_of_two(int v) return v; } +int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const +{ + // Test 12 different subpixel positions since it factors into 3*4 so it gives + // the coverage we need. + + QList<QImage> images; + for (int i=0; i<12; ++i) { + QImage img = textureMapForGlyph(glyph, QFixed::fromReal(i / 12.0)); + + if (images.isEmpty()) { + QPainterPath path; + QFixedPoint point; + m_current_fontengine->addGlyphsToPath(&glyph, &point, 1, &path, QTextItem::RenderFlags()); + + // Glyph is space, return 0 to indicate that we need to keep trying + if (path.isEmpty()) + break; + + images.append(img); + } else { + bool found = false; + for (int j=0; j<images.size(); ++j) { + if (images.at(j) == img) { + found = true; + break; + } + } + if (!found) + images.append(img); + } + } + + return images.size(); +} + +QFixed QTextureGlyphCache::subPixelPositionForX(QFixed x) const +{ + if (m_subPixelPositionCount <= 1) + return QFixed(); + + QFixed subPixelPosition; + if (x != 0) { + subPixelPosition = x - x.floor(); + QFixed fraction = (subPixelPosition / QFixed::fromReal(1.0 / m_subPixelPositionCount)).floor(); + + // Compensate for precision loss in fixed point to make sure we are always drawing at a subpixel position over + // the lower boundary for the selected rasterization by adding 1/64. + subPixelPosition = fraction / QFixed(m_subPixelPositionCount) + QFixed::fromReal(0.015625); + } + return subPixelPosition; +} + bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs, - const QFixedPoint *) + const QFixedPoint *positions) { #ifdef CACHE_DEBUG printf("Populating with %d glyphs\n", numGlyphs); @@ -77,17 +129,46 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const const int margin = glyphMargin(); const int paddingDoubled = glyphPadding() * 2; - QHash<glyph_t, Coord> listItemCoordinates; + bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions(); + if (m_subPixelPositionCount == 0) { + if (!supportsSubPixelPositions) { + m_subPixelPositionCount = 1; + } else { +#if !defined(Q_WS_X11) + int i = 0; + while (m_subPixelPositionCount == 0 && i < numGlyphs) + m_subPixelPositionCount = calculateSubPixelPositionCount(glyphs[i++]); +#else + m_subPixelPositionCount = 4; +#endif + } + } + + QHash<GlyphAndSubPixelPosition, Coord> listItemCoordinates; int rowHeight = 0; + QFontEngine::GlyphFormat format; + switch (m_type) { + case Raster_A8: format = QFontEngine::Format_A8; break; + case Raster_RGBMask: format = QFontEngine::Format_A32; break; + default: format = QFontEngine::Format_Mono; break; + } + // check each glyph for its metrics and get the required rowHeight. for (int i=0; i < numGlyphs; ++i) { const glyph_t glyph = glyphs[i]; - if (coords.contains(glyph)) + + QFixed subPixelPosition; + if (supportsSubPixelPositions) { + QFixed x = positions != 0 ? positions[i].x : QFixed(); + subPixelPosition = subPixelPositionForX(x); + } + + if (coords.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition))) continue; - if (listItemCoordinates.contains(glyph)) + if (listItemCoordinates.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition))) continue; - glyph_metrics_t metrics = fontEngine->boundingBox(glyph, m_transform); + glyph_metrics_t metrics = fontEngine->alphaMapBoundingBox(glyph, subPixelPosition, m_transform, format); #ifdef CACHE_DEBUG printf("(%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n", @@ -98,11 +179,16 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const metrics.yoff.toReal(), metrics.x.toReal(), metrics.y.toReal()); -#endif +#endif + GlyphAndSubPixelPosition key(glyph, subPixelPosition); int glyph_width = metrics.width.ceil().toInt(); int glyph_height = metrics.height.ceil().toInt(); - if (glyph_height == 0 || glyph_width == 0) + if (glyph_height == 0 || glyph_width == 0) { + // Avoid multiple calls to boundingBox() for non-printable characters + Coord c = { 0, 0, 0, 0, 0, 0 }; + coords.insert(key, c); continue; + } glyph_width += margin * 2 + 4; glyph_height += margin * 2 + 4; // align to 8-bit boundary @@ -112,33 +198,38 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const Coord c = { 0, 0, // will be filled in later glyph_width, glyph_height, // texture coords - metrics.x.round().truncate(), + metrics.x.truncate(), -metrics.y.truncate() }; // baseline for horizontal scripts - listItemCoordinates.insert(glyph, c); + listItemCoordinates.insert(key, c); rowHeight = qMax(rowHeight, glyph_height); } if (listItemCoordinates.isEmpty()) return true; rowHeight += margin * 2 + paddingDoubled; - if (isNull()) - createCache(QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH, qt_next_power_of_two(rowHeight)); + + if (m_w == 0) { + if (fontEngine->maxCharWidth() <= QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH) + m_w = QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH; + else + m_w = qt_next_power_of_two(fontEngine->maxCharWidth()); + } // now actually use the coords and paint the wanted glyps into cache. - QHash<glyph_t, Coord>::iterator iter = listItemCoordinates.begin(); + QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = listItemCoordinates.begin(); + int requiredWidth = m_w; while (iter != listItemCoordinates.end()) { Coord c = iter.value(); m_currentRowHeight = qMax(m_currentRowHeight, c.h + margin * 2); - if (m_cx + c.w > m_w) { - int new_width = m_w*2; + if (m_cx + c.w > requiredWidth) { + int new_width = requiredWidth*2; while (new_width < m_cx + c.w) new_width *= 2; if (new_width <= maxTextureWidth()) { - resizeTextureData(new_width, m_h); - m_w = new_width; + requiredWidth = new_width; } else { // no room on the current line, start new glyph strip m_cx = 0; @@ -146,36 +237,63 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const m_currentRowHeight = c.h + margin * 2; // New row } } - if (m_cy + c.h > m_h) { - int new_height = m_h*2; - while (new_height < m_cy + c.h) - new_height *= 2; - - if (maxTextureHeight() > 0 && new_height > maxTextureHeight()) { - // We can't make a new texture of the required size, so - // bail out - return false; - } - // if no room in the current texture - realloc a larger texture - resizeTextureData(m_w, new_height); - m_h = new_height; + if (maxTextureHeight() > 0 && m_cy + c.h > maxTextureHeight()) { + // We can't make a cache of the required size, so we bail out + return false; } c.x = m_cx; c.y = m_cy; - fillTexture(c, iter.key()); coords.insert(iter.key(), c); + m_pendingGlyphs.insert(iter.key(), c); m_cx += c.w + paddingDoubled; ++iter; } - return true; + } -QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const +void QTextureGlyphCache::fillInPendingGlyphs() +{ + if (m_pendingGlyphs.isEmpty()) + return; + + int requiredHeight = m_h; + int requiredWidth = m_w; // Use a minimum size to avoid a lot of initial reallocations + { + QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = m_pendingGlyphs.begin(); + while (iter != m_pendingGlyphs.end()) { + Coord c = iter.value(); + requiredHeight = qMax(requiredHeight, c.y + c.h); + requiredWidth = qMax(requiredWidth, c.x + c.w); + ++iter; + } + } + + if (isNull() || requiredHeight > m_h || requiredWidth > m_w) { + if (isNull()) + createCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight)); + else + resizeCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight)); + } + + { + QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = m_pendingGlyphs.begin(); + while (iter != m_pendingGlyphs.end()) { + GlyphAndSubPixelPosition key = iter.key(); + fillTexture(iter.value(), key.glyph, key.subPixelPosition); + + ++iter; + } + } + + m_pendingGlyphs.clear(); +} + +QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g, QFixed subPixelPosition) const { #if defined(Q_WS_X11) if (m_type != Raster_RGBMask && m_transform.type() > QTransform::TxTranslate && m_current_fontengine->type() == QFontEngine::Freetype) { @@ -190,13 +308,19 @@ QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const format = QFontEngineFT::Format_Mono; imageFormat = QImage::Format_Mono; break; + case Raster_RGBMask: + // impossible condition (see the if-clause above) + // this option is here only to silence a compiler warning + break; }; QFontEngineFT *ft = static_cast<QFontEngineFT*> (m_current_fontengine); QFontEngineFT::QGlyphSet *gset = ft->loadTransformedGlyphSet(m_transform); + QFixedPoint positions[1]; + positions[0].x = subPixelPosition; - if (gset && ft->loadGlyphs(gset, &g, 1, format)) { - QFontEngineFT::Glyph *glyph = gset->getGlyph(g); + if (gset && ft->loadGlyphs(gset, &g, 1, positions, format)) { + QFontEngineFT::Glyph *glyph = gset->getGlyph(g, subPixelPosition); const int bytesPerLine = (format == QFontEngineFT::Format_Mono ? ((glyph->width + 31) & ~31) >> 3 : (glyph->width + 3) & ~3); return QImage(glyph->data, glyph->width, glyph->height, bytesPerLine, imageFormat); @@ -204,9 +328,9 @@ QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const } else #endif if (m_type == QFontEngineGlyphCache::Raster_RGBMask) - return m_current_fontengine->alphaRGBMapForGlyph(g, glyphMargin(), m_transform); + return m_current_fontengine->alphaRGBMapForGlyph(g, subPixelPosition, glyphMargin(), m_transform); else - return m_current_fontengine->alphaMapForGlyph(g, m_transform); + return m_current_fontengine->alphaMapForGlyph(g, subPixelPosition, m_transform); return QImage(); } @@ -243,16 +367,16 @@ void QImageTextureGlyphCache::createTextureData(int width, int height) int QImageTextureGlyphCache::glyphMargin() const { -#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) +#if (defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)) || defined(Q_WS_X11) return 0; #else return m_type == QFontEngineGlyphCache::Raster_RGBMask ? 2 : 0; #endif } -void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g) +void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subPixelPosition) { - QImage mask = textureMapForGlyph(g); + QImage mask = textureMapForGlyph(g, subPixelPosition); #ifdef CACHE_DEBUG printf("fillTexture of %dx%d at %d,%d in the cache of %dx%d\n", c.w, c.h, c.x, c.y, m_image.width(), m_image.height()); @@ -337,7 +461,7 @@ void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g) QPoint base(c.x + glyphMargin(), c.y + glyphMargin() + c.baseLineY-1); if (m_image.rect().contains(base)) m_image.setPixel(base, 255); - m_image.save(QString::fromLatin1("cache-%1.png").arg(int(this))); + m_image.save(QString::fromLatin1("cache-%1.png").arg(qint64(this))); #endif } diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h index a129f41..bc5eebb 100644 --- a/src/gui/painting/qtextureglyphcache_p.h +++ b/src/gui/painting/qtextureglyphcache_p.h @@ -81,11 +81,24 @@ class Q_GUI_EXPORT QTextureGlyphCache : public QFontEngineGlyphCache public: QTextureGlyphCache(QFontEngineGlyphCache::Type type, const QTransform &matrix) : QFontEngineGlyphCache(matrix, type), m_current_fontengine(0), - m_w(0), m_h(0), m_cx(0), m_cy(0), m_currentRowHeight(0) + m_w(0), m_h(0), m_cx(0), m_cy(0), m_currentRowHeight(0), m_subPixelPositionCount(0) { } virtual ~QTextureGlyphCache() { } + struct GlyphAndSubPixelPosition + { + GlyphAndSubPixelPosition(glyph_t g, QFixed spp) : glyph(g), subPixelPosition(spp) {} + + bool operator==(const GlyphAndSubPixelPosition &other) const + { + return glyph == other.glyph && subPixelPosition == other.subPixelPosition; + } + + glyph_t glyph; + QFixed subPixelPosition; + }; + struct Coord { int x; int y; @@ -94,17 +107,23 @@ public: int baseLineX; int baseLineY; + + bool isNull() const + { + return w == 0 || h == 0; + } }; bool populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions); + void fillInPendingGlyphs(); virtual void createTextureData(int width, int height) = 0; virtual void resizeTextureData(int width, int height) = 0; virtual int glyphMargin() const { return 0; } virtual int glyphPadding() const { return 0; } - virtual void fillTexture(const Coord &coord, glyph_t glyph) = 0; + virtual void fillTexture(const Coord &coord, glyph_t glyph, QFixed subPixelPosition) = 0; inline void createCache(int width, int height) { m_w = width; @@ -112,24 +131,42 @@ public: createTextureData(width, height); } - inline bool isNull() const { return m_w <= 0 || m_h <= 0; } + inline void resizeCache(int width, int height) + { + resizeTextureData(width, height); + m_w = width; + m_h = height; + } - QHash<glyph_t, Coord> coords; + inline bool isNull() const { return m_h == 0; } - QImage textureMapForGlyph(glyph_t g) const; + QHash<GlyphAndSubPixelPosition, Coord> coords; virtual int maxTextureWidth() const { return QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH; } virtual int maxTextureHeight() const { return -1; } + QImage textureMapForGlyph(glyph_t g, QFixed subPixelPosition) const; + + QFixed subPixelPositionForX(QFixed x) const; + protected: + int calculateSubPixelPositionCount(glyph_t) const; + QFontEngine *m_current_fontengine; + QHash<GlyphAndSubPixelPosition, Coord> m_pendingGlyphs; int m_w; // image width int m_h; // image height int m_cx; // current x int m_cy; // current y int m_currentRowHeight; // Height of last row + int m_subPixelPositionCount; // Number of positions within a single pixel for this cache }; +inline uint qHash(const QTextureGlyphCache::GlyphAndSubPixelPosition &g) +{ + return (g.glyph << 8) | (g.subPixelPosition * 10).round().toInt(); +} + class Q_GUI_EXPORT QImageTextureGlyphCache : public QTextureGlyphCache { @@ -139,7 +176,7 @@ public: virtual int glyphMargin() const; virtual void createTextureData(int width, int height); virtual void resizeTextureData(int width, int height); - virtual void fillTexture(const Coord &c, glyph_t glyph); + virtual void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition); inline const QImage &image() const { return m_image; } diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index ea80342..4d7b339 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -1085,8 +1085,11 @@ QDebug operator<<(QDebug dbg, const QTransform &m) "TxNone", "TxTranslate", "TxScale", + 0, "TxRotate", + 0, 0, 0, "TxShear", + 0, 0, 0, 0, 0, 0, 0, "TxProject" }; diff --git a/src/gui/painting/qunifiedtoolbarsurface_mac.cpp b/src/gui/painting/qunifiedtoolbarsurface_mac.cpp new file mode 100644 index 0000000..1eb5e61 --- /dev/null +++ b/src/gui/painting/qunifiedtoolbarsurface_mac.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qunifiedtoolbarsurface_mac_p.h" +#include <private/qt_cocoa_helpers_mac_p.h> +#include <private/qbackingstore_p.h> +#include <private/qmainwindowlayout_p.h> + +#include <QDebug> + +#ifdef QT_MAC_USE_COCOA + +QT_BEGIN_NAMESPACE + +QUnifiedToolbarSurface::QUnifiedToolbarSurface(QWidget *widget) + : QRasterWindowSurface(widget, false), d_ptr(new QUnifiedToolbarSurfacePrivate) +{ + d_ptr->image = 0; + d_ptr->inSetGeometry = false; + + setGeometry(QRect(QPoint(0, 0), QSize(widget->width(), 100))); // FIXME: Fix height. +} + +QUnifiedToolbarSurface::~QUnifiedToolbarSurface() +{ + if (d_ptr->image) + delete d_ptr->image; +} + +QPaintDevice *QUnifiedToolbarSurface::paintDevice() +{ + return &d_ptr->image->image; +} + +void QUnifiedToolbarSurface::recursiveRedirect(QObject *object, QWidget *parent_toolbar, const QPoint &offset) +{ + if (object != 0) { + if (object->isWidgetType()) { + QWidget *widget = qobject_cast<QWidget *>(object); + + // We redirect the painting only if the widget is in the same window + // and is not a window in itself. + if (!(widget->windowType() & Qt::Window)) { + widget->d_func()->unifiedSurface = this; + widget->d_func()->isInUnifiedToolbar = true; + widget->d_func()->toolbar_offset = offset; + widget->d_func()->toolbar_ancestor = parent_toolbar; + + for (int i = 0; i < object->children().size(); ++i) { + recursiveRedirect(object->children().at(i), parent_toolbar, offset); + } + } + } + } +} + +void QUnifiedToolbarSurface::insertToolbar(QWidget *toolbar, const QPoint &offset) +{ + setGeometry(QRect(QPoint(0, 0), QSize(offset.x() + toolbar->width(), 100))); // FIXME + recursiveRedirect(toolbar, toolbar, offset); +} + +// We basically undo what we set in recursiveRedirect(). +void QUnifiedToolbarSurface::recursiveRemoval(QObject *object) +{ + if (object != 0) { + if (object->isWidgetType()) { + QWidget *widget = qobject_cast<QWidget *>(object); + + // If it's a pop-up or something similar, we don't redirect it. + if (widget->windowType() & Qt::Window) + return; + + widget->d_func()->unifiedSurface = 0; + widget->d_func()->isInUnifiedToolbar = false; + widget->d_func()->toolbar_offset = QPoint(); + widget->d_func()->toolbar_ancestor = 0; + } + + for (int i = 0; i < object->children().size(); ++i) { + recursiveRemoval(object->children().at(i)); + } + } +} + +void QUnifiedToolbarSurface::removeToolbar(QToolBar *toolbar) +{ + recursiveRemoval(toolbar); +} + +void QUnifiedToolbarSurface::setGeometry(const QRect &rect) +{ + QWindowSurface::setGeometry(rect); + Q_D(QUnifiedToolbarSurface); + d->inSetGeometry = true; + if (d->image == 0 || d->image->width() < rect.width() || d->image->height() < rect.height()) + prepareBuffer(QImage::Format_ARGB32_Premultiplied, window()); + d->inSetGeometry = false; + + // FIXME: set unified toolbar height. +} + +void QUnifiedToolbarSurface::beginPaint(const QRegion &rgn) +{ + QPainter p(&d_ptr->image->image); + p.setCompositionMode(QPainter::CompositionMode_Source); + const QVector<QRect> rects = rgn.rects(); + const QColor blank = Qt::transparent; + for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it) { + p.fillRect(*it, blank); + } +} + +void QUnifiedToolbarSurface::updateToolbarOffset(QWidget *widget) +{ + QMainWindowLayout *mlayout = qobject_cast<QMainWindowLayout*> (widget->window()->layout()); + if (mlayout) + mlayout->updateUnifiedToolbarOffset(); +} + +void QUnifiedToolbarSurface::flush(QWidget *widget, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(region); + Q_UNUSED(offset); + + this->flush(widget); +} + +void QUnifiedToolbarSurface::flush(QWidget *widget) +{ + Q_D(QUnifiedToolbarSurface); + + if (!d->image) + return; + + if (widget->d_func()->flushRequested) + qt_mac_setNeedsDisplay(widget); +} + +void QUnifiedToolbarSurface::prepareBuffer(QImage::Format format, QWidget *widget) +{ + Q_D(QUnifiedToolbarSurface); + + int width = geometry().width(); + int height = 100; // FIXME + if (d->image) { + width = qMax(d->image->width(), width); + height = qMax(d->image->height(), height); + } + + if (width == 0 || height == 0) { + delete d->image; + d->image = 0; + return; + } + + QNativeImage *oldImage = d->image; + + d->image = new QNativeImage(width, height, format, false, widget); + + if (oldImage && d->inSetGeometry && hasStaticContents()) { + // Make sure we use the const version of bits() (no detach). + const uchar *src = const_cast<const QImage &>(oldImage->image).bits(); + uchar *dst = d->image->image.bits(); + + const int srcBytesPerLine = oldImage->image.bytesPerLine(); + const int dstBytesPerLine = d->image->image.bytesPerLine(); + const int bytesPerPixel = oldImage->image.depth() >> 3; + + QRegion staticRegion(staticContents()); + // Make sure we're inside the boundaries of the old image. + staticRegion &= QRect(0, 0, oldImage->image.width(), oldImage->image.height()); + const QVector<QRect> &rects = staticRegion.rects(); + const QRect *srcRect = rects.constData(); + + // Copy the static content of the old image into the new one. + int numRectsLeft = rects.size(); + do { + const int bytesOffset = srcRect->x() * bytesPerPixel; + const int dy = srcRect->y(); + + // Adjust src and dst to point to the right offset. + const uchar *s = src + dy * srcBytesPerLine + bytesOffset; + uchar *d = dst + dy * dstBytesPerLine + bytesOffset; + const int numBytes = srcRect->width() * bytesPerPixel; + + int numScanLinesLeft = srcRect->height(); + do { + ::memcpy(d, s, numBytes); + d += dstBytesPerLine; + s += srcBytesPerLine; + } while (--numScanLinesLeft); + + ++srcRect; + } while (--numRectsLeft); + } + + delete oldImage; +} + +CGContextRef QUnifiedToolbarSurface::imageContext() +{ + Q_D(QUnifiedToolbarSurface); + return d->image->cg; +} + +void QUnifiedToolbarSurface::renderToolbar(QWidget *widget, bool forceFlush) +{ + QWidget *toolbar = widget->d_func()->toolbar_ancestor; + + updateToolbarOffset(toolbar); + QRect beginPaintRect(toolbar->d_func()->toolbar_offset.x(), toolbar->d_func()->toolbar_offset.y(), toolbar->geometry().width(), toolbar->geometry().height()); + QRegion beginPaintRegion(beginPaintRect); + + beginPaint(beginPaintRegion); + toolbar->render(paintDevice(), toolbar->d_func()->toolbar_offset, QRegion(toolbar->geometry()), QWidget::DrawChildren); + toolbar->d_func()->flushRequested = true; + + if (forceFlush) + flush(toolbar); +} + +QT_END_NAMESPACE + +#endif // QT_MAC_USE_COCOA diff --git a/src/gui/painting/qunifiedtoolbarsurface_mac_p.h b/src/gui/painting/qunifiedtoolbarsurface_mac_p.h new file mode 100644 index 0000000..ec09c94 --- /dev/null +++ b/src/gui/painting/qunifiedtoolbarsurface_mac_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QUNIFIEDTOOLBARSURFACE_MAC_P_H +#define QUNIFIEDTOOLBARSURFACE_MAC_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qwindowsurface_raster_p.h> +#include <QWidget> +#include <QToolBar> +#include <private/qwidget_p.h> +#include <private/qnativeimage_p.h> + +#ifdef QT_MAC_USE_COCOA + +QT_BEGIN_NAMESPACE + +class QNativeImage; + +// +// This is the implementation of the unified toolbar on Mac OS X +// with the graphics system raster. +// +// General idea: +// ------------- +// We redirect the painting of widgets inside the unified toolbar +// to a special window surface, the QUnifiedToolbarSurface. +// We need a separate window surface because the unified toolbar +// is out of the content view. +// The input system is the same as for the unified toolbar with the +// native (CoreGraphics) engine. +// +// Execution flow: +// --------------- +// The unified toolbar is triggered by QMainWindow::setUnifiedTitleAndToolBarOnMac(). +// It calls QMainWindowLayout::insertIntoMacToolbar() which will +// set all the appropriate variables (offsets, redirection, ...). +// When Qt tells a widget to repaint, QWidgetPrivate::drawWidget() +// checks if the widget is inside the unified toolbar and exits without +// painting is that is the case. +// We trigger the rendering of the unified toolbar in QWidget::repaint() +// and QWidget::update(). +// We keep track of flush requests via "flushRequested" variable. That +// allow flush() to be a no-op if no repaint occurred for a widget. +// We rely on the needsDisplay: and drawRect: mecanism for drawing our +// content into the graphics context. +// +// Notes: +// ------ +// The painting of items inside the unified toolbar is expensive. +// Too many repaints will drastically slow down the whole application. +// + +class QUnifiedToolbarSurfacePrivate +{ +public: + QNativeImage *image; + uint inSetGeometry : 1; +}; + +class Q_GUI_EXPORT QUnifiedToolbarSurface : public QRasterWindowSurface +{ +public: + QUnifiedToolbarSurface(QWidget *widget); + ~QUnifiedToolbarSurface(); + + void flush(QWidget *widget); + void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); + void setGeometry(const QRect &rect); + void beginPaint(const QRegion &rgn); + void insertToolbar(QWidget *toolbar, const QPoint &offset); + void removeToolbar(QToolBar *toolbar); + void updateToolbarOffset(QWidget *widget); + void renderToolbar(QWidget *widget, bool forceFlush = false); + void recursiveRedirect(QObject *widget, QWidget *parent_toolbar, const QPoint &offset); + + QPaintDevice *paintDevice(); + CGContextRef imageContext(); + +private: + void prepareBuffer(QImage::Format format, QWidget *widget); + void recursiveRemoval(QObject *object); + + Q_DECLARE_PRIVATE(QUnifiedToolbarSurface) + QScopedPointer<QUnifiedToolbarSurfacePrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QT_MAC_USE_COCOA + +#endif // QUNIFIEDTOOLBARSURFACE_MAC_P_H diff --git a/src/gui/painting/qwindowsurface.cpp b/src/gui/painting/qwindowsurface.cpp index 6d1ba49..fa3c683 100644 --- a/src/gui/painting/qwindowsurface.cpp +++ b/src/gui/painting/qwindowsurface.cpp @@ -52,17 +52,17 @@ class QWindowSurfacePrivate public: QWindowSurfacePrivate(QWidget *w) : window(w) - , staticContentsSupport(0) - , partialUpdateSupport(1) { } QWidget *window; +#if !defined(Q_WS_QPA) QRect geometry; +#else + QSize size; +#endif //Q_WS_QPA QRegion staticContents; QList<QImage*> bufferImages; - uint staticContentsSupport : 1; - uint partialUpdateSupport : 1; }; /*! @@ -70,7 +70,7 @@ public: \since 4.3 \internal \preliminary - \ingroup qws + \ingroup qws qpa \brief The QWindowSurface class provides the drawing area for top-level windows. @@ -114,11 +114,11 @@ public: /*! Constructs an empty surface for the given top-level \a window. */ -QWindowSurface::QWindowSurface(QWidget *window) +QWindowSurface::QWindowSurface(QWidget *window, bool setDefaultSurface) : d_ptr(new QWindowSurfacePrivate(window)) { if (!QApplicationPrivate::runtime_graphics_system) { - if(window) + if(setDefaultSurface && window) window->setWindowSurface(this); } } @@ -153,6 +153,7 @@ void QWindowSurface::endPaint(const QRegion &) d_ptr->bufferImages.clear(); } +#if !defined(Q_WS_QPA) /*! Sets the currently allocated area to be the given \a rect. @@ -173,6 +174,26 @@ QRect QWindowSurface::geometry() const { return d_ptr->geometry; } +#else + +/*! + Sets the size of the windowsurface to be \a size. + + \sa size() +*/ +void QWindowSurface::resize(const QSize &size) +{ + d_ptr->size = size; +} + +/*! + Returns the current size of the windowsurface. +*/ +QSize QWindowSurface::size() const +{ + return d_ptr->size; +} +#endif //Q_WS_QPA /*! Scrolls the given \a area \a dx pixels to the right and \a dy @@ -286,20 +307,6 @@ QPoint QWindowSurface::offset(const QWidget *widget) const window surface. */ -bool QWindowSurface::hasStaticContentsSupport() const -{ - return d_ptr->staticContentsSupport; -} - -void QWindowSurface::setStaticContentsSupport(bool enable) -{ - if (enable && !d_ptr->partialUpdateSupport) { - qWarning("QWindowSurface::setStaticContentsSupport: static contents support requires partial update support"); - return; - } - d_ptr->staticContentsSupport = enable; -} - void QWindowSurface::setStaticContents(const QRegion ®ion) { d_ptr->staticContents = region; @@ -312,24 +319,21 @@ QRegion QWindowSurface::staticContents() const bool QWindowSurface::hasStaticContents() const { - return d_ptr->staticContentsSupport && !d_ptr->staticContents.isEmpty(); + return hasFeature(QWindowSurface::StaticContents) && !d_ptr->staticContents.isEmpty(); } -bool QWindowSurface::hasPartialUpdateSupport() const +QWindowSurface::WindowSurfaceFeatures QWindowSurface::features() const { - return d_ptr->partialUpdateSupport; + return PartialUpdates | PreservedContents; } -void QWindowSurface::setPartialUpdateSupport(bool enable) -{ - if (!enable && d_ptr->staticContentsSupport) { - qWarning("QWindowSurface::setPartialUpdateSupport: static contents support requires partial update support"); - return; - } - d_ptr->partialUpdateSupport = enable; -} +#ifdef Q_WS_QPA +#define Q_EXPORT_SCROLLRECT Q_GUI_EXPORT +#else +#define Q_EXPORT_SCROLLRECT +#endif -void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset) +void Q_EXPORT_SCROLLRECT qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset) { // make sure we don't detach uchar *mem = const_cast<uchar*>(const_cast<const QImage &>(img).bits()); diff --git a/src/gui/painting/qwindowsurface_mac.cpp b/src/gui/painting/qwindowsurface_mac.cpp index 1bf15f0..ed794ae 100644 --- a/src/gui/painting/qwindowsurface_mac.cpp +++ b/src/gui/painting/qwindowsurface_mac.cpp @@ -82,6 +82,7 @@ void QMacWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint extern CGContextRef qt_mac_graphicsContextFor(QWidget *); CGContextRef context = qt_mac_graphicsContextFor(widget); #endif + CGContextRetain(context); CGContextSaveGState(context); // Flip context. @@ -109,6 +110,7 @@ void QMacWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint #else CGContextFlush(context); #endif + CGContextRelease(context); } void QMacWindowSurface::setGeometry(const QRect &rect) diff --git a/src/gui/painting/qwindowsurface_p.h b/src/gui/painting/qwindowsurface_p.h index 6c5d500..1e6529c 100644 --- a/src/gui/painting/qwindowsurface_p.h +++ b/src/gui/painting/qwindowsurface_p.h @@ -63,11 +63,20 @@ class QRect; class QPoint; class QImage; class QWindowSurfacePrivate; +class QPlatformWindow; class Q_GUI_EXPORT QWindowSurface { public: - QWindowSurface(QWidget *window); + enum WindowSurfaceFeature { + PartialUpdates = 0x00000001, // Supports doing partial updates. + PreservedContents = 0x00000002, // Supports doing flush without first doing a repaint. + StaticContents = 0x00000004, // Supports having static content regions when being resized. + AllFeatures = 0xffffffff // For convenience + }; + Q_DECLARE_FLAGS(WindowSurfaceFeatures, WindowSurfaceFeature) + + QWindowSurface(QWidget *window, bool setDefaultSurface = true); virtual ~QWindowSurface(); QWidget *window() const; @@ -79,8 +88,14 @@ public: // can be larger than just the offset from the top-level widget as there may also be window // decorations which are painted into the window surface. virtual void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset) = 0; +#if !defined(Q_WS_QPA) virtual void setGeometry(const QRect &rect); QRect geometry() const; +#else + virtual void resize(const QSize &size); + QSize size() const; + inline QRect geometry() const { return QRect(QPoint(), size()); } //### cleanup before Qt 5 +#endif virtual bool scroll(const QRegion &area, int dx, int dy); @@ -93,26 +108,31 @@ public: virtual QPoint offset(const QWidget *widget) const; inline QRect rect(const QWidget *widget) const; - bool hasStaticContentsSupport() const; - bool hasPartialUpdateSupport() const; + bool hasFeature(WindowSurfaceFeature feature) const; + virtual WindowSurfaceFeatures features() const; void setStaticContents(const QRegion ®ion); QRegion staticContents() const; protected: bool hasStaticContents() const; - void setStaticContentsSupport(bool enable); - void setPartialUpdateSupport(bool enable); private: QWindowSurfacePrivate *d_ptr; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QWindowSurface::WindowSurfaceFeatures) + inline QRect QWindowSurface::rect(const QWidget *widget) const { return widget->rect().translated(offset(widget)); } +inline bool QWindowSurface::hasFeature(WindowSurfaceFeature feature) const +{ + return (features() & feature) != 0; +} + QT_END_NAMESPACE #endif // QWINDOWSURFACE_P_H diff --git a/src/gui/painting/qwindowsurface_qws.cpp b/src/gui/painting/qwindowsurface_qws.cpp index c52f864..96e2652 100644 --- a/src/gui/painting/qwindowsurface_qws.cpp +++ b/src/gui/painting/qwindowsurface_qws.cpp @@ -806,6 +806,10 @@ QWSMemorySurface::QWSMemorySurface(QWidget *w) QWSMemorySurface::~QWSMemorySurface() { +#ifndef QT_NO_QWS_MULTIPROCESS + if (memlock != QWSDisplay::Data::getClientLock()) + delete memlock; +#endif } @@ -852,9 +856,9 @@ void QWSMemorySurface::setLock(int lockId) { if (memlock && memlock->id() == lockId) return; - delete memlock; + if (memlock != QWSDisplay::Data::getClientLock()) + delete memlock; memlock = (lockId == -1 ? 0 : new QWSLock(lockId)); - return; } #endif // QT_NO_QWS_MULTIPROCESS @@ -946,37 +950,39 @@ void QWSLocalMemSurface::setGeometry(const QRect &rect) } uchar *deleteLater = 0; - // In case of a Hide event we need to delete the memory after sending the - // event to the server in order to let the server animate the event. - if (size.isEmpty()) { - deleteLater = mem; - mem = 0; - } if (img.size() != size) { - delete[] mem; if (size.isEmpty()) { + if (memsize) { + // In case of a Hide event we need to delete the memory after sending the + // event to the server in order to let the server animate the event. + deleteLater = mem; + memsize = 0; + } mem = 0; img = QImage(); } else { const QImage::Format format = preferredImageFormat(win); const int bpl = nextMulOf4(bytesPerPixel(format) * size.width()); - const int memsize = bpl * size.height(); - mem = new uchar[memsize]; + const int imagesize = bpl * size.height(); + if (memsize < imagesize) { + delete[] mem; + memsize = imagesize; + mem = new uchar[memsize]; + } img = QImage(mem, size.width(), size.height(), bpl, format); setImageMetrics(img, win); } } QWSWindowSurface::setGeometry(rect); + delete[] deleteLater; } QByteArray QWSLocalMemSurface::permanentState() const { - QByteArray array; - array.resize(sizeof(uchar*) + 3 * sizeof(int) + - sizeof(SurfaceFlags)); + QByteArray array(sizeof(uchar*) + 3 * sizeof(int) + sizeof(SurfaceFlags), Qt::Uninitialized); char *ptr = array.data(); @@ -997,6 +1003,11 @@ QByteArray QWSLocalMemSurface::permanentState() const void QWSLocalMemSurface::setPermanentState(const QByteArray &data) { + if (memsize) { + delete[] mem; + memsize = 0; + } + int width; int height; QImage::Format format; @@ -1023,6 +1034,10 @@ void QWSLocalMemSurface::setPermanentState(const QByteArray &data) void QWSLocalMemSurface::releaseSurface() { + if (memsize) { + delete[] mem; + memsize = 0; + } mem = 0; img = QImage(); } @@ -1050,10 +1065,12 @@ bool QWSSharedMemSurface::setMemory(int memId) return true; mem.detach(); - if (!mem.attach(memId)) { + + if (memId != -1 && !mem.attach(memId)) { +#ifndef QT_NO_DEBUG perror("QWSSharedMemSurface: attaching to shared memory"); - qCritical("QWSSharedMemSurface: Error attaching to" - " shared memory 0x%x", memId); + qCritical("QWSSharedMemSurface: Error attaching to shared memory 0x%x", memId); +#endif return false; } @@ -1064,17 +1081,15 @@ bool QWSSharedMemSurface::setMemory(int memId) void QWSSharedMemSurface::setDirectRegion(const QRegion &r, int id) { QWSMemorySurface::setDirectRegion(r, id); - if(mem.address()) + if (mem.address()) *(uint *)mem.address() = id; } const QRegion QWSSharedMemSurface::directRegion() const { - QWSSharedMemory *cmem = const_cast<QWSSharedMemory *>(&mem); - if (cmem->address() && ((int*)cmem->address())[0] == directRegionId()) + if (mem.address() && *(uint *)mem.address() == uint(directRegionId())) return QWSMemorySurface::directRegion(); - else - return QRegion(); + return QRegion(); } #endif @@ -1117,8 +1132,6 @@ void QWSSharedMemSurface::setGeometry(const QRect &rect) mem.detach(); img = QImage(); } else { - mem.detach(); - QWidget *win = window(); const QImage::Format format = preferredImageFormat(win); const int bpl = nextMulOf4(bytesPerPixel(format) * size.width()); @@ -1127,9 +1140,12 @@ void QWSSharedMemSurface::setGeometry(const QRect &rect) #else const int imagesize = bpl * size.height(); #endif - if (!mem.create(imagesize)) { - perror("QWSSharedMemSurface::setGeometry allocating shared memory"); - qFatal("Error creating shared memory of size %d", imagesize); + if (mem.size() < imagesize) { + mem.detach(); + if (!mem.create(imagesize)) { + perror("QWSSharedMemSurface::setGeometry allocating shared memory"); + qFatal("Error creating shared memory of size %d", imagesize); + } } #ifdef QT_QWS_CLIENTBLIT *((uint *)mem.address()) = 0; @@ -1147,8 +1163,7 @@ void QWSSharedMemSurface::setGeometry(const QRect &rect) QByteArray QWSSharedMemSurface::permanentState() const { - QByteArray array; - array.resize(6 * sizeof(int)); + QByteArray array(6 * sizeof(int), Qt::Uninitialized); int *ptr = reinterpret_cast<int*>(array.data()); @@ -1222,8 +1237,8 @@ bool QWSOnScreenSurface::isValid() const QByteArray QWSOnScreenSurface::permanentState() const { - QByteArray array; - array.resize(sizeof(int)); + QByteArray array(sizeof(int), Qt::Uninitialized); + int *ptr = reinterpret_cast<int*>(array.data()); ptr[0] = QApplication::desktop()->screenNumber(window()); return array; @@ -1263,8 +1278,7 @@ QWSYellowSurface::~QWSYellowSurface() QByteArray QWSYellowSurface::permanentState() const { - QByteArray array; - array.resize(2 * sizeof(int)); + QByteArray array(2 * sizeof(int), Qt::Uninitialized); int *ptr = reinterpret_cast<int*>(array.data()); ptr[0] = surfaceSize.width(); diff --git a/src/gui/painting/qwindowsurface_raster.cpp b/src/gui/painting/qwindowsurface_raster.cpp index f271e56..15ff044 100644 --- a/src/gui/painting/qwindowsurface_raster.cpp +++ b/src/gui/painting/qwindowsurface_raster.cpp @@ -64,6 +64,9 @@ #ifdef Q_WS_MAC #include <private/qt_cocoa_helpers_mac_p.h> +#include <QMainWindow> +#include <private/qmainwindowlayout_p.h> +#include <QToolBar> #endif QT_BEGIN_NAMESPACE @@ -75,6 +78,9 @@ public: #ifdef Q_WS_X11 GC gc; +#ifndef QT_NO_MITSHM + uint needsSync : 1; +#endif #ifndef QT_NO_XRENDER uint translucentBackground : 1; #endif @@ -82,8 +88,8 @@ public: uint inSetGeometry : 1; }; -QRasterWindowSurface::QRasterWindowSurface(QWidget *window) - : QWindowSurface(window), d_ptr(new QRasterWindowSurfacePrivate) +QRasterWindowSurface::QRasterWindowSurface(QWidget *window, bool setDefaultSurface) + : QWindowSurface(window, setDefaultSurface), d_ptr(new QRasterWindowSurfacePrivate) { #ifdef Q_WS_X11 d_ptr->gc = XCreateGC(X11->display, window->handle(), 0, 0); @@ -91,10 +97,17 @@ QRasterWindowSurface::QRasterWindowSurface(QWidget *window) d_ptr->translucentBackground = X11->use_xrender && window->x11Info().depth() == 32; #endif +#ifndef QT_NO_MITHSM + d_ptr->needsSync = false; +#endif #endif d_ptr->image = 0; d_ptr->inSetGeometry = false; - setStaticContentsSupport(true); + +#ifdef QT_MAC_USE_COCOA + needsFlush = false; + regionToFlush = QRegion(); +#endif // QT_MAC_USE_COCOA } @@ -113,8 +126,23 @@ QPaintDevice *QRasterWindowSurface::paintDevice() return &d_ptr->image->image; } +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) +void QRasterWindowSurface::syncX() +{ + // delay writing to the backbuffer until we know for sure X is done reading from it + if (d_ptr->needsSync) { + XSync(X11->display, false); + d_ptr->needsSync = false; + } +} +#endif + void QRasterWindowSurface::beginPaint(const QRegion &rgn) { +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) + syncX(); +#endif + #if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_WS_WINCE)) if (!qt_widget_private(window())->isOpaque && window()->testAttribute(Qt::WA_TranslucentBackground)) { #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) @@ -201,7 +229,6 @@ void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoi QRegion wrgn(rgn); if (!wOffset.isNull()) wrgn.translate(-wOffset); - QRect wbr = wrgn.boundingRect(); if (wrgn.rectCount() != 1) { int num; @@ -209,23 +236,25 @@ void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoi XSetClipRectangles(X11->display, d_ptr->gc, 0, 0, rects, num, YXBanded); } - QRect br = rgn.boundingRect().translated(offset); + QPoint widgetOffset = offset + wOffset; + QRect clipRect = widget->rect().translated(widgetOffset).intersected(d_ptr->image->image.rect()); + + QRect br = rgn.boundingRect().translated(offset).intersected(clipRect); + QPoint wpos = br.topLeft() - widgetOffset; + #ifndef QT_NO_MITSHM if (d_ptr->image->xshmpm) { XCopyArea(X11->display, d_ptr->image->xshmpm, widget->handle(), d_ptr->gc, - br.x(), br.y(), br.width(), br.height(), wbr.x(), wbr.y()); - XSync(X11->display, False); + br.x(), br.y(), br.width(), br.height(), wpos.x(), wpos.y()); + d_ptr->needsSync = true; } else if (d_ptr->image->xshmimg) { - const QImage &src = d->image->image; - br = br.intersected(src.rect()); XShmPutImage(X11->display, widget->handle(), d_ptr->gc, d_ptr->image->xshmimg, - br.x(), br.y(), wbr.x(), wbr.y(), br.width(), br.height(), False); - XSync(X11->display, False); + br.x(), br.y(), wpos.x(), wpos.y(), br.width(), br.height(), False); + d_ptr->needsSync = true; } else #endif { const QImage &src = d->image->image; - br = br.intersected(src.rect()); if (src.format() != QImage::Format_RGB32 || widget->x11Info().depth() < 24) { Q_ASSERT(src.depth() >= 16); const QImage sub_src(src.scanLine(br.y()) + br.x() * (uint(src.depth()) / 8), @@ -234,11 +263,11 @@ void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoi data->xinfo = widget->x11Info(); data->fromImage(sub_src, Qt::NoOpaqueDetection); QPixmap pm = QPixmap(data); - XCopyArea(X11->display, pm.handle(), widget->handle(), d_ptr->gc, 0 , 0 , br.width(), br.height(), wbr.x(), wbr.y()); + XCopyArea(X11->display, pm.handle(), widget->handle(), d_ptr->gc, 0 , 0 , br.width(), br.height(), wpos.x(), wpos.y()); } else { // qpaintengine_x11.cpp extern void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image, Drawable hd, GC gc, Display *dpy, Visual *visual, int depth); - qt_x11_drawImage(br, wbr.topLeft(), src, widget->handle(), d_ptr->gc, X11->display, (Visual *)widget->x11Info().visual(), widget->x11Info().depth()); + qt_x11_drawImage(br, wpos, src, widget->handle(), d_ptr->gc, X11->display, (Visual *)widget->x11Info().visual(), widget->x11Info().depth()); } } @@ -248,20 +277,27 @@ void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoi #ifdef Q_WS_MAC -// qDebug() << "Flushing" << widget << rgn << offset; + Q_UNUSED(offset); + + // This is mainly done for native components like native "open file" dialog. + if (widget->testAttribute(Qt::WA_DontShowOnScreen)) { + return; + } -// d->image->image.save("flush.png"); +#ifdef QT_MAC_USE_COCOA - Q_UNUSED(offset); + this->needsFlush = true; + this->regionToFlush += rgn; + + // The actual flushing will be processed in [view drawRect:rect] + qt_mac_setNeedsDisplay(widget); + +#else // Get a context for the widget. -#ifndef QT_MAC_USE_COCOA CGContextRef context; CGrafPtr port = GetWindowPort(qt_mac_window_for(widget)); QDBeginCGContext(port, &context); -#else - extern CGContextRef qt_mac_graphicsContextFor(QWidget *); - CGContextRef context = qt_mac_graphicsContextFor(widget); -#endif + CGContextRetain(context); CGContextSaveGState(context); // Flip context. @@ -276,26 +312,23 @@ void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoi } CGContextClip(context); - QRect r = rgn.boundingRect(); + QRect r = rgn.boundingRect().intersected(d->image->image.rect()); const CGRect area = CGRectMake(r.x(), r.y(), r.width(), r.height()); CGImageRef image = CGBitmapContextCreateImage(d->image->cg); CGImageRef subImage = CGImageCreateWithImageInRect(image, area); qt_mac_drawCGImage(context, &area, subImage); + CGImageRelease(subImage); CGImageRelease(image); -// CGSize size = { d->image->image.width(), d->image->image.height() }; -// CGLayerRef layer = CGLayerCreateWithContext(d->image->cg, size, 0); -// CGPoint pt = { 0, 0 }; -// CGContextDrawLayerAtPoint(context, pt, layer); -// CGLayerRelease(layer); + QDEndCGContext(port, &context); // Restore context. CGContextRestoreGState(context); -#ifndef QT_MAC_USE_COCOA - QDEndCGContext(port, &context); -#endif + CGContextRelease(context); +#endif // QT_MAC_USE_COCOA + #endif // Q_WS_MAC #ifdef Q_OS_SYMBIAN @@ -323,6 +356,25 @@ void QRasterWindowSurface::setGeometry(const QRect &rect) prepareBuffer(QNativeImage::systemFormat(), window()); } d->inSetGeometry = false; + +#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) + QMainWindow* mWindow = qobject_cast<QMainWindow*>(window()); + if (mWindow) { + QMainWindowLayout *mLayout = qobject_cast<QMainWindowLayout*>(mWindow->layout()); + QList<QToolBar *> toolbarList = mLayout->qtoolbarsInUnifiedToolbarList; + + for (int i = 0; i < toolbarList.size(); ++i) { + QToolBar* toolbar = toolbarList.at(i); + if (mLayout->toolBarArea(toolbar) == Qt::TopToolBarArea) { + QWidget* tbWidget = (QWidget*) toolbar; + if (tbWidget->d_func()->unifiedSurface) { + tbWidget->d_func()->unifiedSurface->setGeometry(rect); + } + } + } + } +#endif // Q_WS_MAC && QT_MAC_USE_COCOA + } // from qwindowsurface.cpp @@ -347,6 +399,10 @@ bool QRasterWindowSurface::scroll(const QRegion &area, int dx, int dy) if (!d->image || d->image->image.isNull()) return false; +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) + syncX(); +#endif + const QVector<QRect> rects = area.rects(); for (int i = 0; i < rects.size(); ++i) qt_scrollRectInImage(d->image->image, rects.at(i), QPoint(dx, dy)); @@ -355,6 +411,10 @@ bool QRasterWindowSurface::scroll(const QRegion &area, int dx, int dy) #endif } +QWindowSurface::WindowSurfaceFeatures QRasterWindowSurface::features() const +{ + return QWindowSurface::AllFeatures; +} void QRasterWindowSurface::prepareBuffer(QImage::Format format, QWidget *widget) { @@ -417,4 +477,12 @@ void QRasterWindowSurface::prepareBuffer(QImage::Format format, QWidget *widget) delete oldImage; } +#ifdef QT_MAC_USE_COCOA +CGContextRef QRasterWindowSurface::imageContext() +{ + Q_D(QRasterWindowSurface); + return d->image->cg; +} +#endif // QT_MAC_USE_COCOA + QT_END_NAMESPACE diff --git a/src/gui/painting/qwindowsurface_raster_p.h b/src/gui/painting/qwindowsurface_raster_p.h index 1d5a9bf..7bac561 100644 --- a/src/gui/painting/qwindowsurface_raster_p.h +++ b/src/gui/painting/qwindowsurface_raster_p.h @@ -56,6 +56,10 @@ #include <qglobal.h> #include "private/qwindowsurface_p.h" +#ifdef QT_MAC_USE_COCOA +# include <private/qt_cocoa_helpers_mac_p.h> +#endif // QT_MAC_USE_COCOA + QT_BEGIN_NAMESPACE #ifdef Q_WS_WIN @@ -97,7 +101,7 @@ class QNativeImage; class Q_GUI_EXPORT QRasterWindowSurface : public QWindowSurface { public: - QRasterWindowSurface(QWidget *widget); + QRasterWindowSurface(QWidget *widget, bool setDefaultSurface = true); ~QRasterWindowSurface(); QPaintDevice *paintDevice(); @@ -105,8 +109,19 @@ public: void beginPaint(const QRegion &rgn); void setGeometry(const QRect &rect); bool scroll(const QRegion &area, int dx, int dy); + WindowSurfaceFeatures features() const; + +#ifdef QT_MAC_USE_COCOA + CGContextRef imageContext(); + + bool needsFlush; + QRegion regionToFlush; +#endif // QT_MAC_USE_COCOA private: +#if defined(Q_WS_X11) && !defined(QT_NO_MITSHM) + void syncX(); +#endif void prepareBuffer(QImage::Format format, QWidget *widget); Q_DECLARE_PRIVATE(QRasterWindowSurface) QScopedPointer<QRasterWindowSurfacePrivate> d_ptr; diff --git a/src/gui/painting/qwindowsurface_s60.cpp b/src/gui/painting/qwindowsurface_s60.cpp index 116962f..d1ac926 100644 --- a/src/gui/painting/qwindowsurface_s60.cpp +++ b/src/gui/painting/qwindowsurface_s60.cpp @@ -88,7 +88,8 @@ QS60WindowSurface::QS60WindowSurface(QWidget* widget) const bool opaque = widgetPrivate->isOpaque && !blitWriteAlpha(widgetPrivate); TDisplayMode mode = displayMode(opaque); // We create empty CFbsBitmap here -> it will be resized in setGeometry - CFbsBitmap *bitmap = q_check_ptr(new CFbsBitmap); // CBase derived object needs check on new + CFbsBitmap *bitmap = new CFbsBitmap; // CBase derived object needs check on new + Q_CHECK_PTR(bitmap); qt_symbian_throwIfError( bitmap->Create( TSize(0, 0), mode ) ); QSymbianRasterPixmapData *data = new QSymbianRasterPixmapData(QPixmapData::PixmapType); @@ -96,8 +97,6 @@ QS60WindowSurface::QS60WindowSurface(QWidget* widget) data->fromSymbianBitmap(bitmap, true); d_ptr->device = QPixmap(data); } - - setStaticContentsSupport(true); } QS60WindowSurface::~QS60WindowSurface() @@ -240,6 +239,11 @@ void QS60WindowSurface::setGeometry(const QRect& rect) QWindowSurface::setGeometry(rect); } +QWindowSurface::WindowSurfaceFeatures QS60WindowSurface::features() const +{ + return QWindowSurface::AllFeatures; +} + CFbsBitmap* QS60WindowSurface::symbianBitmap() const { QSymbianRasterPixmapData *data = static_cast<QSymbianRasterPixmapData*>(d_ptr->device.data_ptr().data()); diff --git a/src/gui/painting/qwindowsurface_s60_p.h b/src/gui/painting/qwindowsurface_s60_p.h index 5bb6652..a086ca4 100644 --- a/src/gui/painting/qwindowsurface_s60_p.h +++ b/src/gui/painting/qwindowsurface_s60_p.h @@ -79,6 +79,8 @@ public: void setGeometry(const QRect &rect); + WindowSurfaceFeatures features() const; + CFbsBitmap *symbianBitmap() const; private: diff --git a/src/gui/painting/qwindowsurface_x11.cpp b/src/gui/painting/qwindowsurface_x11.cpp index cf1d386..1a62f47 100644 --- a/src/gui/painting/qwindowsurface_x11.cpp +++ b/src/gui/painting/qwindowsurface_x11.cpp @@ -70,9 +70,6 @@ QX11WindowSurface::QX11WindowSurface(QWidget *widget) #ifndef QT_NO_XRENDER d_ptr->translucentBackground = X11->use_xrender && widget->x11Info().depth() == 32; - setStaticContentsSupport(!d_ptr->translucentBackground); -#else - setStaticContentsSupport(true); #endif } @@ -149,6 +146,8 @@ void QX11WindowSurface::setGeometry(const QRect &rect) return; #ifndef QT_NO_XRENDER if (d_ptr->translucentBackground) { + QPixmap::x11SetDefaultScreen(d_ptr->widget->x11Info().screen()); + QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType); data->xinfo = d_ptr->widget->x11Info(); data->resize(size.width(), size.height()); @@ -251,4 +250,16 @@ QPixmap QX11WindowSurface::grabWidget(const QWidget *widget, return px; } +QWindowSurface::WindowSurfaceFeatures QX11WindowSurface::features() const +{ + WindowSurfaceFeatures features = QWindowSurface::PartialUpdates | QWindowSurface::PreservedContents; +#ifndef QT_NO_XRENDER + if (!d_ptr->translucentBackground) + features |= QWindowSurface::StaticContents; +#else + features |= QWindowSurface::StaticContents; +#endif + return features; +} + QT_END_NAMESPACE diff --git a/src/gui/painting/qwindowsurface_x11_p.h b/src/gui/painting/qwindowsurface_x11_p.h index 101df68..23c00a1 100644 --- a/src/gui/painting/qwindowsurface_x11_p.h +++ b/src/gui/painting/qwindowsurface_x11_p.h @@ -80,6 +80,8 @@ public: bool scroll(const QRegion &area, int dx, int dy); QPixmap grabWidget(const QWidget *widget, const QRect& rectangle = QRect()) const; + WindowSurfaceFeatures features() const; + private: QX11WindowSurfacePrivate *d_ptr; GC gc; diff --git a/src/gui/s60framework/qs60keycapture.cpp b/src/gui/s60framework/qs60keycapture.cpp new file mode 100644 index 0000000..1beefd4 --- /dev/null +++ b/src/gui/s60framework/qs60keycapture.cpp @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Symbian application wrapper of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <remconinterfaceselector.h> +#include <remconcoreapitarget.h> +#include <coemain.h> +#include <private/qkeymapper_p.h> +#include "qs60keycapture_p.h" + +QT_BEGIN_NAMESPACE + +/* + * Helper class for sending key handling responses to RemCon. +*/ +class CResponseHandler : public CActive +{ +public: + static CResponseHandler *NewL(CRemConCoreApiTarget &remConCoreApiTarget); + virtual ~CResponseHandler(); + void CompleteAnyKey(TRemConCoreApiOperationId operationId); + +private: + CResponseHandler(CRemConCoreApiTarget &remConCoreApiTarget); + + void RunL(); + void DoCancel(); + +private: + RArray<TRemConCoreApiOperationId> iResponseArray; + CRemConCoreApiTarget &iRemConCoreApiTarget; +}; + +CResponseHandler::CResponseHandler(CRemConCoreApiTarget &remConCoreApiTarget) + : CActive(CActive::EPriorityStandard), + iRemConCoreApiTarget(remConCoreApiTarget) +{ + CActiveScheduler::Add(this); +} + +CResponseHandler *CResponseHandler::NewL(CRemConCoreApiTarget &remConCoreApiTarget) +{ + CResponseHandler *self = + new (ELeave) CResponseHandler(remConCoreApiTarget); + return self; +} + +CResponseHandler::~CResponseHandler() +{ + Cancel(); + iResponseArray.Close(); +} + +void CResponseHandler::CompleteAnyKey(TRemConCoreApiOperationId operationId) +{ + if (!IsActive()) { + TInt error = KErrNone; + iRemConCoreApiTarget.SendResponse(iStatus, operationId, error); + SetActive(); + } else { + // already active. Append to array and complete later. + iResponseArray.Append(operationId); + } +} + +void CResponseHandler::DoCancel() +{ + iRemConCoreApiTarget.Cancel(); +} + +void CResponseHandler::RunL() +{ + // if any existing -> Send response + if (iResponseArray.Count()) { + CompleteAnyKey(iResponseArray[0]); + // Remove already completed key + iResponseArray.Remove(0); + iResponseArray.Compress(); + } +} + + +/* + * QS60KeyCapture provides media key handling using services from RemCon. + */ +QS60KeyCapture::QS60KeyCapture(CCoeEnv *env, QObject *parent): + QObject(parent), coeEnv(env), selector(0), target(0), handler(0) +{ + initRemCon(); + + TTimeIntervalMicroSeconds32 initialTime; + TTimeIntervalMicroSeconds32 time; + coeEnv->WsSession().GetKeyboardRepeatRate(initialTime, time); + initialRepeatTime = (initialTime.Int() / 1000); // msecs + repeatTime = (time.Int() / 1000); // msecs + + int clickTimeout = initialRepeatTime + repeatTime; + + volumeUpClickTimer.setSingleShot(true); + volumeDownClickTimer.setSingleShot(true); + repeatTimer.setSingleShot(true); + + volumeUpClickTimer.setInterval(clickTimeout); + volumeDownClickTimer.setInterval(clickTimeout); + repeatTimer.setInterval(initialRepeatTime); + + connect(&volumeUpClickTimer, SIGNAL(timeout()), this, SLOT(volumeUpClickTimerExpired())); + connect(&volumeDownClickTimer, SIGNAL(timeout()), this, SLOT(volumeDownClickTimerExpired())); + connect(&repeatTimer, SIGNAL(timeout()), this, SLOT(repeatTimerExpired())); +} + +/* + * Initializes RemCon connection for receiving key events + */ +void QS60KeyCapture::initRemCon() +{ + try { + QT_TRAP_THROWING( + selector = CRemConInterfaceSelector::NewL(); + target = CRemConCoreApiTarget::NewL(*selector, *this); + handler = CResponseHandler::NewL(*target); + selector->OpenTargetL()); + } catch (const std::exception &e) { + delete handler; + delete selector; + selector = 0; + target = 0; + handler = 0; + } +} + +QS60KeyCapture::~QS60KeyCapture() +{ + delete handler; + delete selector; +} + +void QS60KeyCapture::MrccatoCommand(TRemConCoreApiOperationId operationId, + TRemConCoreApiButtonAction buttonAction) +{ + if (!target) + return; + + switch (operationId) { + // Volume up/down keys auto repeat if user hold the key long enough + case ERemConCoreApiVolumeUp: + case ERemConCoreApiVolumeDown: + { + // Side key events are sent using following scheme: + // - ButtonClick is sent first + // - if nothing happens before repeat timer expires, no further events are generated + // - if user holds the button longer, ButtonPress is sent and + // when button is finally released ButtonRelease is sent. + + QTimer &timer = (operationId == ERemConCoreApiVolumeUp) ? volumeUpClickTimer : volumeDownClickTimer; + + switch (buttonAction) { + case ERemConCoreApiButtonPress: + if (timer.isActive()) { + timer.stop(); + // force repeat event + repeatKeyEvent = mapToKeyEvent(operationId); + repeatTimerExpired(); + } else { + sendKey(operationId, QEvent::KeyPress); + repeatTimer.start(initialRepeatTime); + } + break; + case ERemConCoreApiButtonRelease: + timer.stop(); + sendKey(operationId, QEvent::KeyRelease); + repeatTimer.stop(); + break; + case ERemConCoreApiButtonClick: + if (timer.isActive()) { + timer.stop(); + sendKey(operationId, QEvent::KeyRelease); + } + sendKey(operationId, QEvent::KeyPress); + timer.start(); + repeatTimer.stop(); + break; + default: + break; + } + } + break; + default: + switch (buttonAction) { + case ERemConCoreApiButtonPress: + sendKey(operationId, QEvent::KeyPress); + repeatTimer.start(initialRepeatTime); + break; + case ERemConCoreApiButtonRelease: + sendKey(operationId, QEvent::KeyRelease); + repeatTimer.stop(); + break; + case ERemConCoreApiButtonClick: + sendKey(operationId, QEvent::KeyPress); + sendKey(operationId, QEvent::KeyRelease); + repeatTimer.stop(); + break; + default: + break; + } + break; + } + + if (handler) + handler->CompleteAnyKey(operationId); +} + +/* + * Sends volume up KeyRelease event + */ +void QS60KeyCapture::volumeUpClickTimerExpired() +{ + sendKey(ERemConCoreApiVolumeUp, QEvent::KeyRelease); +} + +/* + * Sends volume down KeyRelease event + */ +void QS60KeyCapture::volumeDownClickTimerExpired() +{ + sendKey(ERemConCoreApiVolumeDown, QEvent::KeyRelease); +} + +/* + * Repeats last key event + */ +void QS60KeyCapture::repeatTimerExpired() +{ + // set auto repeat flag on + repeatKeyEvent.iRepeats = 1; + coeEnv->SimulateKeyEventL(repeatKeyEvent, EEventKey); + repeatTimer.start(repeatTime); +} + +/* + * Maps RemCon operation id to key event (includes key code and scan codes) + */ +TKeyEvent QS60KeyCapture::mapToKeyEvent(TRemConCoreApiOperationId operationId) +{ + TKeyEvent keyEvent; + keyEvent.iModifiers = 0; + keyEvent.iRepeats = 0; + keyEvent.iCode = qt_keymapper_private()->mapS60RemConIdToS60Key(operationId); + keyEvent.iScanCode = qt_keymapper_private()->mapS60RemConIdToS60ScanCodes(operationId); + return keyEvent; +} + +/* + * Delivers key events to the application. + * RemCon operations are converted to simulated key events + */ +void QS60KeyCapture::sendKey(TRemConCoreApiOperationId operationId, QEvent::Type type) +{ + TKeyEvent keyEvent = mapToKeyEvent(operationId); + + if (type == QEvent::KeyPress) { + coeEnv->SimulateKeyEventL(keyEvent, EEventKeyDown); + coeEnv->SimulateKeyEventL(keyEvent, EEventKey); + // save the key press event for repeats + repeatKeyEvent = keyEvent; + } else { + coeEnv->SimulateKeyEventL(keyEvent, EEventKeyUp); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/s60framework/qs60keycapture_p.h b/src/gui/s60framework/qs60keycapture_p.h new file mode 100644 index 0000000..a50fad7 --- /dev/null +++ b/src/gui/s60framework/qs60keycapture_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Symbian application wrapper of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QS60KEYCAPTURE_P_H +#define QS60KEYCAPTURE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#ifdef Q_OS_SYMBIAN + +#include <QObject> +#include <QEvent> +#include <QTimer> +#include <remconcoreapitargetobserver.h> +#include <w32std.h> + +class CRemConInterfaceSelector; +class CRemConCoreApiTarget; +class CResponseHandler; +class CCoeEnv; + +QT_BEGIN_NAMESPACE + +class QS60KeyCapture: public QObject, + public MRemConCoreApiTargetObserver +{ + Q_OBJECT +public: + QS60KeyCapture(CCoeEnv *env, QObject *parent = 0); + ~QS60KeyCapture(); + + void MrccatoCommand(TRemConCoreApiOperationId operationId, + TRemConCoreApiButtonAction buttonAct); + +private slots: + void volumeUpClickTimerExpired(); + void volumeDownClickTimerExpired(); + void repeatTimerExpired(); + +private: + void sendKey(TRemConCoreApiOperationId operationId, QEvent::Type type); + TKeyEvent mapToKeyEvent(TRemConCoreApiOperationId operationId); + void initRemCon(); + +private: + QS60KeyCapture(); + Q_DISABLE_COPY(QS60KeyCapture) + + CCoeEnv *coeEnv; + + CRemConInterfaceSelector *selector; + CRemConCoreApiTarget *target; + CResponseHandler *handler; + + QTimer volumeUpClickTimer; + QTimer volumeDownClickTimer; + + TKeyEvent repeatKeyEvent; + int initialRepeatTime; // time before first repeat key event + int repeatTime; // time between subsequent repeat key events + QTimer repeatTimer; +}; + +QT_END_NAMESPACE + +#endif // Q_OS_SYMBIAN +#endif // QS60KEYCAPTURE_P_H diff --git a/src/gui/s60framework/qs60mainappui.cpp b/src/gui/s60framework/qs60mainappui.cpp index 8a8a03d..bd3017c 100644 --- a/src/gui/s60framework/qs60mainappui.cpp +++ b/src/gui/s60framework/qs60mainappui.cpp @@ -49,6 +49,7 @@ #include <avkon.rsg> #endif #include <barsread.h> +#include <coeutils.h> #include <qconfig.h> #include "qs60mainappui.h" @@ -58,6 +59,7 @@ #include <private/qmenu_p.h> #include <private/qt_s60_p.h> #include <qdebug.h> +#include "qs60keycapture_p.h" //Animated wallpapers in Qt applications are not supported. const TInt KAknDisableAnimationBackground = 0x02000000; @@ -65,6 +67,20 @@ const TInt KAknSingleClickCompatible = 0x01000000; QT_BEGIN_NAMESPACE +static QS60KeyCapture *qt_S60KeyCapture = 0; + +static void installS60KeyCapture(CCoeEnv *env) +{ + if (QApplication::testAttribute(Qt::AA_CaptureMultimediaKeys)) + qt_S60KeyCapture = new QS60KeyCapture(env); +} + +static void removeS60KeyCapture() +{ + delete qt_S60KeyCapture; + qt_S60KeyCapture = 0; +} + /*! \class QS60MainAppUi \since 4.6 @@ -126,6 +142,7 @@ void QS60MainAppUi::ConstructL() } #endif BaseConstructL(flags); + installS60KeyCapture(iCoeEnv); } /*! @@ -141,6 +158,7 @@ QS60MainAppUi::QS60MainAppUi() */ QS60MainAppUi::~QS60MainAppUi() { + removeS60KeyCapture(); } /*! @@ -420,6 +438,16 @@ void QS60MainAppUi::HandleForegroundEventL(TBool aForeground) QS60MainAppUiBase::HandleForegroundEventL(aForeground); } +/*! + \internal +*/ +TBool QS60MainAppUi::ProcessCommandParametersL(TApaCommand /*aCommand*/, TFileName &/*aDocumentName*/, const TDesC8 &/*aTail*/) +{ + // bypass CEikAppUi::ProcessCommandParametersL(..) which modifies aDocumentName, preventing apparc document opening from working. + // The return value is effectively unused in Qt apps (see QS60MainDocument::OpenFileL) + return EFalse; +} + #ifndef Q_WS_S60 void QS60StubAknAppUi::HandleViewDeactivation(const TVwsViewId &, const TVwsViewId &) {} diff --git a/src/gui/s60framework/qs60mainappui.h b/src/gui/s60framework/qs60mainappui.h index f3d66d8..374bc44 100644 --- a/src/gui/s60framework/qs60mainappui.h +++ b/src/gui/s60framework/qs60mainappui.h @@ -131,6 +131,7 @@ public: virtual void HandleViewDeactivation(const TVwsViewId &aViewIdToBeDeactivated, const TVwsViewId &aNewlyActivatedViewId); virtual void PrepareToExit(); virtual void HandleTouchPaneSizeChange(); + virtual TBool ProcessCommandParametersL(TApaCommand aCommand, TFileName &aDocumentName, const TDesC8 &aTail); protected: virtual void HandleScreenDeviceChangedL(); diff --git a/src/gui/s60framework/qs60maindocument.cpp b/src/gui/s60framework/qs60maindocument.cpp index 4039bcf..eb1d87c 100644 --- a/src/gui/s60framework/qs60maindocument.cpp +++ b/src/gui/s60framework/qs60maindocument.cpp @@ -41,6 +41,9 @@ #include "qs60mainappui.h" #include "qs60maindocument.h" +#include "qcoreapplication.h" +#include "qevent.h" +#include "private/qcore_symbian_p.h" #include <exception> @@ -108,9 +111,15 @@ CEikAppUi *QS60MainDocument::CreateAppUiL() /*! \internal */ -CFileStore *QS60MainDocument::OpenFileL(TBool aDoOpen, const TDesC &aFilename, RFs &aFs) +CFileStore *QS60MainDocument::OpenFileL(TBool /*aDoOpen*/, const TDesC &aFilename, RFs &/*aFs*/) { - return QS60MainDocumentBase::OpenFileL(aDoOpen, aFilename, aFs); + QT_TRYCATCH_LEAVING( { + QCoreApplication* app = QCoreApplication::instance(); + QString qname = qt_TDesC2QString(aFilename); + QFileOpenEvent* event = new QFileOpenEvent(qname); + app->postEvent(app, event); + }) + return 0; } /*! @@ -118,7 +127,12 @@ CFileStore *QS60MainDocument::OpenFileL(TBool aDoOpen, const TDesC &aFilename, R */ void QS60MainDocument::OpenFileL(CFileStore *&aFileStore, RFile &aFile) { - QS60MainDocumentBase::OpenFileL(aFileStore, aFile); + QT_TRYCATCH_LEAVING( { + QCoreApplication* app = QCoreApplication::instance(); + QFileOpenEvent* event = new QFileOpenEvent(aFile); + app->postEvent(app, event); + aFileStore = 0; + }) } QT_END_NAMESPACE diff --git a/src/gui/s60framework/s60framework.pri b/src/gui/s60framework/s60framework.pri index 19525b7..4e94c21 100644 --- a/src/gui/s60framework/s60framework.pri +++ b/src/gui/s60framework/s60framework.pri @@ -1,10 +1,12 @@ SOURCES += s60framework/qs60mainapplication.cpp \ - s60framework/qs60mainappui.cpp \ - s60framework/qs60maindocument.cpp - + s60framework/qs60mainappui.cpp \ + s60framework/qs60maindocument.cpp \ + s60framework/qs60keycapture.cpp HEADERS += s60framework/qs60mainapplication_p.h \ - s60framework/qs60mainapplication.h \ - s60framework/qs60mainappui.h \ - s60framework/qs60maindocument.h - -!isEmpty(QT_LIBINFIX): DEFINES += QT_LIBINFIX_UNQUOTED=$$QT_LIBINFIX
\ No newline at end of file + s60framework/qs60mainapplication.h \ + s60framework/qs60mainappui.h \ + s60framework/qs60maindocument.h \ + s60framework/qs60keycapture_p.h +LIBS += -lremconcoreapi \ + -lremconinterfacebase +!isEmpty(QT_LIBINFIX):DEFINES += QT_LIBINFIX_UNQUOTED=$$QT_LIBINFIX diff --git a/src/gui/styles/qcleanlooksstyle.cpp b/src/gui/styles/qcleanlooksstyle.cpp index 68016aa..6da0e91 100644 --- a/src/gui/styles/qcleanlooksstyle.cpp +++ b/src/gui/styles/qcleanlooksstyle.cpp @@ -68,9 +68,6 @@ #include <qlibrary.h> #include <private/qstylehelper_p.h> -#define CL_MAX(a,b) (a)>(b) ? (a):(b) // ### qMin/qMax does not work for vc6 -#define CL_MIN(a,b) (a)<(b) ? (a):(b) // remove this when it is working - QT_BEGIN_NAMESPACE using namespace QStyleHelper; @@ -533,8 +530,8 @@ static void qt_cleanlooks_draw_mdibutton(QPainter *painter, const QStyleOptionTi { QColor dark; dark.setHsv(option->palette.button().color().hue(), - CL_MIN(255, (int)(option->palette.button().color().saturation()*1.9)), - CL_MIN(255, (int)(option->palette.button().color().value()*0.7))); + qMin(255, (int)(option->palette.button().color().saturation()*1.9)), + qMin(255, (int)(option->palette.button().color().value()*0.7))); QColor highlight = option->palette.highlight().color(); @@ -691,11 +688,11 @@ void QCleanlooksStyle::drawPrimitive(PrimitiveElement elem, QColor darkOutline; QColor dark; darkOutline.setHsv(button.hue(), - CL_MIN(255, (int)(button.saturation()*3.0)), - CL_MIN(255, (int)(button.value()*0.6))); + qMin(255, (int)(button.saturation()*3.0)), + qMin(255, (int)(button.value()*0.6))); dark.setHsv(button.hue(), - CL_MIN(255, (int)(button.saturation()*1.9)), - CL_MIN(255, (int)(button.value()*0.7))); + qMin(255, (int)(button.saturation()*1.9)), + qMin(255, (int)(button.value()*0.7))); QColor tabFrameColor = mergedColors(option->palette.background().color(), dark.lighter(135), 60); @@ -844,7 +841,6 @@ void QCleanlooksStyle::drawPrimitive(PrimitiveElement elem, case PE_PanelButtonTool: painter->save(); if ((option->state & State_Enabled || option->state & State_On) || !(option->state & State_AutoRaise)) { - QRect rect = option->rect; QPen oldPen = painter->pen(); if (widget && widget->inherits("QDockWidgetTitleButton")) { @@ -1117,8 +1113,8 @@ void QCleanlooksStyle::drawPrimitive(PrimitiveElement elem, QColor gradientMidColor = option->palette.button().color(); QColor gradientStopColor; gradientStopColor.setHsv(buttonColor.hue(), - CL_MIN(255, (int)(buttonColor.saturation()*1.9)), - CL_MIN(255, (int)(buttonColor.value()*0.96))); + qMin(255, (int)(buttonColor.saturation()*1.9)), + qMin(255, (int)(buttonColor.value()*0.96))); QRect gradRect = rect.adjusted(1, 2, -1, -2); // gradient fill @@ -1244,7 +1240,6 @@ void QCleanlooksStyle::drawPrimitive(PrimitiveElement elem, if (const QStyleOptionTabWidgetFrame *twf = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) { QColor borderColor = darkOutline.lighter(110); QColor alphaCornerColor = mergedColors(borderColor, option->palette.background().color()); - QColor innerShadow = mergedColors(borderColor, option->palette.base().color()); int borderThickness = proxy()->pixelMetric(PM_TabBarBaseOverlap, twf, widget); bool reverse = (twf->direction == Qt::RightToLeft); @@ -1384,12 +1379,12 @@ void QCleanlooksStyle::drawControl(ControlElement element, const QStyleOption *o QColor button = option->palette.button().color(); QColor dark; dark.setHsv(button.hue(), - CL_MIN(255, (int)(button.saturation()*1.9)), - CL_MIN(255, (int)(button.value()*0.7))); + qMin(255, (int)(button.saturation()*1.9)), + qMin(255, (int)(button.value()*0.7))); QColor darkOutline; darkOutline.setHsv(button.hue(), - CL_MIN(255, (int)(button.saturation()*2.0)), - CL_MIN(255, (int)(button.value()*0.6))); + qMin(255, (int)(button.saturation()*2.0)), + qMin(255, (int)(button.value()*0.6))); QRect rect = option->rect; QColor shadow = mergedColors(option->palette.background().color().darker(120), dark.lighter(130), 60); @@ -1662,8 +1657,8 @@ void QCleanlooksStyle::drawControl(ControlElement element, const QStyleOption *o QColor gradientStopColor; QColor gradientStartColor = option->palette.button().color(); gradientStopColor.setHsv(gradientStartColor.hue(), - CL_MIN(255, (int)(gradientStartColor.saturation()*2)), - CL_MIN(255, (int)(gradientStartColor.value()*0.96))); + qMin(255, (int)(gradientStartColor.saturation()*2)), + qMin(255, (int)(gradientStartColor.value()*0.96))); QLinearGradient gradient(rect.topLeft(), rect.bottomLeft()); if (option->palette.background().gradient()) { gradient.setStops(option->palette.background().gradient()->stops()); @@ -1882,7 +1877,6 @@ void QCleanlooksStyle::drawControl(ControlElement element, const QStyleOption *o } else { alphaCornerColor = mergedColors(option->palette.background().color(), borderColor); } - QColor alphaTextColor = mergedColors(option->palette.background().color(), option->palette.text().color()); if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) { painter->fillRect(menuItem->rect, menuBackground); int w = 0; @@ -2223,7 +2217,6 @@ void QCleanlooksStyle::drawControl(ControlElement element, const QStyleOption *o && tabBarAlignment == Qt::AlignLeft); QColor light = tab->palette.light().color(); - QColor midlight = tab->palette.midlight().color(); QColor background = tab->palette.background().color(); int borderThinkness = proxy()->pixelMetric(PM_TabBarBaseOverlap, tab, widget); @@ -2425,14 +2418,14 @@ void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOp QColor grooveColor; QColor darkOutline; dark.setHsv(button.hue(), - CL_MIN(255, (int)(button.saturation()*1.9)), - CL_MIN(255, (int)(button.value()*0.7))); + qMin(255, (int)(button.saturation()*1.9)), + qMin(255, (int)(button.value()*0.7))); grooveColor.setHsv(button.hue(), - CL_MIN(255, (int)(button.saturation()*2.6)), - CL_MIN(255, (int)(button.value()*0.9))); + qMin(255, (int)(button.saturation()*2.6)), + qMin(255, (int)(button.value()*0.9))); darkOutline.setHsv(button.hue(), - CL_MIN(255, (int)(button.saturation()*3.0)), - CL_MIN(255, (int)(button.value()*0.6))); + qMin(255, (int)(button.saturation()*3.0)), + qMin(255, (int)(button.value()*0.6))); QColor alphaCornerColor; if (widget) { @@ -2447,14 +2440,6 @@ void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOp QColor gradientStartColor = option->palette.button().color().lighter(108); QColor gradientStopColor = mergedColors(option->palette.button().color().darker(108), dark.lighter(150), 70); - QColor highlightedGradientStartColor = option->palette.button().color(); - QColor highlightedGradientStopColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 85); - - QColor highlightedDarkInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 35); - QColor highlightedLightInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 58); - - QColor buttonShadowAlpha = option->palette.background().color().darker(105); - QPalette palette = option->palette; switch (control) { @@ -3440,7 +3425,6 @@ void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOp if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) { QRect groove = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); QRect handle = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); - QRect ticks = proxy()->subControlRect(CC_Slider, option, SC_SliderTickmarks, widget); bool horizontal = slider->orientation == Qt::Horizontal; bool ticksAbove = slider->tickPosition & QSlider::TicksAbove; @@ -3542,8 +3526,6 @@ void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOp QRect pixmapRect(0, 0, handle.width(), handle.height()); QPainter handlePainter(&cache); - QColor highlightedGradientStartColor = option->palette.button().color(); - QColor highlightedGradientStopColor = option->palette.light().color(); QColor gradientStartColor = mergedColors(option->palette.button().color().lighter(155), dark.lighter(155), 50); QColor gradientStopColor = gradientStartColor.darker(108); @@ -3560,7 +3542,6 @@ void QCleanlooksStyle::drawComplexControl(ComplexControl control, const QStyleOp } // gradient fill - QRect innerBorder = gradRect; QRect r = pixmapRect.adjusted(1, 1, -1, -1); qt_cleanlooks_draw_gradient(&handlePainter, gradRect, @@ -3763,6 +3744,7 @@ int QCleanlooksStyle::pixelMetric(PixelMetric metric, const QStyleOption *option break; case PM_MenuBarItemSpacing: ret = 6; + break; case PM_MenuBarHMargin: ret = 0; break; diff --git a/src/gui/styles/qcommonstyle.cpp b/src/gui/styles/qcommonstyle.cpp index 29b9d0d..327bedf 100644 --- a/src/gui/styles/qcommonstyle.cpp +++ b/src/gui/styles/qcommonstyle.cpp @@ -223,16 +223,13 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q --yy; } if (!(opt->state & State_Enabled) && !(opt->state & State_On)) { - int pnt; - p->setPen(opt->palette.highlightedText().color()); - QPoint offset(1, 1); - for (pnt = 0; pnt < a.size(); ++pnt) - a[pnt].translate(offset.x(), offset.y()); + p->save(); + p->translate(1, 1); + p->setPen(opt->palette.light().color()); p->drawLines(a); - for (pnt = 0; pnt < a.size(); ++pnt) - a[pnt].translate(offset.x(), offset.y()); + p->restore(); } - p->setPen(opt->palette.text().color()); + p->setPen((opt->state & State_On) ? opt->palette.highlightedText().color() : opt->palette.text().color()); p->drawLines(a); break; } case PE_Frame: @@ -840,14 +837,12 @@ static void drawArrow(const QStyle *style, const QStyleOptionToolButton *toolbut QSize QCommonStylePrivate::viewItemSize(const QStyleOptionViewItemV4 *option, int role) const { - Q_Q(const QCommonStyle); - const QWidget *widget = option->widget; switch (role) { case Qt::CheckStateRole: if (option->features & QStyleOptionViewItemV2::HasCheckIndicator) - return QSize(q->pixelMetric(QStyle::PM_IndicatorWidth, option, widget), - q->pixelMetric(QStyle::PM_IndicatorHeight, option, widget)); + return QSize(proxyStyle->pixelMetric(QStyle::PM_IndicatorWidth, option, widget), + proxyStyle->pixelMetric(QStyle::PM_IndicatorHeight, option, widget)); break; case Qt::DisplayRole: if (option->features & QStyleOptionViewItemV2::HasDisplay) { @@ -858,7 +853,7 @@ QSize QCommonStylePrivate::viewItemSize(const QStyleOptionViewItemV4 *option, in textLayout.setFont(option->font); textLayout.setText(option->text); const bool wrapText = option->features & QStyleOptionViewItemV2::WrapText; - const int textMargin = q->pixelMetric(QStyle::PM_FocusFrameHMargin, option, widget) + 1; + const int textMargin = proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, option, widget) + 1; QRect bounds = option->rect; switch (option->decorationPosition) { case QStyleOptionViewItem::Left: @@ -919,11 +914,11 @@ static QSizeF viewItemTextLayout(QTextLayout &textLayout, int lineWidth) return QSizeF(widthUsed, height); } + void QCommonStylePrivate::viewItemDrawText(QPainter *p, const QStyleOptionViewItemV4 *option, const QRect &rect) const { - Q_Q(const QCommonStyle); const QWidget *widget = option->widget; - const int textMargin = q->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1; + const int textMargin = proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1; QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding const bool wrapText = option->features & QStyleOptionViewItemV2::WrapText; @@ -936,7 +931,7 @@ void QCommonStylePrivate::viewItemDrawText(QPainter *p, const QStyleOptionViewIt textLayout.setFont(option->font); textLayout.setText(option->text); - QSizeF textLayoutSize = viewItemTextLayout(textLayout, textRect.width()); + viewItemTextLayout(textLayout, textRect.width()); QString elidedText; qreal height = 0; @@ -1001,7 +996,6 @@ void QCommonStylePrivate::viewItemDrawText(QPainter *p, const QStyleOptionViewIt void QCommonStylePrivate::viewItemLayout(const QStyleOptionViewItemV4 *opt, QRect *checkRect, QRect *pixmapRect, QRect *textRect, bool sizehint) const { - Q_Q(const QCommonStyle); Q_ASSERT(checkRect && pixmapRect && textRect); *pixmapRect = QRect(QPoint(0, 0), viewItemSize(opt, Qt::DecorationRole)); *textRect = QRect(QPoint(0, 0), viewItemSize(opt, Qt::DisplayRole)); @@ -1011,9 +1005,9 @@ void QCommonStylePrivate::viewItemLayout(const QStyleOptionViewItemV4 *opt, QRe const bool hasCheck = checkRect->isValid(); const bool hasPixmap = pixmapRect->isValid(); const bool hasText = textRect->isValid(); - const int textMargin = hasText ? q->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, widget) + 1 : 0; - const int pixmapMargin = hasPixmap ? q->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, widget) + 1 : 0; - const int checkMargin = hasCheck ? q->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, widget) + 1 : 0; + const int textMargin = hasText ? proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, widget) + 1 : 0; + const int pixmapMargin = hasPixmap ? proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, widget) + 1 : 0; + const int checkMargin = hasCheck ? proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, widget) + 1 : 0; int x = opt->rect.left(); int y = opt->rect.top(); int w, h; @@ -2218,7 +2212,7 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, if (vopt->state & QStyle::State_HasFocus) { QStyleOptionFocusRect o; o.QStyleOption::operator=(*vopt); - o.rect = subElementRect(SE_ItemViewItemFocusRect, vopt, widget); + o.rect = proxy()->subElementRect(SE_ItemViewItemFocusRect, vopt, widget); o.state |= QStyle::State_KeyboardFocusChange; o.state |= QStyle::State_Item; QPalette::ColorGroup cg = (vopt->state & QStyle::State_Enabled) @@ -5294,7 +5288,7 @@ QPixmap QCommonStyle::standardPixmap(StandardPixmap sp, const QStyleOption *opti pixmap = QIcon::fromTheme(QLatin1String("media-seek-backward")).pixmap(16); break; case SP_MediaSkipForward: - pixmap = QIcon::fromTheme(QLatin1String("media-skip-backward")).pixmap(16); + pixmap = QIcon::fromTheme(QLatin1String("media-skip-forward")).pixmap(16); break; case SP_MediaSkipBackward: pixmap = QIcon::fromTheme(QLatin1String("media-skip-backward")).pixmap(16); diff --git a/src/gui/styles/qgtkpainter.cpp b/src/gui/styles/qgtkpainter.cpp index 2f1daf5..0b1c3d3 100644 --- a/src/gui/styles/qgtkpainter.cpp +++ b/src/gui/styles/qgtkpainter.cpp @@ -586,7 +586,6 @@ void QGtkPainter::paintShadow(GtkWidget *gtkWidget, const gchar* part, if (!rect.isValid()) return; - QRect r = rect; QPixmap cache; QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) % pmKey; if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { @@ -605,7 +604,6 @@ void QGtkPainter::paintFlatBox(GtkWidget *gtkWidget, const gchar* part, { if (!rect.isValid()) return; - QRect r = rect; QPixmap cache; QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) % pmKey; if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, cache)) { @@ -632,7 +630,6 @@ void QGtkPainter::paintExtention(GtkWidget *gtkWidget, if (!rect.isValid()) return; - QRect r = rect; QPixmap cache; QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) % HexString<uchar>(gap_pos); @@ -660,7 +657,6 @@ void QGtkPainter::paintOption(GtkWidget *gtkWidget, const QRect &radiorect, if (!rect.isValid()) return; - QRect r = rect; QPixmap cache; QString pixmapName = uniqueName(detail, state, shadow, rect.size()); GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()}; @@ -692,7 +688,6 @@ void QGtkPainter::paintCheckbox(GtkWidget *gtkWidget, const QRect &checkrect, if (!rect.isValid()) return; - QRect r = rect; QPixmap cache; QString pixmapName = uniqueName(detail, state, shadow, rect.size()); GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()}; diff --git a/src/gui/styles/qgtkstyle.cpp b/src/gui/styles/qgtkstyle.cpp index fae241a..513a546 100644 --- a/src/gui/styles/qgtkstyle.cpp +++ b/src/gui/styles/qgtkstyle.cpp @@ -782,7 +782,6 @@ void QGtkStyle::drawPrimitive(PrimitiveElement element, GtkStateType state = gtkPainter.gtkState(option); style = gtkTreeHeader->style; GtkArrowType type = GTK_ARROW_UP; - QRect r = header->rect; QImage arrow; // This sorting indicator inversion is intentional, and follows the GNOME HIG. // See http://library.gnome.org/devel/hig-book/stable/controls-lists.html.en#controls-lists-sortable @@ -1706,12 +1705,17 @@ void QGtkStyle::drawComplexControl(ComplexControl control, const QStyleOptionCom fakePos = maximum; else if (scrollBar->sliderPosition > scrollBar->minimum) fakePos = maximum - 1; - GtkObject *adjustment = d->gtk_adjustment_new(fakePos, 0, maximum, 0, 0, 0); - if (horizontal) - d->gtk_range_set_adjustment((GtkRange*)(gtkHScrollBar), (GtkAdjustment*)(adjustment)); - else - d->gtk_range_set_adjustment((GtkRange*)(gtkVScrollBar), (GtkAdjustment*)(adjustment)); + + GtkRange *range = (GtkRange*)(horizontal ? gtkHScrollBar : gtkVScrollBar); + GtkAdjustment *adjustment = d->gtk_range_get_adjustment(range); + + if (adjustment) { + d->gtk_adjustment_configure(adjustment, fakePos, 0, maximum, 0, 0, 0); + } else { + adjustment = (GtkAdjustment*)d->gtk_adjustment_new(fakePos, 0, maximum, 0, 0, 0); + d->gtk_range_set_adjustment(range, adjustment); + } if (scrollBar->subControls & SC_ScrollBarGroove) { GtkStateType state = GTK_STATE_ACTIVE; @@ -1852,7 +1856,6 @@ void QGtkStyle::drawComplexControl(ComplexControl control, const QStyleOptionCom editArea.setRight(upRect.left()); } if (spinBox->frame) { - GtkShadowType shadow = GTK_SHADOW_OUT; GtkStateType state = gtkPainter.gtkState(option); if (!(option->state & State_Enabled)) @@ -1862,7 +1865,6 @@ void QGtkStyle::drawComplexControl(ComplexControl control, const QStyleOptionCom else if (state == GTK_STATE_PRELIGHT) state = GTK_STATE_NORMAL; - shadow = GTK_SHADOW_IN; style = gtkPainter.getStyle(gtkSpinButton); @@ -1990,15 +1992,29 @@ void QGtkStyle::drawComplexControl(ComplexControl control, const QStyleOptionCom style = scaleWidget->style; if ((option->subControls & SC_SliderGroove) && groove.isValid()) { - GtkObject *adjustment = d->gtk_adjustment_new(slider->sliderPosition, - slider->minimum, - slider->maximum, - slider->singleStep, - slider->singleStep, - slider->pageStep); + + GtkRange *range = (GtkRange*)scaleWidget; + GtkAdjustment *adjustment = d->gtk_range_get_adjustment(range); + if (adjustment) { + d->gtk_adjustment_configure(adjustment, + slider->sliderPosition, + slider->minimum, + slider->maximum, + slider->singleStep, + slider->singleStep, + slider->pageStep); + } else { + adjustment = (GtkAdjustment*)d->gtk_adjustment_new(slider->sliderPosition, + slider->minimum, + slider->maximum, + slider->singleStep, + slider->singleStep, + slider->pageStep); + d->gtk_range_set_adjustment(range, adjustment); + } + int outerSize; - d->gtk_range_set_adjustment ((GtkRange*)(scaleWidget), (GtkAdjustment*)(adjustment)); - d->gtk_range_set_inverted((GtkRange*)(scaleWidget), !horizontal); + d->gtk_range_set_inverted(range, !horizontal); d->gtk_widget_style_get(scaleWidget, "trough-border", &outerSize, NULL); outerSize++; @@ -2998,8 +3014,7 @@ void QGtkStyle::drawControl(ControlElement element, else if (bar->progress > bar->minimum) fakePos = maximum - 1; - GtkObject *adjustment = d->gtk_adjustment_new(fakePos, 0, maximum, 0, 0, 0); - d->gtk_progress_set_adjustment((GtkProgress*)(gtkProgressBar), (GtkAdjustment*)(adjustment)); + d->gtk_progress_configure((GtkProgress*)gtkProgressBar, fakePos, 0, maximum); QRect progressBar; diff --git a/src/gui/styles/qgtkstyle_p.cpp b/src/gui/styles/qgtkstyle_p.cpp index f38a726..ae2b918 100644 --- a/src/gui/styles/qgtkstyle_p.cpp +++ b/src/gui/styles/qgtkstyle_p.cpp @@ -121,7 +121,8 @@ Ptr_gtk_combo_box_entry_new QGtkStylePrivate::gtk_combo_box_entry_new = 0; Ptr_gtk_progress_bar_new QGtkStylePrivate::gtk_progress_bar_new = 0; Ptr_gtk_container_add QGtkStylePrivate::gtk_container_add = 0; Ptr_gtk_menu_shell_append QGtkStylePrivate::gtk_menu_shell_append = 0; -Ptr_gtk_progress_set_adjustment QGtkStylePrivate::gtk_progress_set_adjustment = 0; +Ptr_gtk_progress_configure QGtkStylePrivate::gtk_progress_configure = 0; +Ptr_gtk_range_get_adjustment QGtkStylePrivate::gtk_range_get_adjustment = 0; Ptr_gtk_range_set_adjustment QGtkStylePrivate::gtk_range_set_adjustment = 0; Ptr_gtk_range_set_inverted QGtkStylePrivate::gtk_range_set_inverted = 0; Ptr_gtk_icon_factory_lookup_default QGtkStylePrivate::gtk_icon_factory_lookup_default = 0; @@ -145,6 +146,7 @@ Ptr_gtk_paint_focus QGtkStylePrivate::gtk_paint_focus = 0; Ptr_gtk_paint_arrow QGtkStylePrivate::gtk_paint_arrow = 0; Ptr_gtk_paint_handle QGtkStylePrivate::gtk_paint_handle = 0; Ptr_gtk_paint_expander QGtkStylePrivate::gtk_paint_expander = 0; +Ptr_gtk_adjustment_configure QGtkStylePrivate::gtk_adjustment_configure = 0; Ptr_gtk_adjustment_new QGtkStylePrivate::gtk_adjustment_new = 0; Ptr_gtk_paint_hline QGtkStylePrivate::gtk_paint_hline = 0; Ptr_gtk_paint_vline QGtkStylePrivate::gtk_paint_vline = 0; @@ -376,7 +378,8 @@ void QGtkStylePrivate::resolveGtk() const gtk_entry_new = (Ptr_gtk_entry_new)libgtk.resolve("gtk_entry_new"); gtk_tree_view_new = (Ptr_gtk_tree_view_new)libgtk.resolve("gtk_tree_view_new"); gtk_combo_box_new = (Ptr_gtk_combo_box_new)libgtk.resolve("gtk_combo_box_new"); - gtk_progress_set_adjustment = (Ptr_gtk_progress_set_adjustment)libgtk.resolve("gtk_progress_set_adjustment"); + gtk_progress_configure = (Ptr_gtk_progress_configure)libgtk.resolve("gtk_progress_configure"); + gtk_range_get_adjustment = (Ptr_gtk_range_get_adjustment)libgtk.resolve("gtk_range_get_adjustment"); gtk_range_set_adjustment = (Ptr_gtk_range_set_adjustment)libgtk.resolve("gtk_range_set_adjustment"); gtk_range_set_inverted = (Ptr_gtk_range_set_inverted)libgtk.resolve("gtk_range_set_inverted"); gtk_container_add = (Ptr_gtk_container_add)libgtk.resolve("gtk_container_add"); @@ -405,6 +408,7 @@ void QGtkStylePrivate::resolveGtk() const gtk_paint_extension = (Ptr_gtk_paint_extension)libgtk.resolve("gtk_paint_extension"); gtk_paint_hline = (Ptr_gtk_paint_hline)libgtk.resolve("gtk_paint_hline"); gtk_paint_vline = (Ptr_gtk_paint_vline)libgtk.resolve("gtk_paint_vline"); + gtk_adjustment_configure = (Ptr_gtk_adjustment_configure)libgtk.resolve("gtk_adjustment_configure"); gtk_adjustment_new = (Ptr_gtk_adjustment_new)libgtk.resolve("gtk_adjustment_new"); gtk_menu_item_set_submenu = (Ptr_gtk_menu_item_set_submenu)libgtk.resolve("gtk_menu_item_set_submenu"); gtk_settings_get_default = (Ptr_gtk_settings_get_default)libgtk.resolve("gtk_settings_get_default"); diff --git a/src/gui/styles/qgtkstyle_p.h b/src/gui/styles/qgtkstyle_p.h index 7e5e943..e70ddef 100644 --- a/src/gui/styles/qgtkstyle_p.h +++ b/src/gui/styles/qgtkstyle_p.h @@ -174,8 +174,9 @@ typedef GtkWidget* (*Ptr_gtk_frame_new)(const gchar *); typedef GtkWidget* (*Ptr_gtk_expander_new)(const gchar*); typedef GtkWidget* (*Ptr_gtk_statusbar_new)(void); typedef GtkSettings* (*Ptr_gtk_settings_get_default)(void); +typedef GtkAdjustment* (*Ptr_gtk_range_get_adjustment)(GtkRange *); typedef void (*Ptr_gtk_range_set_adjustment)(GtkRange *, GtkAdjustment *); -typedef void (*Ptr_gtk_progress_set_adjustment)(GtkProgress *, GtkAdjustment *); +typedef void (*Ptr_gtk_progress_configure)(GtkProgress *, double, double, double); typedef void (*Ptr_gtk_range_set_inverted)(GtkRange*, bool); typedef void (*Ptr_gtk_container_add)(GtkContainer *container, GtkWidget *widget); typedef GtkIconSet* (*Ptr_gtk_icon_factory_lookup_default) (const gchar*); @@ -198,6 +199,7 @@ typedef void (*Ptr_gtk_paint_arrow) (GtkStyle*,GdkWindow*, GtkStateType, GtkSha typedef void (*Ptr_gtk_paint_option) (GtkStyle*,GdkWindow*, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint); typedef void (*Ptr_gtk_paint_flat_box) (GtkStyle*,GdkWindow*, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint , gint , gint , gint); typedef void (*Ptr_gtk_paint_extension) (GtkStyle *, GdkWindow *, GtkStateType, GtkShadowType, const GdkRectangle *, GtkWidget *, const gchar *, gint, gint, gint, gint, GtkPositionType); +typedef void (*Ptr_gtk_adjustment_configure) (GtkAdjustment *, double, double, double, double, double, double); typedef GtkObject* (*Ptr_gtk_adjustment_new) (double, double, double, double, double, double); typedef void (*Ptr_gtk_paint_hline) (GtkStyle *, GdkWindow *, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, gint, gint, gint y); typedef void (*Ptr_gtk_paint_vline) (GtkStyle *, GdkWindow *, GtkStateType, const GdkRectangle *, GtkWidget *, const gchar *, gint, gint, gint); @@ -393,7 +395,8 @@ public: static Ptr_gtk_progress_bar_new gtk_progress_bar_new; static Ptr_gtk_container_add gtk_container_add; static Ptr_gtk_menu_shell_append gtk_menu_shell_append; - static Ptr_gtk_progress_set_adjustment gtk_progress_set_adjustment; + static Ptr_gtk_progress_configure gtk_progress_configure; + static Ptr_gtk_range_get_adjustment gtk_range_get_adjustment; static Ptr_gtk_range_set_adjustment gtk_range_set_adjustment; static Ptr_gtk_range_set_inverted gtk_range_set_inverted; static Ptr_gtk_icon_factory_lookup_default gtk_icon_factory_lookup_default; @@ -416,6 +419,7 @@ public: static Ptr_gtk_paint_arrow gtk_paint_arrow; static Ptr_gtk_paint_handle gtk_paint_handle; static Ptr_gtk_paint_expander gtk_paint_expander; + static Ptr_gtk_adjustment_configure gtk_adjustment_configure; static Ptr_gtk_adjustment_new gtk_adjustment_new; static Ptr_gtk_paint_vline gtk_paint_vline; static Ptr_gtk_paint_hline gtk_paint_hline; diff --git a/src/gui/styles/qmacstyle_mac.mm b/src/gui/styles/qmacstyle_mac.mm index 5472dac..1c1713c 100644 --- a/src/gui/styles/qmacstyle_mac.mm +++ b/src/gui/styles/qmacstyle_mac.mm @@ -1063,7 +1063,7 @@ bool qt_mac_buttonIsRenderedFlat(const QPushButton *pushButton, const QStyleOpti { QMacStyle *macStyle = qobject_cast<QMacStyle *>(pushButton->style()); if (!macStyle) - return false; + return true; // revert to 'flat' behavior if not Mac style HIThemeButtonDrawInfo bdi; macStyle->d->initHIThemePushButton(option, pushButton, kThemeStateActive, &bdi); return bdi.kind == kThemeBevelButton; @@ -1195,15 +1195,15 @@ QRect QMacStylePrivate::comboboxEditBounds(const QRect &outerBounds, const HIThe QRect ret = outerBounds; switch (bdi.kind){ case kThemeComboBox: - ret.adjust(5, 8, -21, -4); + ret.adjust(5, 8, -22, -4); break; case kThemeComboBoxSmall: - ret.adjust(4, 5, -18, 0); - ret.setHeight(16); + ret.adjust(4, 6, -20, 0); + ret.setHeight(14); break; case kThemeComboBoxMini: - ret.adjust(4, 5, -16, 0); - ret.setHeight(13); + ret.adjust(4, 5, -18, -1); + ret.setHeight(12); break; case kThemePopupButton: ret.adjust(10, 3, -23, -3); @@ -1566,8 +1566,7 @@ void QMacStylePrivate::timerEvent(QTimerEvent *) progressBars.removeAt(i); } else { if (QProgressBar *pb = qobject_cast<QProgressBar *>(maybeProgress)) { - if (pb->maximum() == 0 || pb->value() > 0 - && pb->value() < pb->maximum()) { + if (pb->maximum() == 0 || (pb->value() > 0 && pb->value() < pb->maximum())) { if (doAnimate(AquaProgressBar)) pb->update(); } @@ -1642,7 +1641,7 @@ bool QMacStylePrivate::eventFilter(QObject *o, QEvent *e) case QEvent::FocusOut: case QEvent::Show: case QEvent::WindowActivate: { - QList<QPushButton *> list = qFindChildren<QPushButton *>(btn->window()); + QList<QPushButton *> list = btn->window()->findChildren<QPushButton *>(); for (int i = 0; i < list.size(); ++i) { QPushButton *pBtn = list.at(i); if ((e->type() == QEvent::FocusOut @@ -1948,10 +1947,9 @@ void QMacStyle::unpolish(QWidget* w) rubber->setAttribute(Qt::WA_NoSystemBackground, true); } - if (QFocusFrame *frame = qobject_cast<QFocusFrame *>(w)) { + if (QFocusFrame *frame = qobject_cast<QFocusFrame *>(w)) frame->setAttribute(Qt::WA_NoSystemBackground, true); - frame->setAutoFillBackground(true); - } + QWindowsStyle::unpolish(w); } @@ -3086,7 +3084,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai } break; case PE_PanelScrollAreaCorner: { - const QBrush brush(qApp->palette().brush(QPalette::Base)); + const QBrush brush(opt->palette.brush(QPalette::Base)); p->fillRect(opt->rect, brush); p->setPen(QPen(QColor(217, 217, 217))); p->drawLine(opt->rect.topLeft(), opt->rect.topRight()); @@ -3609,7 +3607,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter break; } } - bool stretchTabs = (!verticalTabs && tabRect.height() > 22 || verticalTabs && tabRect.width() > 22); + bool stretchTabs = (!verticalTabs && tabRect.height() > 22) || (verticalTabs && tabRect.width() > 22); switch (tp) { case QStyleOptionTab::Beginning: @@ -3683,9 +3681,27 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter proxy()->drawItemText(p, nr, alignment, np, tab->state & State_Enabled, tab->text, QPalette::WindowText); p->restore(); - } + QCommonStyle::drawControl(ce, &myTab, p, w); + } else if (qMacVersion() >= QSysInfo::MV_10_7 && (tab->state & State_Selected)) { + p->save(); + rotateTabPainter(p, myTab.shape, myTab.rect); + + QPalette np = tab->palette; + np.setColor(QPalette::WindowText, QColor(0, 0, 0, 75)); + QRect nr = subElementRect(SE_TabBarTabText, opt, w); + nr.moveTop(-1); + int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextHideMnemonic; + proxy()->drawItemText(p, nr, alignment, np, tab->state & State_Enabled, + tab->text, QPalette::WindowText); - QCommonStyle::drawControl(ce, &myTab, p, w); + np.setColor(QPalette::WindowText, QColor(255, 255, 255, 255)); + nr.moveTop(-2); + proxy()->drawItemText(p, nr, alignment, np, tab->state & State_Enabled, + tab->text, QPalette::WindowText); + p->restore(); + } else { + QCommonStyle::drawControl(ce, &myTab, p, w); + } } else { p->save(); CGContextSetShouldAntialias(cg, true); @@ -4034,7 +4050,6 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter bdi.version = qt_mac_hitheme_version; bdi.state = kThemeMenuBarNormal; bdi.attributes = 0; - HIRect hirect = qt_hirectForQRect(mi->rect); HIThemeDrawMenuBarBackground(&menuRect, &bdi, cg, kHIThemeOrientationNormal); } @@ -4624,6 +4639,13 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex tdi.attributes &= ~kThemeTrackShowThumb; if (scrollBarLength < scrollButtonsCutoffSize(scrollButtonsCutoff, sizePolicy)) tdi.enableState = kThemeTrackNothingToScroll; + } else { + if (!(slider->subControls & SC_SliderHandle)) + tdi.attributes &= ~kThemeTrackShowThumb; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if (!(slider->subControls & SC_SliderGroove)) + tdi.attributes |= kThemeTrackHideTrack; +#endif } HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg, @@ -4703,7 +4725,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex HIThemeFrameDrawInfo fdi; fdi.version = qt_mac_hitheme_version; - fdi.state = kThemeStateInactive; + fdi.state = ((sb->state & State_ReadOnly) || !(sb->state & State_Enabled)) ? kThemeStateInactive : kThemeStateActive; fdi.kind = kHIThemeFrameTextFieldSquare; fdi.isFocused = false; HIRect hirect = qt_hirectForQRect(lineeditRect); @@ -5336,8 +5358,8 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op case SC_GroupBoxCheckBox: { // Cheat and use the smaller font if we need to bool checkable = groupBox->subControls & SC_GroupBoxCheckBox; - bool fontIsSet = (widget && widget->testAttribute(Qt::WA_SetFont) - || !QApplication::desktopSettingsAware()); + bool fontIsSet = (widget && widget->testAttribute(Qt::WA_SetFont)) + || !QApplication::desktopSettingsAware(); int tw; int h; int margin = flat || hasNoText ? 0 : 12; @@ -5540,6 +5562,57 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, // hack to work around horrible sizeHint() code in QAbstractSpinBox sz.setHeight(sz.height() - 3); break; + case QStyle::CT_TabWidget: + // the size between the pane and the "contentsRect" (+4,+4) + // (the "contentsRect" is on the inside of the pane) + sz = QWindowsStyle::sizeFromContents(ct, opt, csz, widget); + /** + This is supposed to show the relationship between the tabBar and + the stack widget of a QTabWidget. + Unfortunately ascii is not a good way of representing graphics..... + PS: The '=' line is the painted frame. + + top ---+ + | + | + | + | vvv just outside the painted frame is the "pane" + - -|- - - - - - - - - - <-+ + TAB BAR +=====^============ | +2 pixels + - - -|- - -|- - - - - - - <-+ + | | ^ ^^^ just inside the painted frame is the "contentsRect" + | | | + | overlap | + | | | + bottom ------+ <-+ +14 pixels + | + v + ------------------------------ <- top of stack widget + + + To summarize: + * 2 is the distance between the pane and the contentsRect + * The 14 and the 1's are the distance from the contentsRect to the stack widget. + (same value as used in SE_TabWidgetTabContents) + * overlap is how much the pane should overlap the tab bar + */ + // then add the size between the stackwidget and the "contentsRect" + + if (const QStyleOptionTabWidgetFrame *twf + = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { + QSize extra(0,0); + const int overlap = pixelMetric(PM_TabBarBaseOverlap, opt, widget); + const int gapBetweenTabbarAndStackWidget = 2 + 14 - overlap; + + if (getTabDirection(twf->shape) == kThemeTabNorth || getTabDirection(twf->shape) == kThemeTabSouth) { + extra = QSize(2, gapBetweenTabbarAndStackWidget + 1); + } else { + extra = QSize(gapBetweenTabbarAndStackWidget + 1, 2); + } + sz+= extra; + } + + break; case QStyle::CT_TabBarTab: if (const QStyleOptionTabV3 *tab = qstyleoption_cast<const QStyleOptionTabV3 *>(opt)) { const QAquaWidgetSize AquaSize = d->aquaSizeConstrain(opt, widget); diff --git a/src/gui/styles/qplastiquestyle.cpp b/src/gui/styles/qplastiquestyle.cpp index 68ab104..a5b0235 100644 --- a/src/gui/styles/qplastiquestyle.cpp +++ b/src/gui/styles/qplastiquestyle.cpp @@ -1054,7 +1054,7 @@ void QPlastiqueStylePrivate::drawPartialFrame(QPainter *painter, const QStyleOpt bool reverse = option->direction == Qt::RightToLeft; QStyleOptionFrame frameOpt; #ifndef QT_NO_LINEEDIT - if (QLineEdit *lineedit = qFindChild<QLineEdit *>(widget)) + if (QLineEdit *lineedit = widget->findChild<QLineEdit *>()) frameOpt.initFrom(lineedit); #else Q_UNUSED(widget) @@ -1362,11 +1362,8 @@ void QPlastiqueStyle::drawPrimitive(PrimitiveElement element, const QStyleOption if (const QStyleOptionFrame *lineEdit = qstyleoption_cast<const QStyleOptionFrame *>(option)) { // Panel of a line edit inside combo box or spin box is drawn in CC_ComboBox and CC_SpinBox if (widget) { -#ifndef QT_NO_COMBOBOX - if (qobject_cast<const QComboBox *>(widget->parentWidget())) - break; -#endif #ifndef QT_NO_SPINBOX + // Spinbox doesn't need a separate palette for the lineedit if (qobject_cast<const QAbstractSpinBox *>(widget->parentWidget())) break; #endif @@ -2010,14 +2007,10 @@ void QPlastiqueStyle::drawControl(ControlElement element, const QStyleOption *op } else { alphaCornerColor = mergedColors(option->palette.background().color(), borderColor); } - QColor alphaTextColor = mergedColors(option->palette.background().color(), option->palette.text().color()); QColor gradientStartColor = option->palette.button().color().lighter(104); QColor gradientStopColor = option->palette.button().color().darker(105); - QColor shadowGradientStartColor = option->palette.button().color().darker(115); - QColor shadowGradientStopColor = option->palette.button().color().darker(120); - QColor highlightedGradientStartColor = option->palette.button().color().lighter(101); QColor highlightedGradientStopColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 85); @@ -2028,8 +2021,6 @@ void QPlastiqueStyle::drawControl(ControlElement element, const QStyleOption *op QColor highlightedLightInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 58); QColor alphaInnerColor = mergedColors(highlightedDarkInnerBorderColor, option->palette.base().color()); - QColor lightShadow = lightShadowGradientStartColor; - QColor shadow = shadowGradientStartColor; switch (element) { #ifndef QT_NO_TABBAR @@ -3789,10 +3780,6 @@ void QPlastiqueStyle::drawComplexControl(ComplexControl control, const QStyleOpt } QColor gradientStartColor = option->palette.button().color().lighter(104); QColor gradientStopColor = option->palette.button().color().darker(105); - QColor highlightedGradientStartColor = option->palette.button().color().lighter(101); - QColor highlightedGradientStopColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 85); - QColor highlightedDarkInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 35); - QColor highlightedLightInnerBorderColor = mergedColors(option->palette.button().color(), option->palette.highlight().color(), 58); switch (control) { #ifndef QT_NO_SLIDER @@ -3800,7 +3787,6 @@ void QPlastiqueStyle::drawComplexControl(ComplexControl control, const QStyleOpt if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) { QRect grooveRegion = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); QRect handle = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); - QRect ticks = proxy()->subControlRect(CC_Slider, option, SC_SliderTickmarks, widget); bool horizontal = slider->orientation == Qt::Horizontal; bool ticksAbove = slider->tickPosition & QSlider::TicksAbove; bool ticksBelow = slider->tickPosition & QSlider::TicksBelow; diff --git a/src/gui/styles/qs60style.cpp b/src/gui/styles/qs60style.cpp index bb2d701..e9f7a86 100644 --- a/src/gui/styles/qs60style.cpp +++ b/src/gui/styles/qs60style.cpp @@ -78,14 +78,12 @@ #include "private/qcombobox_p.h" #include "private/qwidget_p.h" #include "private/qapplication_p.h" +#include "private/qfont_p.h" #if !defined(QT_NO_STYLE_S60) || defined(QT_PLUGIN) QT_BEGIN_NAMESPACE -// from text/qfont.cpp -extern Q_GUI_EXPORT int qt_defaultDpiY(); - const QS60StylePrivate::SkinElementFlags QS60StylePrivate::KDefaultSkinElementFlags = SkinElementFlags(SF_PointNorth | SF_StateEnabled); @@ -93,11 +91,8 @@ static const qreal goldenRatio = 1.618; const layoutHeader QS60StylePrivate::m_layoutHeaders[] = { // *** generated layout data *** -{240,320,1,19,"QVGA Landscape"}, -{320,240,1,19,"QVGA Portrait"}, {360,640,1,19,"NHD Landscape"}, {640,360,1,19,"NHD Portrait"}, -{352,800,1,12,"E90 Landscape"}, {480,640,1,19,"VGA Landscape"} // *** End of generated data *** }; @@ -540,29 +535,14 @@ void QS60StylePrivate::setCurrentLayout(int index) void QS60StylePrivate::drawPart(QS60StyleEnums::SkinParts skinPart, QPainter *painter, const QRect &rect, SkinElementFlags flags) { - static const bool doCache = -#if defined(Q_WS_S60) - // Freezes on 3.1. Anyways, caching is only really needed on touch UI - !(QSysInfo::s60Version() == QSysInfo::SV_S60_3_1 || QSysInfo::s60Version() == QSysInfo::SV_S60_3_2); -#else - true; -#endif - - const QPixmap skinPartPixMap((doCache ? cachedPart : part)(skinPart, rect.size(), painter, flags)); + const QPixmap skinPartPixMap((cachedPart)(skinPart, rect.size(), painter, flags)); if (!skinPartPixMap.isNull()) painter->drawPixmap(rect.topLeft(), skinPartPixMap); } void QS60StylePrivate::drawFrame(SkinFrameElements frameElement, QPainter *painter, const QRect &rect, SkinElementFlags flags) { - static const bool doCache = -#if defined(Q_WS_S60) - // Freezes on 3.1. Anyways, caching is only really needed on touch UI - !(QSysInfo::s60Version() == QSysInfo::SV_S60_3_1 || QSysInfo::s60Version() == QSysInfo::SV_S60_3_2); -#else - true; -#endif - const QPixmap frameElementPixMap((doCache ? cachedFrame : frame)(frameElement, rect.size(), flags)); + const QPixmap frameElementPixMap((cachedFrame)(frameElement, rect.size(), flags)); if (!frameElementPixMap.isNull()) painter->drawPixmap(rect.topLeft(), frameElementPixMap); } @@ -1048,23 +1028,10 @@ void QS60Style::drawComplexControl(ComplexControl control, const QStyleOptionCom drawPrimitive(PE_FrameFocusRect, optionSlider, painter, widget);*/ //Groove graphics - if (QS60StylePrivate::hasSliderGrooveGraphic()) { - const QS60StylePrivate::SkinElements grooveElement = horizontal ? - QS60StylePrivate::SE_SliderGrooveHorizontal : - QS60StylePrivate::SE_SliderGrooveVertical; - QS60StylePrivate::drawSkinElement(grooveElement, painter, sliderGroove, flags); - } else { - const QPoint sliderGrooveCenter = sliderGroove.center(); - const bool horizontal = optionSlider->orientation == Qt::Horizontal; - painter->save(); - if (widget) - painter->setPen(widget->palette().windowText().color()); - if (horizontal) - painter->drawLine(0, sliderGrooveCenter.y(), sliderGroove.right(), sliderGrooveCenter.y()); - else - painter->drawLine(sliderGrooveCenter.x(), 0, sliderGrooveCenter.x(), sliderGroove.bottom()); - painter->restore(); - } + const QS60StylePrivate::SkinElements grooveElement = horizontal ? + QS60StylePrivate::SE_SliderGrooveHorizontal : + QS60StylePrivate::SE_SliderGrooveVertical; + QS60StylePrivate::drawSkinElement(grooveElement, painter, sliderGroove, flags); //Handle graphics const QRect sliderHandle = subControlRect(control, optionSlider, SC_SliderHandle, widget); @@ -1163,11 +1130,10 @@ void QS60Style::drawComplexControl(ComplexControl control, const QStyleOptionCom case Qt::UpArrow: pe = PE_IndicatorArrowUp; break; - case Qt::DownArrow: + default: pe = PE_IndicatorArrowDown; break; - default: - break; } + } toolButton.rect = button; drawPrimitive(pe, &toolButton, painter, widget); } @@ -1341,8 +1307,8 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) { const bool isDisabled = !(option->state & State_Enabled); const bool isFlat = button->features & QStyleOptionButton::Flat; - QS60StyleEnums::SkinParts skinPart; - QS60StylePrivate::SkinElements skinElement; + QS60StyleEnums::SkinParts skinPart = QS60StyleEnums::SP_QsnFrButtonCenterInactive; + QS60StylePrivate::SkinElements skinElement = QS60StylePrivate::SE_ButtonInactive; if (!isDisabled) { const bool isPressed = (option->state & State_Sunken) || (option->state & State_On); @@ -1353,11 +1319,6 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, skinElement = isPressed ? QS60StylePrivate::SE_ButtonPressed : QS60StylePrivate::SE_ButtonNormal; } - } else { - if (isFlat) - skinPart =QS60StyleEnums::SP_QsnFrButtonCenterInactive; - else - skinElement = QS60StylePrivate::SE_ButtonInactive; } if (isFlat) QS60StylePrivate::drawSkinPart(skinPart, painter, option->rect, flags); @@ -1435,7 +1396,7 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, bool isScrollBarVisible = false; int scrollBarWidth = 0; - QList<QScrollBar *> scrollBars = qFindChildren<QScrollBar *>(widget); + QList<QScrollBar *> scrollBars = widget->findChildren<QScrollBar *>(); for (int i = 0; i < scrollBars.size(); ++i) { QScrollBar *scrollBar = scrollBars.at(i); if (scrollBar && scrollBar->orientation() == Qt::Vertical) { @@ -1493,26 +1454,14 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, checkMarkOption.rect = selectionRect; // Draw selection mark. if (isSelected && selectItems) { - proxy()->drawPrimitive(PE_IndicatorViewItemCheck, &checkMarkOption, painter, widget); // @todo: this should happen in the rect retrievel i.e. subElementRect() if (textRect.right() > selectionRect.left()) textRect.setRight(selectionRect.left()); } else if (voptAdj.features & QStyleOptionViewItemV2::HasCheckIndicator) { checkMarkOption.state = checkMarkOption.state & ~State_HasFocus; - switch (vopt->checkState) { - case Qt::Unchecked: - checkMarkOption.state |= State_Off; - break; - case Qt::PartiallyChecked: - checkMarkOption.state |= State_NoChange; - break; - case Qt::Checked: - checkMarkOption.state |= State_On; - break; - } - drawPrimitive(PE_IndicatorViewItemCheck, &checkMarkOption, painter, widget); } + proxy()->drawPrimitive(PE_IndicatorViewItemCheck, &checkMarkOption, painter, widget); } // draw the text @@ -1845,7 +1794,7 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, drawPrimitive(PE_IndicatorMenuCheckMark, &optionCheckBox, painter, widget); //draw icon and/or checkState - QPixmap pix = menuItem->icon.pixmap(pixelMetric(PM_SmallIconSize), + QPixmap pix = menuItem->icon.pixmap(iconRect.width(), enabled ? QIcon::Normal : QIcon::Disabled); const bool itemWithIcon = !pix.isNull(); if (itemWithIcon) { @@ -1993,36 +1942,31 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, if (!tbWidget || (widget && qobject_cast<QToolBar *>(widget->parentWidget()))) break; - // Normally in S60 5.0+ there is no background for toolbar, but in some cases with versatile QToolBar, + // Normally in S60 there is no background for toolbar, but in some cases with versatile QToolBar, // it looks a bit strange. So, lets fillRect with Button. - if (!QS60StylePrivate::isToolBarBackground()) { - QList<QAction *> actions = tbWidget->actions(); - bool justToolButtonsInToolBar = true; - for (int i = 0; i < actions.size(); ++i) { - QWidget *childWidget = tbWidget->widgetForAction(actions.at(i)); - const QToolButton *button = qobject_cast<const QToolButton *>(childWidget); - if (!button){ - justToolButtonsInToolBar = false; - } - } - - // Draw frame background - // for vertical toolbars with text only and - // for toolbars with extension buttons and - // for toolbars with widgets in them. - if (!justToolButtonsInToolBar || - (tbWidget && - (tbWidget->orientation() == Qt::Vertical) && - (tbWidget->toolButtonStyle() == Qt::ToolButtonTextOnly))) { - painter->save(); - if (widget) - painter->setBrush(widget->palette().button()); - painter->setOpacity(0.3); - painter->fillRect(toolBar->rect, painter->brush()); - painter->restore(); - } - } else { - QS60StylePrivate::drawSkinElement(QS60StylePrivate::SE_ToolBar, painter, toolBar->rect, flags); + QList<QAction *> actions = tbWidget->actions(); + bool justToolButtonsInToolBar = true; + for (int i = 0; i < actions.size(); ++i) { + QWidget *childWidget = tbWidget->widgetForAction(actions.at(i)); + const QToolButton *button = qobject_cast<const QToolButton *>(childWidget); + if (!button) + justToolButtonsInToolBar = false; + } + + // Draw frame background + // for vertical toolbars with text only and + // for toolbars with extension buttons and + // for toolbars with widgets in them. + if (!justToolButtonsInToolBar + || (tbWidget + && tbWidget->orientation() == Qt::Vertical + && tbWidget->toolButtonStyle() == Qt::ToolButtonTextOnly)) { + painter->save(); + if (widget) + painter->setBrush(widget->palette().button()); + painter->setOpacity(0.3); + painter->fillRect(toolBar->rect, painter->brush()); + painter->restore(); } } break; @@ -2167,12 +2111,28 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti #ifndef QT_NO_ITEMVIEWS if (const QAbstractItemView *itemView = (qobject_cast<const QAbstractItemView *>(widget))) { if (const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(option)) { + QStyleOptionViewItemV4 voptAdj = *vopt; const bool checkBoxVisible = vopt->features & QStyleOptionViewItemV2::HasCheckIndicator; const bool singleSelection = itemView->selectionMode() == QAbstractItemView::SingleSelection || itemView->selectionMode() == QAbstractItemView::NoSelection; // draw either checkbox at the beginning if (checkBoxVisible && singleSelection) { - drawPrimitive(PE_IndicatorCheckBox, option, painter, widget); + if (vopt->features & QStyleOptionViewItemV2::HasCheckIndicator) { + switch (vopt->checkState) { + case Qt::Unchecked: + voptAdj.state |= State_Off; + break; + case Qt::PartiallyChecked: + voptAdj.state |= State_NoChange; + break; + case Qt::Checked: + voptAdj.state |= State_On; + break; + default: + break; + } + } + drawPrimitive(PE_IndicatorCheckBox, &voptAdj, painter, widget); // ... or normal "tick" selection at the end. } else if (option->state & State_Selected) { QRect tickRect = option->rect; @@ -2234,21 +2194,16 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti break; #ifndef QT_NO_TOOLBUTTON case PE_IndicatorArrowDown: + QS60StylePrivate::drawSkinPart(QS60StyleEnums::SP_QgnGrafScrollArrowDown, painter, option->rect, flags); + break; case PE_IndicatorArrowLeft: + QS60StylePrivate::drawSkinPart(QS60StyleEnums::SP_QgnGrafScrollArrowLeft, painter, option->rect, flags); + break; case PE_IndicatorArrowRight: - case PE_IndicatorArrowUp: { - QS60StyleEnums::SkinParts skinPart; - if (element==PE_IndicatorArrowDown) - skinPart = QS60StyleEnums::SP_QgnGrafScrollArrowDown; - else if (element==PE_IndicatorArrowLeft) - skinPart = QS60StyleEnums::SP_QgnGrafScrollArrowLeft; - else if (element==PE_IndicatorArrowRight) - skinPart = QS60StyleEnums::SP_QgnGrafScrollArrowRight; - else if (element==PE_IndicatorArrowUp) - skinPart = QS60StyleEnums::SP_QgnGrafScrollArrowUp; - - QS60StylePrivate::drawSkinPart(skinPart, painter, option->rect, flags); - } + QS60StylePrivate::drawSkinPart(QS60StyleEnums::SP_QgnGrafScrollArrowRight, painter, option->rect, flags); + break; + case PE_IndicatorArrowUp: + QS60StylePrivate::drawSkinPart(QS60StyleEnums::SP_QgnGrafScrollArrowUp, painter, option->rect, flags); break; #endif //QT_NO_TOOLBUTTON #ifndef QT_NO_SPINBOX @@ -2439,52 +2394,46 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti case PE_IndicatorBranch: #if defined(Q_WS_S60) - // 3.1 AVKON UI does not have tree view component, use common style for drawing there - if (QSysInfo::s60Version() == QSysInfo::SV_S60_3_1) { -#else - if (true) { -#endif - QCommonStyle::drawPrimitive(element, option, painter, widget); - } else { - if (const QStyleOptionViewItemV2 *vopt = qstyleoption_cast<const QStyleOptionViewItemV2 *>(option)) { - const bool rightLine = option->state & State_Item; - const bool downLine = option->state & State_Sibling; - const bool upLine = option->state & (State_Open | State_Children | State_Item | State_Sibling); - QS60StylePrivate::SkinElementFlags adjustedFlags = flags; - - QS60StyleEnums::SkinParts skinPart; - bool drawSkinPart = false; - if (rightLine && downLine && upLine) { - skinPart = QS60StyleEnums::SP_QgnIndiHlLineBranch; - drawSkinPart = true; - } else if (rightLine && upLine) { - skinPart = QS60StyleEnums::SP_QgnIndiHlLineEnd; - drawSkinPart = true; - } else if (upLine && downLine) { - skinPart = QS60StyleEnums::SP_QgnIndiHlLineStraight; - drawSkinPart = true; - } + if (const QStyleOptionViewItemV2 *vopt = qstyleoption_cast<const QStyleOptionViewItemV2 *>(option)) { + const bool rightLine = option->state & State_Item; + const bool downLine = option->state & State_Sibling; + const bool upLine = option->state & (State_Open | State_Children | State_Item | State_Sibling); + QS60StylePrivate::SkinElementFlags adjustedFlags = flags; - if (option->direction == Qt::RightToLeft) - adjustedFlags |= QS60StylePrivate::SF_Mirrored_X_Axis; - - if (drawSkinPart) - QS60StylePrivate::drawSkinPart(skinPart, painter, option->rect, adjustedFlags); - - if (option->state & State_Children) { - QS60StyleEnums::SkinParts skinPart = - (option->state & State_Open) ? QS60StyleEnums::SP_QgnIndiHlColSuper : QS60StyleEnums::SP_QgnIndiHlExpSuper; - const QRect selectionRect = subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget); - const int minDimension = qMin(option->rect.width(), option->rect.height()); - const int magicTweak = (option->direction == Qt::RightToLeft) ? -3 : 3; //@todo: magic - //The branch indicator icon in S60 is supposed to be superimposed on top of branch lines. - QRect iconRect(QPoint(option->rect.left() + magicTweak, selectionRect.top() + 1), QSize(minDimension, minDimension)); - if (!QS60StylePrivate::isTouchSupported()) - iconRect.translate(0, -4); //@todo: magic - QS60StylePrivate::drawSkinPart(skinPart, painter, iconRect, adjustedFlags); - } + QS60StyleEnums::SkinParts skinPart; + bool drawSkinPart = false; + if (rightLine && downLine && upLine) { + skinPart = QS60StyleEnums::SP_QgnIndiHlLineBranch; + drawSkinPart = true; + } else if (rightLine && upLine) { + skinPart = QS60StyleEnums::SP_QgnIndiHlLineEnd; + drawSkinPart = true; + } else if (upLine && downLine) { + skinPart = QS60StyleEnums::SP_QgnIndiHlLineStraight; + drawSkinPart = true; + } + + if (option->direction == Qt::RightToLeft) + adjustedFlags |= QS60StylePrivate::SF_Mirrored_X_Axis; + + if (drawSkinPart) + QS60StylePrivate::drawSkinPart(skinPart, painter, option->rect, adjustedFlags); + + if (option->state & State_Children) { + QS60StyleEnums::SkinParts skinPart = + (option->state & State_Open) ? QS60StyleEnums::SP_QgnIndiHlColSuper : QS60StyleEnums::SP_QgnIndiHlExpSuper; + const QRect selectionRect = subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget); + const int minDimension = qMin(option->rect.width(), option->rect.height()); + const int magicTweak = (option->direction == Qt::RightToLeft) ? -3 : 3; + //The branch indicator icon in S60 is supposed to be superimposed on top of branch lines. + QRect iconRect(QPoint(option->rect.left() + magicTweak, selectionRect.top() + 1), QSize(minDimension, minDimension)); + iconRect.translate(0, -4); + QS60StylePrivate::drawSkinPart(skinPart, painter, iconRect, adjustedFlags); } } +#else + QCommonStyle::drawPrimitive(element, option, painter, widget); +#endif break; case PE_PanelItemViewRow: // ### Qt 5: remove #ifndef QT_NO_ITEMVIEWS @@ -2506,10 +2455,7 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti case PE_PanelScrollAreaCorner: break; case PE_IndicatorItemViewItemDrop: - if (QS60StylePrivate::isTouchSupported()) - QS60StylePrivate::drawSkinElement(QS60StylePrivate::SE_DropArea, painter, option->rect, flags); - else - commonStyleDraws = true; + QS60StylePrivate::drawSkinElement(QS60StylePrivate::SE_DropArea, painter, option->rect, flags); break; // todo: items are below with #ifdefs "just in case". in final version, remove all non-required cases case PE_FrameLineEdit: @@ -2685,13 +2631,11 @@ QSize QS60Style::sizeFromContents(ContentsType ct, const QStyleOption *opt, } } sz = QCommonStyle::sizeFromContents( ct, opt, csz, widget); - if (QS60StylePrivate::isTouchSupported()) { - //Make itemview easier to use in touch devices - sz.setHeight(sz.height() + 2 * pixelMetric(PM_FocusFrameVMargin)); - //QCommonStyle does not adjust height with horizontal margin, it only adjusts width - if (ct == CT_MenuItem) - sz.setHeight(sz.height() - 8); //QCommonstyle adds 8 to height that this style handles through PM values - } + //Make itemview easier to use in touch devices + sz.setHeight(sz.height() + 2 * pixelMetric(PM_FocusFrameVMargin)); + //QCommonStyle does not adjust height with horizontal margin, it only adjusts width + if (ct == CT_MenuItem) + sz.setHeight(sz.height() - 8); //QCommonstyle adds 8 to height that this style handles through PM values break; #ifndef QT_NO_COMBOBOX case CT_ComboBox: { @@ -3325,8 +3269,7 @@ void QS60Style::polish(QApplication *application) QCommonStyle::polish(qApp); d->m_originalPalette = application->palette(); d->setThemePalette(application); - if (QS60StylePrivate::isTouchSupported()) - qApp->installEventFilter(this); + qApp->installEventFilter(this); } /*! @@ -3341,8 +3284,7 @@ void QS60Style::unpolish(QApplication *application) const QPalette newPalette = QApplication::style()->standardPalette(); QApplication::setPalette(newPalette); QApplicationPrivate::setSystemPalette(d->m_originalPalette); - if (QS60StylePrivate::isTouchSupported()) - qApp->removeEventFilter(this); + qApp->removeEventFilter(this); } /*! @@ -3353,11 +3295,10 @@ bool QS60Style::event(QEvent *e) #ifdef QT_KEYPAD_NAVIGATION Q_D(QS60Style); const QEvent::Type eventType = e->type(); - if ((eventType == QEvent::FocusIn || - eventType == QEvent::FocusOut || - eventType == QEvent::EnterEditFocus || - eventType == QEvent::LeaveEditFocus) && - QS60StylePrivate::isTouchSupported()) + if (eventType == QEvent::FocusIn + || eventType == QEvent::FocusOut + || eventType == QEvent::EnterEditFocus + || eventType == QEvent::LeaveEditFocus) return false; #endif diff --git a/src/gui/styles/qs60style_feedbackinterface_p.h b/src/gui/styles/qs60style_feedbackinterface_p.h new file mode 100644 index 0000000..0dc46b8 --- /dev/null +++ b/src/gui/styles/qs60style_feedbackinterface_p.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QObject> + +class TactileFeedbackInterface : public QObject +{ + public: + virtual void touchFeedback(QEvent *event, const QWidget *widget) = 0; +}; + +Q_DECLARE_INTERFACE(TactileFeedbackInterface, "com.trolltech.Qt.TactileFeedbackInterface/1.0") diff --git a/src/gui/styles/qs60style_p.h b/src/gui/styles/qs60style_p.h index 10a43e2..2fa8c7f 100644 --- a/src/gui/styles/qs60style_p.h +++ b/src/gui/styles/qs60style_p.h @@ -614,9 +614,6 @@ public: void setBackgroundTexture(QApplication *application) const; static void deleteBackground(); - static bool isTouchSupported(); - static bool isToolBarBackground(); - static bool hasSliderGrooveGraphic(); static bool isSingleClickUi(); static bool isWidgetPressed(const QWidget *widget); diff --git a/src/gui/styles/qs60style_s60.cpp b/src/gui/styles/qs60style_s60.cpp index 2051362..33619d6 100644 --- a/src/gui/styles/qs60style_s60.cpp +++ b/src/gui/styles/qs60style_s60.cpp @@ -683,7 +683,6 @@ QPixmap QS60StyleModeSpecifics::colorSkinnedGraphicsLX( const TAknsItemID skinId = m_partMap[stylepartIndex].skinID; TInt fallbackGraphicID = -1; - HBufC* iconFile = HBufC::NewLC( KMaxFileName ); fallbackInfo(stylepart, fallbackGraphicID); TAknsItemID colorGroup = KAknsIIDQsnIconColors; @@ -717,7 +716,7 @@ QPixmap QS60StyleModeSpecifics::colorSkinnedGraphicsLX( defaultColor); QPixmap result = fromFbsBitmap(icon, iconMask, flags, targetSize); - CleanupStack::PopAndDestroy(3); //icon, iconMask, iconFile + CleanupStack::PopAndDestroy(2); //icon, iconMask return result; } @@ -797,21 +796,6 @@ QPixmap QS60StyleModeSpecifics::fromFbsBitmap(CFbsBitmap *icon, CFbsBitmap *mask return pixmap; } -bool QS60StylePrivate::isTouchSupported() -{ - return bool(AknLayoutUtils::PenEnabled()); -} - -bool QS60StylePrivate::isToolBarBackground() -{ - return (QSysInfo::s60Version() == QSysInfo::SV_S60_3_1 || QSysInfo::s60Version() == QSysInfo::SV_S60_3_2); -} - -bool QS60StylePrivate::hasSliderGrooveGraphic() -{ - return QSysInfo::s60Version() != QSysInfo::SV_S60_3_1; -} - bool QS60StylePrivate::isSingleClickUi() { return (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0); @@ -890,16 +874,8 @@ QPoint qt_s60_fill_background_offset(const QWidget *targetWidget) { CCoeControl *control = targetWidget->effectiveWinId(); TPoint pos(0,0); - if (control) { - // FIXME properly: S60 3.1 has a bug that CCoeControl::PositionRelativeToScreen sometimes - // freezes the device, possibly in cases where we run out of memory. - // We use CCoeControl::Position instead in S60 3.1, which returns same values - // in most cases. - if (QSysInfo::s60Version() == QSysInfo::SV_S60_3_1) - pos = control->Position(); - else - pos = control->PositionRelativeToScreen(); - } + if (control) + pos = control->PositionRelativeToScreen(); return QPoint(pos.iX, pos.iY); } @@ -1180,20 +1156,8 @@ void QS60StyleModeSpecifics::frameIdAndCenterId(QS60StylePrivate::SkinFrameEleme switch(frameElement) { case QS60StylePrivate::SF_ToolTip: - if (QSysInfo::s60Version() != QSysInfo::SV_S60_3_1) { - centerId.Set(EAknsMajorGeneric, 0x19c2); - frameId.Set(EAknsMajorSkin, 0x5300); - } else { - centerId.Set(KAknsIIDQsnFrPopupCenter); - frameId.iMinor = centerId.iMinor - 9; - } - break; - case QS60StylePrivate::SF_ToolBar: - if (QSysInfo::s60Version() == QSysInfo::SV_S60_3_1 || - QSysInfo::s60Version() == QSysInfo::SV_S60_3_2) { - centerId.Set(KAknsIIDQsnFrPopupCenterSubmenu); - frameId.Set(KAknsIIDQsnFrPopupSub); - } + centerId.Set(EAknsMajorGeneric, 0x19c2); + frameId.Set(EAknsMajorSkin, 0x5300); break; case QS60StylePrivate::SF_PopupBackground: centerId.Set(KAknsIIDQsnFrPopupCenterSubmenu); @@ -1348,10 +1312,7 @@ void QS60StylePrivate::setActiveLayout() //not found, lets try with either of dimensions if (activeLayoutIndex==-1){ - const QSysInfo::S60Version currentRelease = QSysInfo::s60Version(); const bool landscape = screenHeight < screenWidth; - - activeLayoutIndex = (currentRelease == QSysInfo::SV_S60_3_1 || currentRelease == QSysInfo::SV_S60_3_2) ? 0 : 2; activeLayoutIndex += (!landscape) ? 1 : 0; } @@ -1407,9 +1368,7 @@ bool QS60StyleModeSpecifics::disabledPartGraphic(QS60StyleEnums::SkinParts &part case QS60StyleEnums::SP_QsnFrButtonSideLInactive: case QS60StyleEnums::SP_QsnFrButtonSideRInactive: case QS60StyleEnums::SP_QsnFrButtonCenterInactive: - if (!(QSysInfo::s60Version()==QSysInfo::SV_S60_3_1 || - QSysInfo::s60Version()==QSysInfo::SV_S60_3_2)) - disabledGraphic = true; + disabledGraphic = true; break; default: break; @@ -1425,9 +1384,7 @@ bool QS60StyleModeSpecifics::disabledFrameGraphic(QS60StylePrivate::SkinFrameEle switch(frame){ // inactive button graphics are available from 5.0 onwards case QS60StylePrivate::SF_ButtonInactive: - if (!(QSysInfo::s60Version()==QSysInfo::SV_S60_3_1 || - QSysInfo::s60Version()==QSysInfo::SV_S60_3_2)) - disabledGraphic = true; + disabledGraphic = true; break; default: break; @@ -1438,9 +1395,6 @@ bool QS60StyleModeSpecifics::disabledFrameGraphic(QS60StylePrivate::SkinFrameEle QPixmap QS60StyleModeSpecifics::generateMissingThemeGraphic(QS60StyleEnums::SkinParts &part, const QSize &size, QS60StylePrivate::SkinElementFlags flags) { - if (!QS60StylePrivate::isTouchSupported()) - return QPixmap(); - QS60StyleEnums::SkinParts updatedPart = part; switch(part){ // AVKON UI has a abnormal handling for scrollbar graphics. It is possible that the root @@ -1680,7 +1634,7 @@ QVariant QS60StyleModeSpecifics::themeDefinition( //Animation definitions case QS60StyleEnums::TD_AnimationData: { - CAknsBmpAnimItemData *animationData; + CAknsBmpAnimItemData *animationData = 0; TAknsItemID animationSkinId = partSpecificThemeId(part); QList<QVariant> list; @@ -1696,9 +1650,6 @@ QVariant QS60StyleModeSpecifics::themeDefinition( QS60StyleEnums::AnimationMode playMode; switch(animationData->PlayMode()) { - case CBitmapAnimClientData::EPlay: - playMode = QS60StyleEnums::AM_PlayOnce; - break; case CBitmapAnimClientData::ECycle: playMode = QS60StyleEnums::AM_Looping; break; @@ -1706,6 +1657,7 @@ QVariant QS60StyleModeSpecifics::themeDefinition( playMode = QS60StyleEnums::AM_Bounce; break; default: + playMode = QS60StyleEnums::AM_PlayOnce; break; } list.append(QVariant((int)playMode)); diff --git a/src/gui/styles/qs60style_simulated.cpp b/src/gui/styles/qs60style_simulated.cpp index ca02cdf..d0789a8 100644 --- a/src/gui/styles/qs60style_simulated.cpp +++ b/src/gui/styles/qs60style_simulated.cpp @@ -318,25 +318,6 @@ QPixmap QS60StylePrivate::backgroundTexture(bool /*skipCreation*/) return *m_background; } -bool QS60StylePrivate::isTouchSupported() -{ -#ifdef QT_KEYPAD_NAVIGATION - return !QApplication::keypadNavigationEnabled(); -#else - return true; -#endif -} - -bool QS60StylePrivate::isToolBarBackground() -{ - return true; -} - -bool QS60StylePrivate::hasSliderGrooveGraphic() -{ - return false; -} - bool QS60StylePrivate::isSingleClickUi() { return false; diff --git a/src/gui/styles/qstylehelper_p.h b/src/gui/styles/qstylehelper_p.h index 6585ab6..a5959e7 100644 --- a/src/gui/styles/qstylehelper_p.h +++ b/src/gui/styles/qstylehelper_p.h @@ -110,6 +110,7 @@ template <typename T> enum { ExactSize = true }; static int size(const HexString<T> &) { return sizeof(T) * 2; } static inline void appendTo(const HexString<T> &str, QChar *&out) { str.write(out); } + typedef QString ConvertTo; }; QT_END_NAMESPACE diff --git a/src/gui/styles/qstylesheetstyle.cpp b/src/gui/styles/qstylesheetstyle.cpp index 1efe339..7d2ed2a 100644 --- a/src/gui/styles/qstylesheetstyle.cpp +++ b/src/gui/styles/qstylesheetstyle.cpp @@ -99,14 +99,7 @@ public: }; -static QHash<const QWidget *, QVector<StyleRule> > *styleRulesCache = 0; -static QHash<const QWidget *, QHash<int, bool> > *hasStyleRuleCache = 0; -typedef QHash<int, QHash<quint64, QRenderRule> > QRenderRules; -static QHash<const QWidget *, QRenderRules> *renderRulesCache = 0; -static QHash<const QWidget *, QPalette> *customPaletteWidgets = 0; // widgets whose palette we tampered -static QHash<const void *, StyleSheet> *styleSheetCache = 0; // parsed style sheets -static QSet<const QWidget *> *autoFillDisabledWidgets = 0; - +static QStyleSheetStyleCaches *styleSheetCaches = 0; /* RECURSION_GUARD: * the QStyleSheetStyle is a proxy. If used with others proxy style, we may end up with something like: @@ -1525,8 +1518,8 @@ private: QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QWidget *w) const { - QHash<const QWidget *, QVector<StyleRule> >::const_iterator cacheIt = styleRulesCache->constFind(w); - if (cacheIt != styleRulesCache->constEnd()) + QHash<const QWidget *, QVector<StyleRule> >::const_iterator cacheIt = styleSheetCaches->styleRulesCache.constFind(w); + if (cacheIt != styleSheetCaches->styleRulesCache.constEnd()) return cacheIt.value(); if (!initWidget(w)) { @@ -1536,12 +1529,12 @@ QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QWidget *w) const QStyleSheetStyleSelector styleSelector; StyleSheet defaultSs; - QHash<const void *, StyleSheet>::const_iterator defaultCacheIt = styleSheetCache->constFind(baseStyle()); - if (defaultCacheIt == styleSheetCache->constEnd()) { + QHash<const void *, StyleSheet>::const_iterator defaultCacheIt = styleSheetCaches->styleSheetCache.constFind(baseStyle()); + if (defaultCacheIt == styleSheetCaches->styleSheetCache.constEnd()) { defaultSs = getDefaultStyleSheet(); QStyle *bs = baseStyle(); - styleSheetCache->insert(bs, defaultSs); - QObject::connect(bs, SIGNAL(destroyed(QObject*)), this, SLOT(styleDestroyed(QObject*)), Qt::UniqueConnection); + styleSheetCaches->styleSheetCache.insert(bs, defaultSs); + QObject::connect(bs, SIGNAL(destroyed(QObject*)), styleSheetCaches, SLOT(styleDestroyed(QObject*)), Qt::UniqueConnection); } else { defaultSs = defaultCacheIt.value(); } @@ -1549,8 +1542,8 @@ QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QWidget *w) const if (!qApp->styleSheet().isEmpty()) { StyleSheet appSs; - QHash<const void *, StyleSheet>::const_iterator appCacheIt = styleSheetCache->constFind(qApp); - if (appCacheIt == styleSheetCache->constEnd()) { + QHash<const void *, StyleSheet>::const_iterator appCacheIt = styleSheetCaches->styleSheetCache.constFind(qApp); + if (appCacheIt == styleSheetCaches->styleSheetCache.constEnd()) { QString ss = qApp->styleSheet(); if (ss.startsWith(QLatin1String("file:///"))) ss.remove(0, 8); @@ -1559,7 +1552,7 @@ QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QWidget *w) const qWarning("Could not parse application stylesheet"); appSs.origin = StyleSheetOrigin_Inline; appSs.depth = 1; - styleSheetCache->insert(qApp, appSs); + styleSheetCaches->styleSheetCache.insert(qApp, appSs); } else { appSs = appCacheIt.value(); } @@ -1571,8 +1564,8 @@ QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QWidget *w) const if (wid->styleSheet().isEmpty()) continue; StyleSheet ss; - QHash<const void *, StyleSheet>::const_iterator widCacheIt = styleSheetCache->constFind(wid); - if (widCacheIt == styleSheetCache->constEnd()) { + QHash<const void *, StyleSheet>::const_iterator widCacheIt = styleSheetCaches->styleSheetCache.constFind(wid); + if (widCacheIt == styleSheetCaches->styleSheetCache.constEnd()) { parser.init(wid->styleSheet()); if (!parser.parse(&ss)) { parser.init(QLatin1String("* {") + wid->styleSheet() + QLatin1Char('}')); @@ -1580,7 +1573,7 @@ QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QWidget *w) const qWarning("Could not parse stylesheet of widget %p", wid); } ss.origin = StyleSheetOrigin_Inline; - styleSheetCache->insert(wid, ss); + styleSheetCaches->styleSheetCache.insert(wid, ss); } else { ss = widCacheIt.value(); } @@ -1595,7 +1588,7 @@ QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QWidget *w) const StyleSelector::NodePtr n; n.ptr = (void *)w; QVector<QCss::StyleRule> rules = styleSelector.styleRulesForNode(n); - styleRulesCache->insert(w, rules); + styleSheetCaches->styleRulesCache.insert(w, rules); return rules; } @@ -1724,7 +1717,7 @@ static void qt_check_if_internal_widget(const QWidget **w, int *element) QRenderRule QStyleSheetStyle::renderRule(const QWidget *w, int element, quint64 state) const { qt_check_if_internal_widget(&w, &element); - QHash<quint64, QRenderRule> &cache = (*renderRulesCache)[w][element]; + QHash<quint64, QRenderRule> &cache = styleSheetCaches->renderRulesCache[w][element]; QHash<quint64, QRenderRule>::const_iterator cacheIt = cache.constFind(state); if (cacheIt != cache.constEnd()) return cacheIt.value(); @@ -2035,7 +2028,7 @@ QRenderRule QStyleSheetStyle::renderRule(const QWidget *w, const QStyleOption *o bool QStyleSheetStyle::hasStyleRule(const QWidget *w, int part) const { - QHash<int, bool> &cache = (*hasStyleRuleCache)[w]; + QHash<int, bool> &cache = styleSheetCaches->hasStyleRuleCache[w]; QHash<int, bool>::const_iterator cacheIt = cache.constFind(part); if (cacheIt != cache.constEnd()) return cacheIt.value(); @@ -2342,7 +2335,7 @@ static QWidget *embeddedWidget(QWidget *w) #ifndef QT_NO_SPINBOX if (QAbstractSpinBox *sb = qobject_cast<QAbstractSpinBox *>(w)) - return qFindChild<QLineEdit *>(sb); + return sb->findChild<QLineEdit *>(); #endif #ifndef QT_NO_SCROLLAREA @@ -2565,7 +2558,7 @@ void QStyleSheetStyle::setPalette(QWidget *w) rule.configurePalette(&p, map[i].group, ew, ew != w); } - customPaletteWidgets->insert(w, w->palette()); + styleSheetCaches->customPaletteWidgets.insert(w, w->palette()); w->setPalette(p); if (ew != w) ew->setPalette(p); @@ -2573,32 +2566,32 @@ void QStyleSheetStyle::setPalette(QWidget *w) void QStyleSheetStyle::unsetPalette(QWidget *w) { - if (customPaletteWidgets->contains(w)) { - QPalette p = customPaletteWidgets->value(w); + if (styleSheetCaches->customPaletteWidgets.contains(w)) { + QPalette p = styleSheetCaches->customPaletteWidgets.value(w); w->setPalette(p); QWidget *ew = embeddedWidget(w); if (ew != w) ew->setPalette(p); - customPaletteWidgets->remove(w); + styleSheetCaches->customPaletteWidgets.remove(w); } QVariant oldFont = w->property("_q_styleSheetWidgetFont"); if (oldFont.isValid()) { - w->setFont(qVariantValue<QFont>(oldFont)); + w->setFont(qvariant_cast<QFont>(oldFont)); } - if (autoFillDisabledWidgets->contains(w)) { + if (styleSheetCaches->autoFillDisabledWidgets.contains(w)) { embeddedWidget(w)->setAutoFillBackground(true); - autoFillDisabledWidgets->remove(w); + styleSheetCaches->autoFillDisabledWidgets.remove(w); } } static void updateWidgets(const QList<const QWidget *>& widgets) { - if (!styleRulesCache->isEmpty() || !hasStyleRuleCache->isEmpty() || !renderRulesCache->isEmpty()) { + if (!styleSheetCaches->styleRulesCache.isEmpty() || !styleSheetCaches->hasStyleRuleCache.isEmpty() || !styleSheetCaches->renderRulesCache.isEmpty()) { for (int i = 0; i < widgets.size(); ++i) { const QWidget *widget = widgets.at(i); - styleRulesCache->remove(widget); - hasStyleRuleCache->remove(widget); - renderRulesCache->remove(widget); + styleSheetCaches->styleRulesCache.remove(widget); + styleSheetCaches->hasStyleRuleCache.remove(widget); + styleSheetCaches->renderRulesCache.remove(widget); } } for (int i = 0; i < widgets.size(); ++i) { @@ -2622,12 +2615,7 @@ QStyleSheetStyle::QStyleSheetStyle(QStyle *base) { ++numinstances; if (numinstances == 1) { - styleRulesCache = new QHash<const QWidget *, QVector<StyleRule> >; - hasStyleRuleCache = new QHash<const QWidget *, QHash<int, bool> >; - renderRulesCache = new QHash<const QWidget *, QRenderRules>; - customPaletteWidgets = new QHash<const QWidget *, QPalette>; - styleSheetCache = new QHash<const void *, StyleSheet>; - autoFillDisabledWidgets = new QSet<const QWidget *>; + styleSheetCaches = new QStyleSheetStyleCaches; } } @@ -2635,18 +2623,7 @@ QStyleSheetStyle::~QStyleSheetStyle() { --numinstances; if (numinstances == 0) { - delete styleRulesCache; - styleRulesCache = 0; - delete hasStyleRuleCache; - hasStyleRuleCache = 0; - delete renderRulesCache; - renderRulesCache = 0; - delete customPaletteWidgets; - customPaletteWidgets = 0; - delete styleSheetCache; - styleSheetCache = 0; - delete autoFillDisabledWidgets; - autoFillDisabledWidgets = 0; + delete styleSheetCaches; } } QStyle *QStyleSheetStyle::baseStyle() const @@ -2658,19 +2635,19 @@ QStyle *QStyleSheetStyle::baseStyle() const return QApplication::style(); } -void QStyleSheetStyle::widgetDestroyed(QObject *o) +void QStyleSheetStyleCaches::widgetDestroyed(QObject *o) { - styleRulesCache->remove((const QWidget *)o); - hasStyleRuleCache->remove((const QWidget *)o); - renderRulesCache->remove((const QWidget *)o); - customPaletteWidgets->remove((const QWidget *)o); - styleSheetCache->remove((const QWidget *)o); - autoFillDisabledWidgets->remove((const QWidget *)o); + styleRulesCache.remove((const QWidget *)o); + hasStyleRuleCache.remove((const QWidget *)o); + renderRulesCache.remove((const QWidget *)o); + customPaletteWidgets.remove((const QWidget *)o); + styleSheetCache.remove((const QWidget *)o); + autoFillDisabledWidgets.remove((const QWidget *)o); } -void QStyleSheetStyle::styleDestroyed(QObject *o) +void QStyleSheetStyleCaches::styleDestroyed(QObject *o) { - styleSheetCache->remove(o); + styleSheetCache.remove(o); } /*! @@ -2688,7 +2665,7 @@ bool QStyleSheetStyle::initWidget(const QWidget *w) const return false; const_cast<QWidget *>(w)->setAttribute(Qt::WA_StyleSheet, true); - QObject::connect(w, SIGNAL(destroyed(QObject*)), this, SLOT(widgetDestroyed(QObject*))); + QObject::connect(w, SIGNAL(destroyed(QObject*)), styleSheetCaches, SLOT(widgetDestroyed(QObject*)), Qt::UniqueConnection); return true; } @@ -2700,12 +2677,12 @@ void QStyleSheetStyle::polish(QWidget *w) if (!initWidget(w)) return; - if (styleRulesCache->contains(w)) { + if (styleSheetCaches->styleRulesCache.contains(w)) { // the widget accessed its style pointer before polish (or repolish) // (exemple: the QAbstractSpinBox constructor ask for the stylehint) - styleRulesCache->remove(w); - hasStyleRuleCache->remove(w); - renderRulesCache->remove(w); + styleSheetCaches->styleRulesCache.remove(w); + styleSheetCaches->hasStyleRuleCache.remove(w); + styleSheetCaches->renderRulesCache.remove(w); } setGeometry(w); setProperties(w); @@ -2771,7 +2748,7 @@ void QStyleSheetStyle::polish(QWidget *w) QWidget *ew = embeddedWidget(w); if (ew->autoFillBackground()) { ew->setAutoFillBackground(false); - autoFillDisabledWidgets->insert(w); + styleSheetCaches->autoFillDisabledWidgets.insert(w); if (ew != w) { //eg. viewport of a scrollarea //(in order to draw the background anyway in case we don't.) ew->setAttribute(Qt::WA_StyledBackground, true); @@ -2795,20 +2772,20 @@ void QStyleSheetStyle::polish(QPalette &pal) void QStyleSheetStyle::repolish(QWidget *w) { - QList<const QWidget *> children = qFindChildren<const QWidget *>(w, QString()); + QList<const QWidget *> children = w->findChildren<const QWidget *>(QString()); children.append(w); - styleSheetCache->remove(w); + styleSheetCaches->styleSheetCache.remove(w); updateWidgets(children); } void QStyleSheetStyle::repolish(QApplication *app) { Q_UNUSED(app); - const QList<const QWidget*> allWidgets = styleRulesCache->keys(); - styleSheetCache->remove(qApp); - styleRulesCache->clear(); - hasStyleRuleCache->clear(); - renderRulesCache->clear(); + const QList<const QWidget*> allWidgets = styleSheetCaches->styleRulesCache.keys(); + styleSheetCaches->styleSheetCache.remove(qApp); + styleSheetCaches->styleRulesCache.clear(); + styleSheetCaches->hasStyleRuleCache.clear(); + styleSheetCaches->renderRulesCache.clear(); updateWidgets(allWidgets); } @@ -2819,10 +2796,10 @@ void QStyleSheetStyle::unpolish(QWidget *w) return; } - styleRulesCache->remove(w); - hasStyleRuleCache->remove(w); - renderRulesCache->remove(w); - styleSheetCache->remove(w); + styleSheetCaches->styleRulesCache.remove(w); + styleSheetCaches->hasStyleRuleCache.remove(w); + styleSheetCaches->renderRulesCache.remove(w); + styleSheetCaches->styleSheetCache.remove(w); unsetPalette(w); w->setProperty("_q_stylesheet_minw", QVariant()); w->setProperty("_q_stylesheet_minh", QVariant()); @@ -2849,10 +2826,10 @@ void QStyleSheetStyle::unpolish(QApplication *app) { baseStyle()->unpolish(app); RECURSION_GUARD(return) - styleRulesCache->clear(); - hasStyleRuleCache->clear(); - renderRulesCache->clear(); - styleSheetCache->remove(qApp); + styleSheetCaches->styleRulesCache.clear(); + styleSheetCaches->hasStyleRuleCache.clear(); + styleSheetCaches->renderRulesCache.clear(); + styleSheetCaches->styleSheetCache.remove(qApp); } #ifndef QT_NO_TABBAR @@ -3048,6 +3025,13 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC titleRule.configurePalette(&pal, QPalette::WindowText, QPalette::Window); drawItemText(p, labelRect, alignment, pal, gb->state & State_Enabled, gb->text, QPalette::WindowText); + + if (gb->state & State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*gb); + fropt.rect = labelRect; + drawPrimitive(PE_FrameFocusRect, &fropt, p, w); + } } return; @@ -3724,7 +3708,9 @@ void QStyleSheetStyle::drawControl(ControlElement ce, const QStyleOption *opt, Q editRect.translate(cb->iconSize.width() + spacing, 0); } if (!cb->currentText.isEmpty() && !cb->editable) { - drawItemText(p, editRect.adjusted(0, 0, 0, 0), Qt::AlignLeft | Qt::AlignVCenter, cb->palette, + QPalette styledPalette(cb->palette); + rule.configurePalette(&styledPalette, QPalette::Text, QPalette::Base); + drawItemText(p, editRect.adjusted(0, 0, 0, 0), Qt::AlignLeft | Qt::AlignVCenter, styledPalette, cb->state & State_Enabled, cb->currentText, QPalette::Text); } p->restore(); @@ -4157,6 +4143,10 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op pseudoElement = PseudoElement_DownArrow; break; + case PE_IndicatorArrowUp: + pseudoElement = PseudoElement_UpArrow; + break; + case PE_IndicatorRadioButton: pseudoElement = PseudoElement_ExclusiveIndicator; break; @@ -4257,7 +4247,7 @@ void QStyleSheetStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *op case PE_Widget: if (w && !rule.hasDrawable()) { QWidget *container = containerWidget(w); - if (autoFillDisabledWidgets->contains(container) + if (styleSheetCaches->autoFillDisabledWidgets.contains(container) && (container == w || !renderRule(container, opt).hasBackground())) { //we do not have a background, but we disabled the autofillbackground anyway. so fill the background now. // (this may happen if we have rules like :focus) @@ -5081,7 +5071,7 @@ QIcon QStyleSheetStyle::standardIconImplementation(StandardPixmap standardIcon, if (!s.isEmpty()) { QRenderRule rule = renderRule(w, opt); if (rule.hasStyleHint(s)) - return qVariantValue<QIcon>(rule.styleHint(s)); + return qvariant_cast<QIcon>(rule.styleHint(s)); } return baseStyle()->standardIcon(standardIcon, opt, w); } @@ -5099,7 +5089,7 @@ QPixmap QStyleSheetStyle::standardPixmap(StandardPixmap standardPixmap, const QS if (!s.isEmpty()) { QRenderRule rule = renderRule(w, opt); if (rule.hasStyleHint(s)) { - QIcon icon = qVariantValue<QIcon>(rule.styleHint(s)); + QIcon icon = qvariant_cast<QIcon>(rule.styleHint(s)); return icon.pixmap(16, 16); // ###: unhard-code this if someone complains } } @@ -5193,7 +5183,7 @@ int QStyleSheetStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWi case SH_ComboBox_PopupFrameStyle: #ifndef QT_NO_COMBOBOX if (qobject_cast<const QComboBox *>(w)) { - QAbstractItemView *view = qFindChild<QAbstractItemView *>(w); + QAbstractItemView *view = w->findChild<QAbstractItemView *>(); if (view) { view->ensurePolished(); QRenderRule subRule = renderRule(view, PseudoElement_None); diff --git a/src/gui/styles/qstylesheetstyle_default.cpp b/src/gui/styles/qstylesheetstyle_default.cpp index 8b4271e..c9cfc43 100644 --- a/src/gui/styles/qstylesheetstyle_default.cpp +++ b/src/gui/styles/qstylesheetstyle_default.cpp @@ -151,7 +151,7 @@ StyleSheet QStyleSheetStyle::getDefaultStyleSheet() const BasicSelector bSelector; Selector selector; Declaration decl; - Value value; + QCss::Value value; Pseudo pseudo; AttributeSelector attr; diff --git a/src/gui/styles/qstylesheetstyle_p.h b/src/gui/styles/qstylesheetstyle_p.h index c992314..e597ba1 100644 --- a/src/gui/styles/qstylesheetstyle_p.h +++ b/src/gui/styles/qstylesheetstyle_p.h @@ -145,10 +145,6 @@ protected Q_SLOTS: protected: bool event(QEvent *e); -private Q_SLOTS: - void widgetDestroyed(QObject *); - void styleDestroyed(QObject *); - private: int refcount; @@ -186,6 +182,22 @@ private: Q_DECLARE_PRIVATE(QStyleSheetStyle) }; +class QStyleSheetStyleCaches : public QObject +{ + Q_OBJECT +public Q_SLOTS: + void widgetDestroyed(QObject *); + void styleDestroyed(QObject *); +public: + QHash<const QWidget *, QVector<QCss::StyleRule> > styleRulesCache; + QHash<const QWidget *, QHash<int, bool> > hasStyleRuleCache; + typedef QHash<int, QHash<quint64, QRenderRule> > QRenderRules; + QHash<const QWidget *, QRenderRules> renderRulesCache; + QHash<const QWidget *, QPalette> customPaletteWidgets; // widgets whose palette we tampered + QHash<const void *, QCss::StyleSheet> styleSheetCache; // parsed style sheets + QSet<const QWidget *> autoFillDisabledWidgets; +}; + QT_END_NAMESPACE #endif // QT_NO_STYLE_STYLESHEET diff --git a/src/gui/styles/qwindowscestyle.cpp b/src/gui/styles/qwindowscestyle.cpp index 6c75b3b..39174f9 100644 --- a/src/gui/styles/qwindowscestyle.cpp +++ b/src/gui/styles/qwindowscestyle.cpp @@ -2294,6 +2294,7 @@ int QWindowsCEStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QW break; case SH_EtchDisabledText: ret = false; + break; case SH_RequestSoftwareInputPanel: ret = RSIP_OnMouseClick; break; diff --git a/src/gui/styles/qwindowsstyle.cpp b/src/gui/styles/qwindowsstyle.cpp index 915cba4..9732c7e 100644 --- a/src/gui/styles/qwindowsstyle.cpp +++ b/src/gui/styles/qwindowsstyle.cpp @@ -176,12 +176,15 @@ bool QWindowsStyle::eventFilter(QObject *o, QEvent *e) widget = widget->window(); // Alt has been pressed - find all widgets that care - QList<QWidget *> l = qFindChildren<QWidget *>(widget); - for (int pos=0 ; pos < l.size() ; ++pos) { + QList<QWidget *> l = widget->findChildren<QWidget *>(); + for (int pos=0 ; pos < l.size() ;) { QWidget *w = l.at(pos); if (w->isWindow() || !w->isVisible() || - w->style()->styleHint(SH_UnderlineShortcut, 0, w)) + w->style()->styleHint(SH_UnderlineShortcut, 0, w)) { l.removeAt(pos); + continue; + } + pos++; } // Update states before repainting d->seenAlt.append(widget); @@ -199,7 +202,7 @@ bool QWindowsStyle::eventFilter(QObject *o, QEvent *e) // Update state and repaint the menu bars. d->alt_down = false; #ifndef QT_NO_MENUBAR - QList<QMenuBar *> l = qFindChildren<QMenuBar *>(widget); + QList<QMenuBar *> l = widget->findChildren<QMenuBar *>(); for (int i = 0; i < l.size(); ++i) l.at(i)->update(); #endif @@ -1161,7 +1164,7 @@ int QWindowsStyle::styleHint(StyleHint hint, const QStyleOption *opt, const QWid if (!menuBar && qobject_cast<const QMenu *>(widget)) { QWidget *w = QApplication::activeWindow(); if (w && w != widget) - menuBar = qFindChild<QMenuBar *>(w); + menuBar = w->findChild<QMenuBar *>(); } // If we paint a menu bar draw underlines if is in the keyboardState if (menuBar) { @@ -1858,8 +1861,8 @@ void QWindowsStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPai } QRect vCheckRect = visualRect(opt->direction, menuitem->rect, QRect(menuitem->rect.x(), menuitem->rect.y(), checkcol, menuitem->rect.height())); - if (checked) { - if (act && !dis) { + if (!menuitem->icon.isNull() && checked) { + if (act) { qDrawShadePanel(p, vCheckRect, menuitem->palette, true, 1, &menuitem->palette.brush(QPalette::Button)); @@ -2028,10 +2031,8 @@ void QWindowsStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPai && tabBarAlignment == Qt::AlignLeft); QColor light = tab->palette.light().color(); - QColor midlight = tab->palette.midlight().color(); QColor dark = tab->palette.dark().color(); QColor shadow = tab->palette.shadow().color(); - QColor background = tab->palette.background().color(); int borderThinkness = proxy()->pixelMetric(PM_TabBarBaseOverlap, tab, widget); if (selected) borderThinkness /= 2; diff --git a/src/gui/styles/qwindowsvistastyle.cpp b/src/gui/styles/qwindowsvistastyle.cpp index d882bf1..b894eb4 100644 --- a/src/gui/styles/qwindowsvistastyle.cpp +++ b/src/gui/styles/qwindowsvistastyle.cpp @@ -76,6 +76,10 @@ static const int windowsRightBorder = 15; // right border on windows # define CMDLGS_PRESSED 3 # define CMDLGS_DISABLED 4 #endif +#ifndef PP_TRANSPARENTBAR +# define PP_TRANSPARENTBAR 11 +# define PP_TRANSPARENTBARVERT 12 +#endif // Runtime resolved theme engine function calls @@ -588,10 +592,6 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt if (QAbstractSpinBox *spinbox = qobject_cast<QAbstractSpinBox*>(widget->parentWidget())) resolve_mask = spinbox->palette().resolve(); #endif // QT_NO_SPINBOX -#ifndef QT_NO_COMBOBOX - if (QComboBox *combobox = qobject_cast<QComboBox*>(widget->parentWidget())) - resolve_mask = combobox->palette().resolve(); -#endif // QT_NO_COMBOBOX } if (resolve_mask & (1 << QPalette::Base)) { // Base color is set for this widget, so use it @@ -842,10 +842,10 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt const QDialogButtonBox *buttonBox = 0; if (qobject_cast<const QMessageBox *> (widget)) - buttonBox = qFindChild<const QDialogButtonBox *>(widget,QLatin1String("qt_msgbox_buttonbox")); + buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); #ifndef QT_NO_INPUTDIALOG else if (qobject_cast<const QInputDialog *> (widget)) - buttonBox = qFindChild<const QDialogButtonBox *>(widget,QLatin1String("qt_inputdlg_buttonbox")); + buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); #endif // QT_NO_INPUTDIALOG if (buttonBox) { @@ -973,7 +973,8 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { - if (QWindowsVistaAnimation *anim = d->widgetAnimation(widget)) { + QWindowsVistaAnimation *anim = d->widgetAnimation(widget); + if (anim && (btn->state & State_Enabled)) { anim->paint(painter, option); } else { name = QLatin1String("BUTTON"); @@ -1000,7 +1001,6 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption !(state & (State_Sunken | State_On)) && !(state & State_MouseOver) && (state & State_Enabled) && (state & State_Active)) { - QWindowsVistaAnimation *anim = d->widgetAnimation(widget); if (!anim && widget) { QImage startImage(option->rect.size(), QImage::Format_ARGB32_Premultiplied); startImage.fill(0); @@ -1063,6 +1063,19 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption } break; #ifndef QT_NO_PROGRESSBAR + case CE_ProgressBarGroove: + { + Qt::Orientation orient = Qt::Horizontal; + if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option)) + orient = pb2->orientation; + partId = (orient == Qt::Horizontal) ? PP_TRANSPARENTBAR : PP_TRANSPARENTBARVERT; + name = QLatin1String("PROGRESS"); + stateId = 1; + + XPThemeData theme(widget, painter, name, partId, stateId, rect); + d->drawBackground(theme); + } + break; case CE_ProgressBarContents: if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { @@ -1078,8 +1091,8 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption } if (const QProgressBar *progressbar = qobject_cast<const QProgressBar *>(widget)) { - if (((progressbar->value() > 0 && d->transitionsEnabled()) || isIndeterminate)) { - if (!d->widgetAnimation(progressbar) && progressbar->value() < progressbar->maximum()) { + if (isIndeterminate || (progressbar->value() > 0 && (progressbar->value() < progressbar->maximum()) && d->transitionsEnabled())) { + if (!d->widgetAnimation(progressbar)) { QWindowsVistaAnimation *a = new QWindowsVistaAnimation; a->setWidget(const_cast<QWidget*>(widget)); a->setStartTime(QTime::currentTime()); @@ -2396,14 +2409,14 @@ void QWindowsVistaStyle::polish(QWidget *widget) } } else if (qobject_cast<QMessageBox *> (widget)) { widget->setAttribute(Qt::WA_StyledBackground); - QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox *>(widget,QLatin1String("qt_msgbox_buttonbox")); + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); if (buttonBox) buttonBox->setContentsMargins(0, 9, 0, 0); } #ifndef QT_NO_INPUTDIALOG else if (qobject_cast<QInputDialog *> (widget)) { widget->setAttribute(Qt::WA_StyledBackground); - QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox *>(widget,QLatin1String("qt_inputdlg_buttonbox")); + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); if (buttonBox) buttonBox->setContentsMargins(0, 9, 0, 0); } @@ -2435,14 +2448,14 @@ void QWindowsVistaStyle::unpolish(QWidget *widget) widget->setAttribute(Qt::WA_Hover, false); else if (qobject_cast<QMessageBox *> (widget)) { widget->setAttribute(Qt::WA_StyledBackground, false); - QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox *>(widget,QLatin1String("qt_msgbox_buttonbox")); + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); if (buttonBox) buttonBox->setContentsMargins(0, 0, 0, 0); } #ifndef QT_NO_INPUTDIALOG else if (qobject_cast<QInputDialog *> (widget)) { widget->setAttribute(Qt::WA_StyledBackground, false); - QDialogButtonBox *buttonBox = qFindChild<QDialogButtonBox *>(widget,QLatin1String("qt_inputdlg_buttonbox")); + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); if (buttonBox) buttonBox->setContentsMargins(0, 0, 0, 0); } @@ -2506,7 +2519,6 @@ void QWindowsVistaStylePrivate::timerEvent() animations[i]->widget()->update(); if (!animations[i]->widget() || - !animations[i]->widget()->isEnabled() || !animations[i]->widget()->isVisible() || animations[i]->widget()->window()->isMinimized() || !animations[i]->running() || diff --git a/src/gui/styles/qwindowsxpstyle.cpp b/src/gui/styles/qwindowsxpstyle.cpp index 3d6112c..3c33df3 100644 --- a/src/gui/styles/qwindowsxpstyle.cpp +++ b/src/gui/styles/qwindowsxpstyle.cpp @@ -1196,8 +1196,14 @@ QRect QWindowsXPStyle::subElementRect(SubElement sr, const QStyleOption *option, if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) { rect = QWindowsStyle::subElementRect(sr, option, widget); - if (sr == SE_TabWidgetTabContents) - rect.adjust(0, 0, -2, -2); + if (sr == SE_TabWidgetTabContents) { + if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) { + if (tabWidget->documentMode()) + break; + } + + rect.adjust(0, 0, -2, -2); + } } break; case SE_TabWidgetTabBar: { @@ -4054,7 +4060,7 @@ void QWindowsXPStylePrivate::dumpNativeDIB(int w, int h) bufferPos += sprintf(bufferPos, "\n};\n\n"); printf(bufferDump); - delete bufferDump; + delete[] bufferDump; ++pCount; } } diff --git a/src/gui/styles/styles.pri b/src/gui/styles/styles.pri index b22a908..b6eeec9 100644 --- a/src/gui/styles/styles.pri +++ b/src/gui/styles/styles.pri @@ -35,7 +35,7 @@ contains( styles, all ) { styles = mac windows windowsxp windowsvista } -x11|embedded|!macx-*:styles -= mac +x11|embedded|qpa|!macx-*:styles -= mac x11{ QMAKE_CXXFLAGS += $$QT_CFLAGS_QGTKSTYLE diff --git a/src/gui/symbian/qsymbianevent.cpp b/src/gui/symbian/qsymbianevent.cpp index ac9c4d5..f6b2185 100644 --- a/src/gui/symbian/qsymbianevent.cpp +++ b/src/gui/symbian/qsymbianevent.cpp @@ -40,6 +40,9 @@ ****************************************************************************/ #include "qsymbianevent.h" +#include <qdebug.h> + +#include <w32std.h> QT_BEGIN_NAMESPACE @@ -140,4 +143,34 @@ int QSymbianEvent::resourceChangeType() const return (m_type == ResourceChangeEvent) ? m_eventValue : 0; } +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QSymbianEvent *o) +{ + if (!o) { + dbg << "QSymbianEvent(0x0)"; + return dbg; + } + dbg.nospace() << "QSymbianEvent("; + switch (o->type()) { + case QSymbianEvent::InvalidEvent: + dbg << "InvalidEvent"; + break; + case QSymbianEvent::WindowServerEvent: + dbg << "WindowServerEvent, Type = " << o->windowServerEvent()->Type(); + break; + case QSymbianEvent::CommandEvent: + dbg << "CommandEvent, command = " << o->command(); + break; + case QSymbianEvent::ResourceChangeEvent: + dbg << "ResourceChangeEvent, resourceChangeType = " << o->resourceChangeType(); + break; + default: + dbg << "Unknown event type"; + break; + } + dbg << ")"; + return dbg.space(); +} +#endif + QT_END_NAMESPACE diff --git a/src/gui/symbian/qsymbianevent.h b/src/gui/symbian/qsymbianevent.h index e17a9a7..6a7984e 100644 --- a/src/gui/symbian/qsymbianevent.h +++ b/src/gui/symbian/qsymbianevent.h @@ -95,6 +95,10 @@ inline bool QSymbianEvent::isValid() const return m_type != InvalidEvent; } +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QSymbianEvent *o); +#endif + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index 4be50b7..028011b 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -68,6 +68,8 @@ static const QCssKnownValue properties[NumProperties - 1] = { { "-qt-background-role", QtBackgroundRole }, { "-qt-block-indent", QtBlockIndent }, { "-qt-list-indent", QtListIndent }, + { "-qt-list-number-prefix", QtListNumberPrefix }, + { "-qt-list-number-suffix", QtListNumberSuffix }, { "-qt-paragraph-type", QtParagraphType }, { "-qt-style-features", QtStyleFeatures }, { "-qt-table-type", QtTableType }, @@ -120,6 +122,7 @@ static const QCssKnownValue properties[NumProperties - 1] = { { "image", QtImage }, { "image-position", QtImageAlignment }, { "left", Left }, + { "line-height", LineHeight }, { "list-style", ListStyle }, { "list-style-type", ListStyleType }, { "margin" , Margin }, @@ -403,7 +406,7 @@ int ValueExtractor::lengthValue(const Declaration &decl) if (decl.d->values.count() < 1) return 0; LengthData data = lengthValue(decl.d->values.at(0)); - decl.d->parsed = qVariantFromValue<LengthData>(data); + decl.d->parsed = QVariant::fromValue<LengthData>(data); return lengthValueFromData(data,f); } @@ -435,7 +438,7 @@ void ValueExtractor::lengthValues(const Declaration &decl, int *m) QList<QVariant> v; for (i = 0; i < 4; i++) { - v += qVariantFromValue<LengthData>(datas[i]); + v += QVariant::fromValue<LengthData>(datas[i]); m[i] = lengthValueFromData(datas[i], f); } decl.d->parsed = v; @@ -541,7 +544,7 @@ QSize ValueExtractor::sizeValue(const Declaration &decl) else x[1] = x[0]; QList<QVariant> v; - v << qVariantFromValue<LengthData>(x[0]) << qVariantFromValue<LengthData>(x[1]); + v << QVariant::fromValue<LengthData>(x[0]) << qVariantFromValue<LengthData>(x[1]); decl.d->parsed = v; return QSize(lengthValueFromData(x[0], f), lengthValueFromData(x[1], f)); } @@ -646,7 +649,7 @@ bool ValueExtractor::extractOutline(int *borders, QBrush *colors, BorderStyle *s return hit; } -static Qt::Alignment parseAlignment(const Value *values, int count) +static Qt::Alignment parseAlignment(const QCss::Value *values, int count) { Qt::Alignment a[2] = { 0, 0 }; for (int i = 0; i < qMin(2, count); i++) { @@ -669,7 +672,7 @@ static Qt::Alignment parseAlignment(const Value *values, int count) return a[0] | a[1]; } -static ColorData parseColorValue(Value v) +static ColorData parseColorValue(QCss::Value v) { if (v.type == Value::Identifier || v.type == Value::String) { v.variant.convert(QVariant::Color); @@ -703,7 +706,7 @@ static ColorData parseColorValue(Value v) if (!p.testExpr()) return ColorData(); - QVector<Value> colorDigits; + QVector<QCss::Value> colorDigits; if (!p.parseExpr(&colorDigits)) return ColorData(); @@ -735,7 +738,7 @@ static QColor colorFromData(const ColorData& c, const QPalette &pal) return QColor(); } -static BrushData parseBrushValue(const Value &v, const QPalette &pal) +static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal) { ColorData c = parseColorValue(v); if (c.type == ColorData::Color) { @@ -777,7 +780,7 @@ static BrushData parseBrushValue(const Value &v, const QPalette &pal) return BrushData(); parser.skipSpace(); if (attr.compare(QLatin1String("stop"), Qt::CaseInsensitive) == 0) { - Value stop, color; + QCss::Value stop, color; parser.next(); if (!parser.parseTerm(&stop)) return BrushData(); parser.skipSpace(); @@ -789,7 +792,7 @@ static BrushData parseBrushValue(const Value &v, const QPalette &pal) stops.append(QGradientStop(stop.variant.toReal(), colorFromData(cd, pal))); } else { parser.next(); - Value value; + QCss::Value value; (void)parser.parseTerm(&value); if (attr.compare(QLatin1String("spread"), Qt::CaseInsensitive) == 0) { spread = spreads.indexOf(value.variant.toString()); @@ -853,7 +856,7 @@ static QBrush brushFromData(const BrushData& c, const QPalette &pal) } } -static BorderStyle parseStyleValue(Value v) +static BorderStyle parseStyleValue(QCss::Value v) { if (v.type == Value::KnownIdentifier) { switch (v.variant.toInt()) { @@ -916,7 +919,7 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord data.width = lengthValue(decl.d->values.at(i)); *width = lengthValueFromData(data.width, f); if (++i >= decl.d->values.count()) { - decl.d->parsed = qVariantFromValue<BorderData>(data); + decl.d->parsed = QVariant::fromValue<BorderData>(data); return; } } @@ -925,7 +928,7 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord if (data.style != BorderStyle_Unknown) { *style = data.style; if (++i >= decl.d->values.count()) { - decl.d->parsed = qVariantFromValue<BorderData>(data); + decl.d->parsed = QVariant::fromValue<BorderData>(data); return; } } else { @@ -935,10 +938,10 @@ void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::Bord data.color = parseBrushValue(decl.d->values.at(i), pal); *color = brushFromData(data.color, pal); if (data.color.type != BrushData::DependsOnThePalette) - decl.d->parsed = qVariantFromValue<BorderData>(data); + decl.d->parsed = QVariant::fromValue<BorderData>(data); } -static void parseShorthandBackgroundProperty(const QVector<Value> &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal) +static void parseShorthandBackgroundProperty(const QVector<QCss::Value> &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal) { *brush = BrushData(); *image = QString(); @@ -946,7 +949,7 @@ static void parseShorthandBackgroundProperty(const QVector<Value> &values, Brush *alignment = Qt::AlignTop | Qt::AlignLeft; for (int i = 0; i < values.count(); ++i) { - const Value &v = values.at(i); + const QCss::Value &v = values.at(i); if (v.type == Value::Uri) { *image = v.variant.toString(); continue; @@ -993,7 +996,7 @@ bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *re const Declaration &decl = declarations.at(i); if (decl.d->values.isEmpty()) continue; - const Value &val = decl.d->values.at(0); + const QCss::Value &val = decl.d->values.at(0); switch (decl.d->propertyId) { case BackgroundColor: *brush = decl.brushValue(); @@ -1032,16 +1035,8 @@ bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *re parseShorthandBackgroundProperty(decl.d->values, &brushData, image, repeat, alignment, pal); *brush = brushFromData(brushData, pal); if (brushData.type != BrushData::DependsOnThePalette) { -#if defined Q_CC_MSVC && _MSC_VER <= 1300 - BackgroundData data; - data.brush = brushData; - data.image = *image; - data.repeat = *repeat; - data.alignment = *alignment; -#else BackgroundData data = { brushData, *image, *repeat, *alignment }; -#endif - decl.d->parsed = qVariantFromValue<BackgroundData>(data); + decl.d->parsed = QVariant::fromValue<BackgroundData>(data); } } break; @@ -1055,7 +1050,7 @@ bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *re return hit; } -static bool setFontSizeFromValue(Value value, QFont *font, int *fontSizeAdjustment) +static bool setFontSizeFromValue(QCss::Value value, QFont *font, int *fontSizeAdjustment) { if (value.type == Value::KnownIdentifier) { bool valid = true; @@ -1092,7 +1087,7 @@ static bool setFontSizeFromValue(Value value, QFont *font, int *fontSizeAdjustme return valid; } -static bool setFontStyleFromValue(const Value &value, QFont *font) +static bool setFontStyleFromValue(const QCss::Value &value, QFont *font) { if (value.type != Value::KnownIdentifier) return false ; @@ -1105,7 +1100,7 @@ static bool setFontStyleFromValue(const Value &value, QFont *font) return false; } -static bool setFontWeightFromValue(const Value &value, QFont *font) +static bool setFontWeightFromValue(const QCss::Value &value, QFont *font) { if (value.type == Value::KnownIdentifier) { switch (value.variant.toInt()) { @@ -1126,12 +1121,12 @@ static bool setFontWeightFromValue(const Value &value, QFont *font) * and set it the \a font * \returns true if a family was extracted. */ -static bool setFontFamilyFromValues(const QVector<Value> &values, QFont *font, int start = 0) +static bool setFontFamilyFromValues(const QVector<QCss::Value> &values, QFont *font, int start = 0) { QString family; bool shouldAddSpace = false; for (int i = start; i < values.count(); ++i) { - const Value &v = values.at(i); + const QCss::Value &v = values.at(i); if (v.type == Value::TermOperatorComma) { family += QLatin1Char(','); shouldAddSpace = false; @@ -1151,7 +1146,7 @@ static bool setFontFamilyFromValues(const QVector<Value> &values, QFont *font, i return true; } -static void setTextDecorationFromValues(const QVector<Value> &values, QFont *font) +static void setTextDecorationFromValues(const QVector<QCss::Value> &values, QFont *font) { for (int i = 0; i < values.count(); ++i) { if (values.at(i).type != Value::KnownIdentifier) @@ -1170,7 +1165,7 @@ static void setTextDecorationFromValues(const QVector<Value> &values, QFont *fon } } -static void parseShorthandFontProperty(const QVector<Value> &values, QFont *font, int *fontSizeAdjustment) +static void parseShorthandFontProperty(const QVector<QCss::Value> &values, QFont *font, int *fontSizeAdjustment) { font->setStyle(QFont::StyleNormal); font->setWeight(QFont::Normal); @@ -1195,7 +1190,7 @@ static void parseShorthandFontProperty(const QVector<Value> &values, QFont *font } } -static void setFontVariantFromValue(const Value &value, QFont *font) +static void setFontVariantFromValue(const QCss::Value &value, QFont *font) { if (value.type == Value::KnownIdentifier) { switch (value.variant.toInt()) { @@ -1206,7 +1201,7 @@ static void setFontVariantFromValue(const Value &value, QFont *font) } } -static void setTextTransformFromValue(const Value &value, QFont *font) +static void setTextTransformFromValue(const QCss::Value &value, QFont *font) { if (value.type == Value::KnownIdentifier) { switch (value.variant.toInt()) { @@ -1231,7 +1226,7 @@ bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment) const Declaration &decl = declarations.at(i); if (decl.d->values.isEmpty()) continue; - const Value &val = decl.d->values.at(0); + const QCss::Value &val = decl.d->values.at(0); switch (decl.d->propertyId) { case FontSize: setFontSizeFromValue(val, font, fontSizeAdjustment); break; case FontStyle: setFontStyleFromValue(val, font); break; @@ -1319,10 +1314,10 @@ QColor Declaration::colorValue(const QPalette &pal) const ColorData color = parseColorValue(d->values.at(0)); if(color.type == ColorData::Role) { - d->parsed = qVariantFromValue<int>(color.role); + d->parsed = QVariant::fromValue<int>(color.role); return pal.color((QPalette::ColorRole)(color.role)); } else { - d->parsed = qVariantFromValue<QColor>(color.color); + d->parsed = QVariant::fromValue<QColor>(color.color); return color.color; } } @@ -1342,11 +1337,11 @@ QBrush Declaration::brushValue(const QPalette &pal) const BrushData data = parseBrushValue(d->values.at(0), pal); if(data.type == BrushData::Role) { - d->parsed = qVariantFromValue<int>(data.role); + d->parsed = QVariant::fromValue<int>(data.role); return pal.color((QPalette::ColorRole)(data.role)); } else { if (data.type != BrushData::DependsOnThePalette) - d->parsed = qVariantFromValue<QBrush>(data.brush); + d->parsed = QVariant::fromValue<QBrush>(data.brush); return data.brush; } } @@ -1376,11 +1371,11 @@ void Declaration::brushValues(QBrush *c, const QPalette &pal) const continue; BrushData data = parseBrushValue(d->values.at(i), pal); if(data.type == BrushData::Role) { - v += qVariantFromValue<int>(data.role); + v += QVariant::fromValue<int>(data.role); c[i] = pal.color((QPalette::ColorRole)(data.role)); } else { if (data.type != BrushData::DependsOnThePalette) { - v += qVariantFromValue<QBrush>(data.brush); + v += QVariant::fromValue<QBrush>(data.brush); } else { v += QVariant(); } @@ -1416,7 +1411,7 @@ bool Declaration::realValue(qreal *real, const char *unit) const return ok; } -static bool intValueHelper(const Value &v, int *i, const char *unit) +static bool intValueHelper(const QCss::Value &v, int *i, const char *unit) { if (unit && v.type != Value::Length) return false; @@ -1453,7 +1448,7 @@ QSize Declaration::sizeValue() const else x[1] = x[0]; QSize size(x[0], x[1]); - d->parsed = qVariantFromValue<QSize>(size); + d->parsed = QVariant::fromValue<QSize>(size); return size; } @@ -1465,7 +1460,7 @@ QRect Declaration::rectValue() const if (d->parsed.isValid()) return qvariant_cast<QRect>(d->parsed); - const Value &v = d->values.at(0); + const QCss::Value &v = d->values.at(0); if (v.type != Value::Function) return QRect(); QStringList func = v.variant.toStringList(); @@ -1475,7 +1470,7 @@ QRect Declaration::rectValue() const if (args.count() != 4) return QRect(); QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt()); - d->parsed = qVariantFromValue<QRect>(rect); + d->parsed = QVariant::fromValue<QRect>(rect); return rect; } @@ -1496,10 +1491,10 @@ void Declaration::colorValues(QColor *c, const QPalette &pal) const for (i = 0; i < qMin(d->values.count(), 4); i++) { ColorData color = parseColorValue(d->values.at(i)); if(color.type == ColorData::Role) { - v += qVariantFromValue<int>(color.role); + v += QVariant::fromValue<int>(color.role); c[i] = pal.color((QPalette::ColorRole)(color.role)); } else { - v += qVariantFromValue<QColor>(color.color); + v += QVariant::fromValue<QColor>(color.color); c[i] = color.color; } } @@ -1691,7 +1686,7 @@ QIcon Declaration::iconValue() const i++; } - d->parsed = qVariantFromValue<QIcon>(icon); + d->parsed = QVariant::fromValue<QIcon>(icon); return icon; } diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index 99de22f..9c974f7 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -71,6 +71,9 @@ #if defined(Q_OS_VXWORKS) && defined(NONE) # undef NONE #endif +#if defined(Q_OS_INTEGRITY) +# undef Value +#endif QT_BEGIN_NAMESPACE @@ -178,6 +181,9 @@ enum Property { OutlineBottomRightRadius, FontVariant, TextTransform, + QtListNumberPrefix, + QtListNumberSuffix, + LineHeight, NumProperties }; diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 1e29f34..2df88e2 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -75,6 +75,10 @@ #ifdef Q_OS_SYMBIAN #include <private/qt_s60_p.h> #endif +#ifdef Q_WS_QPA +#include <QtGui/qplatformscreen_qpa.h> +#include <QtGui/private/qapplication_p.h> +#endif #include <QMutexLocker> @@ -135,11 +139,15 @@ bool QFontDef::exactMatch(const QFontDef &other) const QFontDatabase::parseFontName(family, this_foundry, this_family); QFontDatabase::parseFontName(other.family, other_foundry, other_family); + this_family = QFontDatabase::resolveFontFamilyAlias(this_family); + other_family = QFontDatabase::resolveFontFamilyAlias(other_family); + return (styleHint == other.styleHint && styleStrategy == other.styleStrategy && weight == other.weight && style == other.style && this_family == other_family + && (styleName.isEmpty() || other.styleName.isEmpty() || styleName == other.styleName) && (this_foundry.isEmpty() || other_foundry.isEmpty() || this_foundry == other_foundry) @@ -172,6 +180,17 @@ Q_GUI_EXPORT int qt_defaultDpiX() if (!subScreens.isEmpty()) screen = subScreens.at(0); dpi = qRound(screen->width() / (screen->physicalWidth() / qreal(25.4))); +#elif defined(Q_WS_QPA) + QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); + if (pi) { + QPlatformScreen *screen = QApplicationPrivate::platformIntegration()->screens().at(0); + const QSize screenSize = screen->geometry().size(); + const QSize physicalSize = screen->physicalSize(); + dpi = qRound(screenSize.width() / (physicalSize.width() / qreal(25.4))); + } else { + //PI has not been initialised, or it is being initialised. Give a default dpi + dpi = 100; + } #elif defined(Q_OS_SYMBIAN) dpi = S60->defaultDpiX; #endif // Q_WS_X11 @@ -200,6 +219,17 @@ Q_GUI_EXPORT int qt_defaultDpiY() if (!subScreens.isEmpty()) screen = subScreens.at(0); dpi = qRound(screen->height() / (screen->physicalHeight() / qreal(25.4))); +#elif defined(Q_WS_QPA) + QPlatformIntegration *pi = QApplicationPrivate::platformIntegration(); + if (pi) { + QPlatformScreen *screen = QApplicationPrivate::platformIntegration()->screens().at(0); + const QSize screenSize = screen->geometry().size(); + const QSize physicalSize = screen->physicalSize(); + dpi = qRound(screenSize.height() / (physicalSize.height() / qreal(25.4))); + } else { + //PI has not been initialised, or it is being initialised. Give a default dpi + dpi = 100; + } #elif defined(Q_OS_SYMBIAN) dpi = S60->defaultDpiY; #endif // Q_WS_X11 @@ -253,27 +283,16 @@ QFontPrivate::~QFontPrivate() scFont = 0; } -#if !defined(Q_WS_MAC) extern QMutex *qt_fontdatabase_mutex(); -QFontEngine *QFontPrivate::engineForScript(int script) const -{ - QMutexLocker locker(qt_fontdatabase_mutex()); - if (script >= QUnicodeTables::Inherited) - script = QUnicodeTables::Common; - if (engineData && engineData->fontCache != QFontCache::instance()) { - // throw out engineData that came from a different thread - engineData->ref.deref(); - engineData = 0; - } - if (!engineData || !engineData->engines[script]) - QFontDatabase::load(this, script); - return engineData->engines[script]; -} +#if !defined(Q_WS_MAC) +#define QT_FONT_ENGINE_FROM_DATA(data, script) data->engines[script] #else +#define QT_FONT_ENGINE_FROM_DATA(data, script) data->engine +#endif + QFontEngine *QFontPrivate::engineForScript(int script) const { - extern QMutex *qt_fontdatabase_mutex(); QMutexLocker locker(qt_fontdatabase_mutex()); if (script >= QUnicodeTables::Inherited) script = QUnicodeTables::Common; @@ -282,11 +301,10 @@ QFontEngine *QFontPrivate::engineForScript(int script) const engineData->ref.deref(); engineData = 0; } - if (!engineData || !engineData->engine) + if (!engineData || !QT_FONT_ENGINE_FROM_DATA(engineData, script)) QFontDatabase::load(this, script); - return engineData->engine; + return QT_FONT_ENGINE_FROM_DATA(engineData, script); } -#endif void QFontPrivate::alterCharForCapitalization(QChar &c) const { switch (capital) { @@ -331,6 +349,9 @@ void QFontPrivate::resolve(uint mask, const QFontPrivate *other) if (! (mask & QFont::FamilyResolved)) request.family = other->request.family; + if (! (mask & QFont::StyleNameResolved)) + request.styleName = other->request.styleName; + if (! (mask & QFont::SizeResolved)) { request.pointSize = other->request.pointSize; request.pixelSize = other->request.pixelSize; @@ -354,6 +375,9 @@ void QFontPrivate::resolve(uint mask, const QFontPrivate *other) if (! (mask & QFont::StretchResolved)) request.stretch = other->request.stretch; + if (! (mask & QFont::HintingPreferenceResolved)) + request.hintingPreference = other->request.hintingPreference; + if (! (mask & QFont::UnderlineResolved)) underline = other->underline; @@ -877,6 +901,39 @@ void QFont::setFamily(const QString &family) } /*! + \since 4.8 + + Returns the requested font style name, it will be used to match the + font with irregular styles (that can't be normalized in other style + properties). It depends on system font support, thus only works for + Mac OS X and X11 so far. On Windows irregular styles will be added + as separate font families so there is no need for this. + + \sa setFamily() setStyle() +*/ +QString QFont::styleName() const +{ + return d->request.styleName; +} + +/*! + \since 4.8 + + Sets the style name of the font to the given \a styleName. + When set, other style properties like style() and weight() will be ignored + for font matching. + + \sa styleName() +*/ +void QFont::setStyleName(const QString &styleName) +{ + detach(); + + d->request.styleName = styleName; + resolve_mask |= QFont::StyleNameResolved; +} + +/*! Returns the point size of the font. Returns -1 if the font size was specified in pixels. @@ -888,6 +945,105 @@ int QFont::pointSize() const } /*! + \since 4.8 + + \enum QFont::HintingPreference + + This enum describes the different levels of hinting that can be applied + to glyphs to improve legibility on displays where it might be warranted + by the density of pixels. + + \value PreferDefaultHinting Use the default hinting level for the target platform. + \value PreferNoHinting If possible, render text without hinting the outlines + of the glyphs. The text layout will be typographically accurate and + scalable, using the same metrics as are used e.g. when printing. + \value PreferVerticalHinting If possible, render text with no horizontal hinting, + but align glyphs to the pixel grid in the vertical direction. The text will appear + crisper on displays where the density is too low to give an accurate rendering + of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's + layout will be scalable to higher density devices (such as printers) without impacting + details such as line breaks. + \value PreferFullHinting If possible, render text with hinting in both horizontal and + vertical directions. The text will be altered to optimize legibility on the target + device, but since the metrics will depend on the target size of the text, the positions + of glyphs, line breaks, and other typographical detail will not scale, meaning that a + text layout may look different on devices with different pixel densities. + + Please note that this enum only describes a preference, as the full range of hinting levels + are not supported on all of Qt's supported platforms. The following table details the effect + of a given hinting preference on a selected set of target platforms. + + \table + \header + \o + \o PreferDefaultHinting + \o PreferNoHinting + \o PreferVerticalHinting + \o PreferFullHinting + \row + \o Windows Vista (w/o Platform Update) and earlier + \o Full hinting + \o Full hinting + \o Full hinting + \o Full hinting + \row + \o Windows 7 and Windows Vista (w/Platform Update) and DirectWrite enabled in Qt + \o Full hinting + \o Vertical hinting + \o Vertical hinting + \o Full hinting + \row + \o FreeType + \o Operating System setting + \o No hinting + \o Vertical hinting (light) + \o Full hinting + \row + \o Cocoa on Mac OS X + \o No hinting + \o No hinting + \o No hinting + \o No hinting + \endtable + + \note Please be aware that altering the hinting preference on Windows is available through + the DirectWrite font engine. This is available on Windows Vista after installing the platform + update, and on Windows 7. In order to use this extension, configure Qt using -directwrite. + The target application will then depend on the availability of DirectWrite on the target + system. + +*/ + +/*! + \since 4.8 + + Set the preference for the hinting level of the glyphs to \a hintingPreference. This is a hint + to the underlying font rendering system to use a certain level of hinting, and has varying + support across platforms. See the table in the documentation for QFont::HintingPreference for + more details. + + The default hinting preference is QFont::PreferDefaultHinting. +*/ +void QFont::setHintingPreference(HintingPreference hintingPreference) +{ + detach(); + + d->request.hintingPreference = hintingPreference; + + resolve_mask |= QFont::HintingPreferenceResolved; +} + +/*! + \since 4.8 + + Returns the currently preferred hinting level for glyphs rendered with this font. +*/ +QFont::HintingPreference QFont::hintingPreference() const +{ + return QFont::HintingPreference(d->request.hintingPreference); +} + +/*! Sets the point size to \a pointSize. The point size must be greater than zero. @@ -2390,6 +2546,21 @@ QString QFontInfo::family() const } /*! + \since 4.8 + + Returns the style name of the matched window system font on + system that supports it. + + \sa QFont::styleName() +*/ +QString QFontInfo::styleName() const +{ + QFontEngine *engine = d->engineForScript(QUnicodeTables::Common); + Q_ASSERT(engine != 0); + return engine->fontDef.styleName; +} + +/*! Returns the point size of the matched window system font. \sa pointSizeF() QFont::pointSize() diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index 809d816..14f290c 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -93,6 +93,13 @@ public: NoFontMerging = 0x8000 }; + enum HintingPreference { + PreferDefaultHinting = 0, + PreferNoHinting = 1, + PreferVerticalHinting = 2, + PreferFullHinting = 3 + }; + enum Weight { Light = 25, Normal = 50, @@ -133,22 +140,24 @@ public: }; enum ResolveProperties { - FamilyResolved = 0x0001, - SizeResolved = 0x0002, - StyleHintResolved = 0x0004, - StyleStrategyResolved = 0x0008, - WeightResolved = 0x0010, - StyleResolved = 0x0020, - UnderlineResolved = 0x0040, - OverlineResolved = 0x0080, - StrikeOutResolved = 0x0100, - FixedPitchResolved = 0x0200, - StretchResolved = 0x0400, - KerningResolved = 0x0800, - CapitalizationResolved = 0x1000, - LetterSpacingResolved = 0x2000, - WordSpacingResolved = 0x4000, - AllPropertiesResolved = 0x7fff + FamilyResolved = 0x0001, + SizeResolved = 0x0002, + StyleHintResolved = 0x0004, + StyleStrategyResolved = 0x0008, + WeightResolved = 0x0010, + StyleResolved = 0x0020, + UnderlineResolved = 0x0040, + OverlineResolved = 0x0080, + StrikeOutResolved = 0x0100, + FixedPitchResolved = 0x0200, + StretchResolved = 0x0400, + KerningResolved = 0x0800, + CapitalizationResolved = 0x1000, + LetterSpacingResolved = 0x2000, + WordSpacingResolved = 0x4000, + HintingPreferenceResolved = 0x8000, + StyleNameResolved = 0x10000, + AllPropertiesResolved = 0x1ffff }; QFont(); @@ -160,6 +169,9 @@ public: QString family() const; void setFamily(const QString &); + QString styleName() const; + void setStyleName(const QString &); + int pointSize() const; void setPointSize(int); qreal pointSizeF() const; @@ -213,6 +225,9 @@ public: void setCapitalization(Capitalization); Capitalization capitalization() const; + void setHintingPreference(HintingPreference hintingPreference); + HintingPreference hintingPreference() const; + // is raw mode still needed? bool rawMode() const; void setRawMode(bool); @@ -226,7 +241,10 @@ public: bool operator<(const QFont &) const; operator QVariant() const; bool isCopyOf(const QFont &) const; - +#ifdef Q_COMPILER_RVALUE_REFS + inline QFont &operator=(QFont &&other) + { qSwap(d, other.d); qSwap(resolve_mask, other.resolve_mask); return *this; } +#endif #ifdef Q_WS_WIN HFONT handle() const; @@ -315,6 +333,7 @@ private: friend class QPainterReplayer; friend class QPaintBufferEngine; friend class QCommandLinkButtonPrivate; + friend class QFontEngine; #ifndef QT_NO_DATASTREAM friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QFont &); diff --git a/src/gui/text/qfont_mac.cpp b/src/gui/text/qfont_mac.cpp index a734432..3bbff7f 100644 --- a/src/gui/text/qfont_mac.cpp +++ b/src/gui/text/qfont_mac.cpp @@ -42,6 +42,8 @@ #include "qfont.h" #include "qfont_p.h" #include "qfontengine_p.h" +#include "qfontengine_mac_p.h" +#include "qfontengine_coretext_p.h" #include "qfontinfo.h" #include "qfontmetrics.h" #include "qpaintdevice.h" @@ -118,10 +120,10 @@ quint32 QFont::macFontID() const // ### need 64-bit version // Returns an ATSUFonFamilyRef Qt::HANDLE QFont::handle() const { -#if 0 +#ifdef QT_MAC_USE_COCOA QFontEngine *fe = d->engineForScript(QUnicodeTables::Common); - if (fe && fe->type() == QFontEngine::Mac) - return (Qt::HANDLE)static_cast<QFontEngineMacMulti*>(fe)->fontFamilyRef(); + if (fe && fe->type() == QFontEngine::Multi) + return (Qt::HANDLE)static_cast<QCoreTextFontEngineMulti*>(fe)->macFontID(); #endif return 0; } diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h index 795c41c..ebc842c 100644 --- a/src/gui/text/qfont_p.h +++ b/src/gui/text/qfont_p.h @@ -72,7 +72,7 @@ struct QFontDef : pointSize(-1.0), pixelSize(-1), styleStrategy(QFont::PreferDefault), styleHint(QFont::AnyStyle), weight(50), fixedPitch(false), style(QFont::StyleNormal), stretch(100), - ignorePitch(true) + ignorePitch(true), hintingPreference(QFont::PreferDefaultHinting) #ifdef Q_WS_MAC ,fixedPitchComputed(false) #endif @@ -80,6 +80,7 @@ struct QFontDef } QString family; + QString styleName; #ifdef Q_WS_X11 QString addStyle; @@ -97,8 +98,9 @@ struct QFontDef uint stretch : 12; // 0-400 uint ignorePitch : 1; + uint hintingPreference : 2; uint fixedPitchComputed : 1; // for Mac OS X only - int reserved : 16; // for future extensions + int reserved : 14; // for future extensions bool exactMatch(const QFontDef &other) const; bool operator==(const QFontDef &other) const @@ -111,6 +113,8 @@ struct QFontDef && styleStrategy == other.styleStrategy && ignorePitch == other.ignorePitch && fixedPitch == other.fixedPitch && family == other.family + && (styleName.isEmpty() || other.styleName.isEmpty() || styleName == other.styleName) + && hintingPreference == other.hintingPreference #ifdef Q_WS_X11 && addStyle == other.addStyle #endif @@ -125,6 +129,9 @@ struct QFontDef if (styleHint != other.styleHint) return styleHint < other.styleHint; if (styleStrategy != other.styleStrategy) return styleStrategy < other.styleStrategy; if (family != other.family) return family < other.family; + if (!styleName.isEmpty() && !other.styleName.isEmpty() && styleName != other.styleName) + return styleName < other.styleName; + if (hintingPreference != other.hintingPreference) return hintingPreference < other.hintingPreference; #ifdef Q_WS_X11 if (addStyle != other.addStyle) return addStyle < other.addStyle; @@ -192,6 +199,11 @@ public: QFont smallCapsFont() const { return QFont(smallCapsFontPrivate()); } QFontPrivate *smallCapsFontPrivate() const; + static QFontPrivate *get(const QFont &font) + { + return font.d.data(); + } + void resolve(uint mask, const QFontPrivate *other); private: QFontPrivate &operator=(const QFontPrivate &) { return *this; } @@ -273,6 +285,10 @@ public: int timer_id; }; +Q_GUI_EXPORT int qt_defaultDpiX(); +Q_GUI_EXPORT int qt_defaultDpiY(); +Q_GUI_EXPORT int qt_defaultDpi(); + QT_END_NAMESPACE #endif // QFONT_P_H diff --git a/src/gui/text/qfont_qpa.cpp b/src/gui/text/qfont_qpa.cpp new file mode 100644 index 0000000..3e0a246 --- /dev/null +++ b/src/gui/text/qfont_qpa.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/private/qapplication_p.h> +#include <QtGui/QPlatformFontDatabase> + +QT_BEGIN_NAMESPACE + +void QFont::initialize() +{ + QApplicationPrivate::platformIntegration()->fontDatabase()->populateFontDatabase(); +} + +void QFont::cleanup() +{ + QFontCache::cleanup(); +} + + +/***************************************************************************** + QFont member functions + *****************************************************************************/ + +Qt::HANDLE QFont::handle() const +{ + return 0; +} + +QString QFont::rawName() const +{ + return QLatin1String("unknown"); +} + +void QFont::setRawName(const QString &) +{ +} + +QString QFont::defaultFamily() const +{ + QString familyName; + switch(d->request.styleHint) { + case QFont::Times: + familyName = QString::fromLatin1("times"); + case QFont::Courier: + case QFont::Monospace: + familyName = QString::fromLatin1("monospace"); + case QFont::Decorative: + familyName = QString::fromLatin1("old english"); + case QFont::Helvetica: + case QFont::System: + default: + familyName = QString::fromLatin1("helvetica"); + } + + QStringList list = QApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(familyName,QFont::StyleNormal,QFont::StyleHint(d->request.styleHint),QUnicodeTables::Common); + if (list.size()) { + familyName = list.at(0); + } + return familyName; +} + +QString QFont::lastResortFamily() const +{ + return QString::fromLatin1("helvetica"); +} + +QString QFont::lastResortFont() const +{ + qFatal("QFont::lastResortFont: Cannot find any reasonable font"); + // Shut compiler up + return QString(); +} + + +QT_END_NAMESPACE + diff --git a/src/gui/text/qfont_s60.cpp b/src/gui/text/qfont_s60.cpp index 2218617..76133fa 100644 --- a/src/gui/text/qfont_s60.cpp +++ b/src/gui/text/qfont_s60.cpp @@ -117,7 +117,20 @@ QString QFont::lastResortFamily() const const bool isJapaneseOrChineseSystem = User::Language() == ELangJapanese || User::Language() == ELangPrcChinese; - return QLatin1String(isJapaneseOrChineseSystem?"Heisei Kaku Gothic S60":"Series 60 Sans"); + static QString family; + if (family.isEmpty()) { + QStringList families = qt_symbian_fontFamiliesOnFontServer(); + const char* const preferredFamilies[] = {"Nokia Sans S60", "Series 60 Sans"}; + for (int i = 0; i < sizeof preferredFamilies / sizeof preferredFamilies[0]; ++i) { + const QString preferredFamily = QLatin1String(preferredFamilies[i]); + if (families.contains(preferredFamily)) { + family = preferredFamily; + break; + } + } + } + + return QLatin1String(isJapaneseOrChineseSystem?"Heisei Kaku Gothic S60":family.toLatin1()); #endif // QT_NO_FREETYPE } diff --git a/src/gui/text/qfont_win.cpp b/src/gui/text/qfont_win.cpp index 7311861..7a0f234 100644 --- a/src/gui/text/qfont_win.cpp +++ b/src/gui/text/qfont_win.cpp @@ -58,6 +58,7 @@ QT_BEGIN_NAMESPACE extern HDC shared_dc(); // common dc for all fonts +extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp // ### maybe move to qapplication_win QFont qt_LOGFONTtoQFont(LOGFONT& lf, bool /*scale*/) @@ -65,20 +66,8 @@ QFont qt_LOGFONTtoQFont(LOGFONT& lf, bool /*scale*/) QString family = QString::fromWCharArray(lf.lfFaceName); QFont qf(family); qf.setItalic(lf.lfItalic); - if (lf.lfWeight != FW_DONTCARE) { - int weight; - if (lf.lfWeight < 400) - weight = QFont::Light; - else if (lf.lfWeight < 600) - weight = QFont::Normal; - else if (lf.lfWeight < 700) - weight = QFont::DemiBold; - else if (lf.lfWeight < 800) - weight = QFont::Bold; - else - weight = QFont::Black; - qf.setWeight(weight); - } + if (lf.lfWeight != FW_DONTCARE) + qf.setWeight(weightFromInteger(lf.lfWeight)); int lfh = qAbs(lf.lfHeight); qf.setPointSizeF(lfh * 72.0 / GetDeviceCaps(shared_dc(),LOGPIXELSY)); qf.setUnderline(false); diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index cc25dee..1d463c4 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -50,13 +50,19 @@ #include "private/qunicodetables_p.h" #include "qfontengine_p.h" +#ifdef Q_WS_QPA +#include <QtGui/private/qapplication_p.h> +#include <QtGui/qplatformfontdatabase_qpa.h> +#include "qabstractfileengine.h" +#endif + #ifdef Q_WS_X11 #include <locale.h> #endif #include <stdlib.h> #include <limits.h> -#if (defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE) +#if (defined(Q_WS_QWS)|| defined(Q_OS_SYMBIAN)) && !defined(QT_NO_FREETYPE) # include <ft2build.h> # include FT_TRUETYPE_TABLES_H #endif @@ -75,16 +81,14 @@ # define FM_DEBUG if (false) qDebug #endif -#if defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) -# define for if(0){}else for +#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECTWRITE) +# include <dwrite.h> #endif QT_BEGIN_NAMESPACE #define SMOOTH_SCALABLE 0xffff -Q_GUI_EXPORT extern int qt_defaultDpiY(); // in qfont.cpp - bool qt_enable_test_font = false; Q_AUTOTEST_EXPORT void qt_setQtEnableTestFont(bool value) @@ -132,6 +136,21 @@ static int getFontWeight(const QString &weightString) return (int) QFont::Normal; } +// convert 0 ~ 1000 integer to QFont::Weight +QFont::Weight weightFromInteger(int weight) +{ + if (weight < 400) + return QFont::Light; + else if (weight < 600) + return QFont::Normal; + else if (weight < 700) + return QFont::DemiBold; + else if (weight < 800) + return QFont::Bold; + else + return QFont::Black; +} + struct QtFontEncoding { signed int encoding : 16; @@ -143,7 +162,7 @@ struct QtFontEncoding uchar pitch : 8; }; -struct QtFontSize +struct QtFontSize { #ifdef Q_WS_X11 QtFontEncoding *encodings; @@ -151,10 +170,14 @@ struct QtFontSize uint yres = 0, uint avgwidth = 0, bool add = false); unsigned short count : 16; #endif // Q_WS_X11 + #if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) QByteArray fileName; int fileIndex; -#endif // defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) +#endif // defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_QPA) + void *handle; +#endif unsigned short pixelSize : 16; }; @@ -195,16 +218,14 @@ struct QtFontStyle Key(const QString &styleString); Key() : style(QFont::StyleNormal), weight(QFont::Normal), stretch(0) { } - Key(const Key &o) : style(o.style), - weight(o.weight), stretch(o.stretch) { } + Key(const Key &o) : style(o.style), weight(o.weight), stretch(o.stretch) { } uint style : 2; signed int weight : 8; signed int stretch : 12; bool operator==(const Key & other) { - return (style == other.style && - weight == other.weight && - (stretch == 0 || other.stretch == 0 || stretch == other.stretch)); + return (style == other.style && weight == other.weight && + (stretch == 0 || other.stretch == 0 || stretch == other.stretch)); } bool operator!=(const Key &other) { return !operator==(other); @@ -230,7 +251,7 @@ struct QtFontStyle delete [] weightName; delete [] setwidthName; #endif -#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN) while (count) { // bitfield count-- in while condition does not work correctly in mwccsym2 count--; @@ -240,6 +261,12 @@ struct QtFontStyle #if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) pixelSizes[count].fileName.~QByteArray(); #endif +#if defined (Q_WS_QPA) + QPlatformIntegration *integration = QApplicationPrivate::platformIntegration(); + if (integration) { //on shut down there will be some that we don't release. + integration->fontDatabase()->releaseHandle(pixelSizes[count].handle); + } +#endif } #endif free(pixelSizes); @@ -250,12 +277,13 @@ struct QtFontStyle bool smoothScalable : 1; signed int count : 30; QtFontSize *pixelSizes; + QString styleName; #ifdef Q_WS_X11 const char *weightName; const char *setwidthName; #endif // Q_WS_X11 -#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN) bool antialiased; #endif @@ -305,6 +333,9 @@ QtFontSize *QtFontStyle::pixelSize(unsigned short size, bool add) new (&pixelSizes[count].fileName) QByteArray; pixelSizes[count].fileIndex = 0; #endif +#if defined(Q_WS_QPA) + pixelSizes[count].handle = 0; +#endif return pixelSizes + (count++); } @@ -321,13 +352,20 @@ struct QtFontFoundry int count; QtFontStyle **styles; - QtFontStyle *style(const QtFontStyle::Key &, bool = false); + QtFontStyle *style(const QtFontStyle::Key &, const QString & = QString(), bool = false); }; -QtFontStyle *QtFontFoundry::style(const QtFontStyle::Key &key, bool create) +QtFontStyle *QtFontFoundry::style(const QtFontStyle::Key &key, const QString &styleName, bool create) { int pos = 0; if (count) { + // if styleName for searching first if possible + if (!styleName.isEmpty()) { + for (; pos < count; pos++) { + if (styles[pos]->styleName == styleName) + return styles[pos]; + } + } int low = 0; int high = count; pos = count / 2; @@ -354,6 +392,7 @@ QtFontStyle *QtFontFoundry::style(const QtFontStyle::Key &key, bool create) } QtFontStyle *style = new QtFontStyle(key); + style->styleName = styleName; memmove(styles + pos + 1, styles + pos, (count-pos)*sizeof(QtFontStyle *)); styles[pos] = style; count++; @@ -361,7 +400,7 @@ QtFontStyle *QtFontFoundry::style(const QtFontStyle::Key &key, bool create) } -struct QtFontFamily +struct QtFontFamily { enum WritingSystemStatus { Unknown = 0, @@ -387,9 +426,12 @@ struct QtFontFamily fixedPitchComputed(false), #endif name(n), count(0), foundries(0) -#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) && !defined(QT_NO_FREETYPE) +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN) && !defined(QT_NO_FREETYPE) , bogusWritingSystems(false) #endif +#if defined(Q_WS_QPA) + , askedForFallback(false) +#endif { memset(writingSystems, 0, sizeof(writingSystems)); } @@ -427,10 +469,13 @@ struct QtFontFamily int count; QtFontFoundry **foundries; -#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) && !defined(QT_NO_FREETYPE) +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN) && !defined(QT_NO_FREETYPE) bool bogusWritingSystems; QStringList fallbackFamilies; #endif +#if defined (Q_WS_QPA) + bool askedForFallback; +#endif unsigned char writingSystems[QFontDatabase::WritingSystemsCount]; QtFontFoundry *foundry(const QString &f, bool = false); @@ -474,7 +519,6 @@ QtFontFoundry *QtFontFamily::foundry(const QString &f, bool create) // ### copied to tools/makeqpf/qpf2.cpp -#if (defined(Q_WS_QWS) && !defined(QT_NO_FREETYPE)) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) || (defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA)) // see the Unicode subset bitfields in the MSDN docs static int requiredUnicodeBits[QFontDatabase::WritingSystemsCount][2] = { // Any, @@ -552,7 +596,7 @@ static int requiredUnicodeBits[QFontDatabase::WritingSystemsCount][2] = { #define JapaneseCsbBit 17 #define KoreanCsbBit 21 -static QList<QFontDatabase::WritingSystem> determineWritingSystemsFromTrueTypeBits(quint32 unicodeRange[4], quint32 codePageRange[2]) +QList<QFontDatabase::WritingSystem> qt_determine_writing_systems_from_truetype_bits(quint32 unicodeRange[4], quint32 codePageRange[2]) { QList<QFontDatabase::WritingSystem> writingSystems; bool hasScript = false; @@ -599,7 +643,6 @@ static QList<QFontDatabase::WritingSystem> determineWritingSystemsFromTrueTypeBi return writingSystems; } -#endif #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) // class with virtual destructor, derived in qfontdatabase_s60.cpp @@ -621,13 +664,24 @@ public: #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) , symbianExtras(0) #endif +#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECTWRITE) + , directWriteFactory(0) + , directWriteGdiInterop(0) +#endif { } + ~QFontDatabasePrivate() { free(); #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) if (symbianExtras) delete symbianExtras; #endif +#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECTWRITE) + if (directWriteGdiInterop) + directWriteGdiInterop->Release(); + if (directWriteFactory != 0) + directWriteFactory->Release(); +#endif } QtFontFamily *family(const QString &f, bool = false); void free() { @@ -640,8 +694,17 @@ public: } int count; +#if defined(Q_WS_X11) && !defined(QT_NO_FONTCONFIG) + QString systemLang; +#endif QtFontFamily **families; +#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECTWRITE) + IDWriteFactory *directWriteFactory; + IDWriteGdiInterop *directWriteGdiInterop; +#endif + + struct ApplicationFont { QString fileName; QByteArray data; @@ -669,7 +732,7 @@ public: bool loadFromCache(const QString &fontPath); void addQPF2File(const QByteArray &file); #endif // Q_WS_QWS -#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) && !defined(QT_NO_FREETYPE) +#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) && !defined(QT_NO_FREETYPE) void addFont(const QString &familyname, const char *foundryname, int weight, bool italic, int pixelSize, const QByteArray &file, int fileIndex, bool antialiased, @@ -680,10 +743,12 @@ public: #endif #if defined(Q_WS_QWS) QDataStream *stream; - QStringList fallbackFamilies; #elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) QSymbianFontDatabaseExtras *symbianExtras; #endif +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) + QStringList fallbackFamilies; +#endif }; void QFontDatabasePrivate::invalidate() @@ -732,7 +797,7 @@ QtFontFamily *QFontDatabasePrivate::family(const QString &f, bool create) return families[pos]; } -#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) && !defined(QT_NO_FREETYPE) +#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) && !defined(QT_NO_FREETYPE) void QFontDatabasePrivate::addFont(const QString &familyname, const char *foundryname, int weight, bool italic, int pixelSize, const QByteArray &file, int fileIndex, bool antialiased, const QList<QFontDatabase::WritingSystem> &writingSystems) @@ -756,7 +821,7 @@ void QFontDatabasePrivate::addFont(const QString &familyname, const char *foundr } QtFontFoundry *foundry = f->foundry(QString::fromLatin1(foundryname), true); - QtFontStyle *style = foundry->style(styleKey, true); + QtFontStyle *style = foundry->style(styleKey, QString(), true); style->smoothScalable = (pixelSize == 0); style->antialiased = antialiased; QtFontSize *size = style->pixelSize(pixelSize?pixelSize:SMOOTH_SCALABLE, true); @@ -821,13 +886,13 @@ QStringList QFontDatabasePrivate::addTTFile(const QByteArray &file, const QByteA TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); if (os2) { quint32 unicodeRange[4] = { - os2->ulUnicodeRange1, os2->ulUnicodeRange2, os2->ulUnicodeRange3, os2->ulUnicodeRange4 + static_cast<quint32>(os2->ulUnicodeRange1), static_cast<quint32>(os2->ulUnicodeRange2), static_cast<quint32>(os2->ulUnicodeRange3), static_cast<quint32>(os2->ulUnicodeRange4) }; quint32 codePageRange[2] = { - os2->ulCodePageRange1, os2->ulCodePageRange2 + static_cast<quint32>(os2->ulCodePageRange1), static_cast<quint32>(os2->ulCodePageRange2) }; - writingSystems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + writingSystems = qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange); //for (int i = 0; i < writingSystems.count(); ++i) // qDebug() << QFontDatabase::writingSystemName(writingSystems.at(i)); } @@ -890,6 +955,11 @@ static const int scriptForWritingSystem[] = { QUnicodeTables::Nko // Nko }; +int qt_script_for_writing_system(QFontDatabase::WritingSystem writingSystem) +{ + return scriptForWritingSystem[writingSystem]; +} + #if defined Q_WS_QWS || (defined(Q_WS_X11) && !defined(QT_NO_FONTCONFIG)) || defined(Q_WS_WIN) static inline bool requiresOpenType(int writingSystem) @@ -965,7 +1035,7 @@ static void match(int script, const QFontDef &request, const QString &family_name, const QString &foundry_name, int force_encoding_id, QtFontDesc *desc, const QList<int> &blacklistedFamilies = QList<int>(), bool forceXLFD=false); -#if defined(Q_WS_X11) || defined(Q_WS_QWS) +#if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_QPA) static void initFontDef(const QtFontDesc &desc, const QFontDef &request, QFontDef *fontDef) { fontDef->family = desc.family->name; @@ -994,7 +1064,7 @@ static void initFontDef(const QtFontDesc &desc, const QFontDef &request, QFontDe #endif #endif -#if defined(Q_WS_X11) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_X11) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) || defined(Q_WS_QPA) static void getEngineData(const QFontPrivate *d, const QFontCache::Key &key) { // look for the requested font in the engine data cache @@ -1055,12 +1125,22 @@ QT_BEGIN_INCLUDE_NAMESPACE # include "qfontdatabase_win.cpp" #elif defined(Q_WS_QWS) # include "qfontdatabase_qws.cpp" +#elif defined(Q_WS_QPA) +# include "qfontdatabase_qpa.cpp" #elif defined(Q_OS_SYMBIAN) # include "qfontdatabase_s60.cpp" #endif QT_END_INCLUDE_NAMESPACE -static QtFontStyle *bestStyle(QtFontFoundry *foundry, const QtFontStyle::Key &styleKey) +#if !defined(Q_WS_X11) +QString QFontDatabase::resolveFontFamilyAlias(const QString &family) +{ + return family; +} +#endif + +static QtFontStyle *bestStyle(QtFontFoundry *foundry, const QtFontStyle::Key &styleKey, + const QString &styleName = QString()) { int best = 0; int dist = 0xffff; @@ -1068,6 +1148,12 @@ static QtFontStyle *bestStyle(QtFontFoundry *foundry, const QtFontStyle::Key &st for ( int i = 0; i < foundry->count; i++ ) { QtFontStyle *style = foundry->styles[i]; + if (!styleName.isEmpty() && styleName == style->styleName) { + dist = 0; + best = i; + break; + } + int d = qAbs( styleKey.weight - style->key.weight ); if ( styleKey.stretch != 0 && style->key.stretch != 0 ) { @@ -1340,6 +1426,7 @@ static void match(int script, const QFontDef &request, styleKey.stretch = request.stretch; char pitch = request.ignorePitch ? '*' : request.fixedPitch ? 'm' : 'p'; + FM_DEBUG("QFontDatabase::match\n" " request:\n" " family: %s [%s], script: %d\n" @@ -1460,7 +1547,8 @@ static QString styleStringHelper(int weight, QFont::Style style) */ QString QFontDatabase::styleString(const QFont &font) { - return styleStringHelper(font.weight(), font.style()); + return font.styleName().isEmpty() ? styleStringHelper(font.weight(), font.style()) + : font.styleName(); } /*! @@ -1470,7 +1558,8 @@ QString QFontDatabase::styleString(const QFont &font) */ QString QFontDatabase::styleString(const QFontInfo &fontInfo) { - return styleStringHelper(fontInfo.weight(), fontInfo.style()); + return fontInfo.styleName().isEmpty() ? styleStringHelper(fontInfo.weight(), fontInfo.style()) + : fontInfo.styleName(); } @@ -1716,13 +1805,17 @@ QStringList QFontDatabase::styles(const QString &family) const for (int k = 0; k < foundry->count; k++) { QtFontStyle::Key ke(foundry->styles[k]->key); ke.stretch = 0; - allStyles.style(ke, true); + allStyles.style(ke, foundry->styles[k]->styleName, true); } } } - for (int i = 0; i < allStyles.count; i++) - l.append(styleStringHelper(allStyles.styles[i]->key.weight, (QFont::Style)allStyles.styles[i]->key.style)); + for (int i = 0; i < allStyles.count; i++) { + l.append(allStyles.styles[i]->styleName.isEmpty() ? + styleStringHelper(allStyles.styles[i]->key.weight, + (QFont::Style)allStyles.styles[i]->key.style) : + allStyles.styles[i]->styleName); + } return l; } @@ -1780,7 +1873,9 @@ bool QFontDatabase::isBitmapScalable(const QString &family, QtFontFoundry *foundry = f->foundries[j]; if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) { for (int k = 0; k < foundry->count; k++) - if ((style.isEmpty() || foundry->styles[k]->key == styleKey) + if ((style.isEmpty() || + foundry->styles[k]->styleName == style || + foundry->styles[k]->key == styleKey) && foundry->styles[k]->bitmapScalable && !foundry->styles[k]->smoothScalable) { bitmapScalable = true; goto end; @@ -1819,7 +1914,9 @@ bool QFontDatabase::isSmoothlyScalable(const QString &family, const QString &sty QtFontFoundry *foundry = f->foundries[j]; if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) { for (int k = 0; k < foundry->count; k++) - if ((style.isEmpty() || foundry->styles[k]->key == styleKey) && foundry->styles[k]->smoothScalable) { + if ((style.isEmpty() || + foundry->styles[k]->styleName == style || + foundry->styles[k]->key == styleKey) && foundry->styles[k]->smoothScalable) { smoothScalable = true; goto end; } @@ -1846,18 +1943,19 @@ bool QFontDatabase::isScalable(const QString &family, /*! - Returns a list of the point sizes available for the font that has - family \a family and style \a style. The list may be empty. + \fn QList<int> QFontDatabase::pointSizes(const QString &family, const QString &style) + Returns a list of the point sizes available for the font with the + given \a family and \a style. The list may be empty. \sa smoothSizes(), standardSizes() */ QList<int> QFontDatabase::pointSizes(const QString &family, - const QString &style) + const QString &styleName) { #if defined(Q_WS_WIN) // windows and macosx are always smoothly scalable Q_UNUSED(family); - Q_UNUSED(style); + Q_UNUSED(styleName); return standardSizes(); #else bool smoothScalable = false; @@ -1868,7 +1966,7 @@ QList<int> QFontDatabase::pointSizes(const QString &family, QT_PREPEND_NAMESPACE(load)(familyName); - QtFontStyle::Key styleKey(style); + QtFontStyle::Key styleKey(styleName); QList<int> sizes; @@ -1885,7 +1983,7 @@ QList<int> QFontDatabase::pointSizes(const QString &family, for (int j = 0; j < fam->count; j++) { QtFontFoundry *foundry = fam->foundries[j]; if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) { - QtFontStyle *style = foundry->style(styleKey); + QtFontStyle *style = foundry->style(styleKey, styleName); if (!style) continue; if (style->smoothScalable) { @@ -1936,35 +2034,39 @@ QFont QFontDatabase::font(const QString &family, const QString &style, QtFontFoundry *foundry = f->foundries[j]; if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) { for (int k = 0; k < foundry->count; k++) - allStyles.style(foundry->styles[k]->key, true); + allStyles.style(foundry->styles[k]->key, foundry->styles[k]->styleName, true); } } QtFontStyle::Key styleKey(style); - QtFontStyle *s = bestStyle(&allStyles, styleKey); + QtFontStyle *s = bestStyle(&allStyles, styleKey, style); if (!s) // no styles found? return QApplication::font(); + QFont fnt(family, pointSize, s->key.weight); fnt.setStyle((QFont::Style)s->key.style); + if (!s->styleName.isEmpty()) + fnt.setStyleName(s->styleName); return fnt; } /*! - Returns the point sizes of a font that has family \a family and - style \a style that will look attractive. The list may be empty. + \fn QList<int> QFontDatabase::smoothSizes(const QString &family, const QString &style) + Returns the point sizes of a font with the given \a family and \a style + that will look attractive. The list may be empty. For non-scalable fonts and bitmap scalable fonts, this function is equivalent to pointSizes(). \sa pointSizes(), standardSizes() */ QList<int> QFontDatabase::smoothSizes(const QString &family, - const QString &style) + const QString &styleName) { #ifdef Q_WS_WIN Q_UNUSED(family); - Q_UNUSED(style); + Q_UNUSED(styleName); return QFontDatabase::standardSizes(); #else bool smoothScalable = false; @@ -1975,7 +2077,7 @@ QList<int> QFontDatabase::smoothSizes(const QString &family, QT_PREPEND_NAMESPACE(load)(familyName); - QtFontStyle::Key styleKey(style); + QtFontStyle::Key styleKey(styleName); QList<int> sizes; @@ -1992,7 +2094,7 @@ QList<int> QFontDatabase::smoothSizes(const QString &family, for (int j = 0; j < fam->count; j++) { QtFontFoundry *foundry = fam->foundries[j]; if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) { - QtFontStyle *style = foundry->style(styleKey); + QtFontStyle *style = foundry->style(styleKey, styleName); if (!style) continue; if (style->smoothScalable) { @@ -2059,12 +2161,12 @@ bool QFontDatabase::italic(const QString &family, const QString &style) const QtFontFoundry *foundry = f->foundries[j]; if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) { for (int k = 0; k < foundry->count; k++) - allStyles.style(foundry->styles[k]->key, true); + allStyles.style(foundry->styles[k]->key, foundry->styles[k]->styleName, true); } } QtFontStyle::Key styleKey(style); - QtFontStyle *s = allStyles.style(styleKey); + QtFontStyle *s = allStyles.style(styleKey, style); return s && s->key.style == QFont::StyleItalic; } @@ -2094,12 +2196,12 @@ bool QFontDatabase::bold(const QString &family, if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) { for (int k = 0; k < foundry->count; k++) - allStyles.style(foundry->styles[k]->key, true); + allStyles.style(foundry->styles[k]->key, foundry->styles[k]->styleName, true); } } QtFontStyle::Key styleKey(style); - QtFontStyle *s = allStyles.style(styleKey); + QtFontStyle *s = allStyles.style(styleKey, style); return s && s->key.weight >= QFont::Bold; } @@ -2130,16 +2232,26 @@ int QFontDatabase::weight(const QString &family, if (foundryName.isEmpty() || foundry->name.compare(foundryName, Qt::CaseInsensitive) == 0) { for (int k = 0; k < foundry->count; k++) - allStyles.style(foundry->styles[k]->key, true); + allStyles.style(foundry->styles[k]->key, foundry->styles[k]->styleName, true); } } QtFontStyle::Key styleKey(style); - QtFontStyle *s = allStyles.style(styleKey); + QtFontStyle *s = allStyles.style(styleKey, style); return s ? s->key.weight : -1; } +/*! \internal */ +bool QFontDatabase::hasFamily(const QString &family) const +{ + QString parsedFamily, foundry; + parseFontName(family, foundry, parsedFamily); + const QString familyAlias = resolveFontFamilyAlias(parsedFamily); + return families().contains(familyAlias, Qt::CaseInsensitive); +} + + /*! Returns the names the \a writingSystem (e.g. for displaying to the user in a dialog). @@ -2429,10 +2541,12 @@ QString QFontDatabase::writingSystemSample(WritingSystem writingSystem) sample += QChar(0x4f8b); break; case Japanese: - sample += QChar(0x3050); - sample += QChar(0x3060); - sample += QChar(0x30b0); - sample += QChar(0x30c0); + sample += QChar(0x30b5); + sample += QChar(0x30f3); + sample += QChar(0x30d7); + sample += QChar(0x30eb); + sample += QChar(0x3067); + sample += QChar(0x3059); break; case Korean: sample += QChar(0xac00); @@ -2485,7 +2599,7 @@ void QFontDatabase::createDatabase() { initializeDb(); } // used from qfontengine_ft.cpp -QByteArray qt_fontdata_from_index(int index) +Q_GUI_EXPORT QByteArray qt_fontdata_from_index(int index) { QMutexLocker locker(fontDatabaseMutex()); return privateDb()->applicationFonts.value(index).data; diff --git a/src/gui/text/qfontdatabase.h b/src/gui/text/qfontdatabase.h index 120c1e7..b1f370e 100644 --- a/src/gui/text/qfontdatabase.h +++ b/src/gui/text/qfontdatabase.h @@ -138,6 +138,8 @@ public: bool bold(const QString &family, const QString &style) const; int weight(const QString &family, const QString &style) const; + bool hasFamily(const QString &family) const; + static QString writingSystemName(WritingSystem writingSystem); static QString writingSystemSample(WritingSystem writingSystem); @@ -152,7 +154,8 @@ public: private: static void createDatabase(); static void parseFontName(const QString &name, QString &foundry, QString &family); -#if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) + static QString resolveFontFamilyAlias(const QString &family); +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN) static QFontEngine *findFont(int script, const QFontPrivate *fp, const QFontDef &request); #endif static void load(const QFontPrivate *d, int script); @@ -167,6 +170,7 @@ private: friend class QFontEngineMultiXLFD; friend class QFontEngineMultiQWS; friend class QFontEngineMultiS60; + friend class QFontEngineMultiQPA; QFontDatabasePrivate *d; }; diff --git a/src/gui/text/qfontdatabase_mac.cpp b/src/gui/text/qfontdatabase_mac.cpp index 85fb104..fc8247d 100644 --- a/src/gui/text/qfontdatabase_mac.cpp +++ b/src/gui/text/qfontdatabase_mac.cpp @@ -45,6 +45,8 @@ #include <qabstractfileengine.h> #include <stdlib.h> #include <qendian.h> +#include <private/qfontengine_coretext_p.h> +#include <private/qfontengine_mac_p.h> QT_BEGIN_NAMESPACE @@ -70,7 +72,7 @@ static void initWritingSystems(QtFontFamily *family, ATSFontRef atsFont) qFromBigEndian<quint32>(os2Table.data() + 54) }; quint32 codePageRange[2] = { qFromBigEndian<quint32>(os2Table.data() + 78), qFromBigEndian<quint32>(os2Table.data() + 82) }; - QList<QFontDatabase::WritingSystem> systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + QList<QFontDatabase::WritingSystem> systems = qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange); #if 0 QCFString name; ATSFontGetName(atsFont, kATSOptionFlagsDefault, &name); @@ -104,12 +106,14 @@ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fonts, i); QCFString family_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute); + QCFString style_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute); QtFontFamily *family = db->family(family_name, true); for(int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) family->writingSystems[ws] = QtFontFamily::Supported; QtFontFoundry *foundry = family->foundry(foundry_name, true); QtFontStyle::Key styleKey; + QString styleName = style_name; if(QCFType<CFDictionaryRef> styles = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute)) { if(CFNumberRef weight = (CFNumberRef)CFDictionaryGetValue(styles, kCTFontWeightTrait)) { Q_ASSERT(CFNumberIsFloatType(weight)); @@ -130,7 +134,7 @@ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { } } - QtFontStyle *style = foundry->style(styleKey, true); + QtFontStyle *style = foundry->style(styleKey, styleName, true); style->smoothScalable = true; if(QCFType<CFNumberRef> size = (CFNumberRef)CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) { //qDebug() << "WHEE"; @@ -203,7 +207,7 @@ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { QtFontFamily *family = db->family(familyName, true); QtFontFoundry *foundry = family->foundry(QString(), true); - QtFontStyle *style = foundry->style(styleKey, true); + QtFontStyle *style = foundry->style(styleKey, QString(), true); style->pixelSize(0, true); style->smoothScalable = true; @@ -242,6 +246,68 @@ static const char *styleHint(const QFontDef &request) return stylehint; } +static inline float weightToFloat(unsigned int weight) +{ + return (weight - 50) / 100.0; +} + +static QFontEngine *loadFromDatabase(const QFontDef &req, const QFontPrivate *d) +{ +#if defined(QT_MAC_USE_COCOA) + QCFString fontName = NULL; +#else + ATSFontFamilyRef familyRef = 0; + ATSFontRef fontRef = 0; +#endif + + QStringList family_list = familyList(req); + + const char *stylehint = styleHint(req); + if (stylehint) + family_list << QLatin1String(stylehint); + + // add QFont::defaultFamily() to the list, for compatibility with previous versions + family_list << QApplication::font().defaultFamily(); + + QMutexLocker locker(fontDatabaseMutex()); + QFontDatabasePrivate *db = privateDb(); + if (!db->count) + initializeDb(); + for (int i = 0; i < family_list.size(); ++i) { + for (int k = 0; k < db->count; ++k) { + if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) { + QByteArray family_name = db->families[k]->name.toUtf8(); +#if defined(QT_MAC_USE_COCOA) + QCFType<CTFontRef> ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL); + if (ctFont) { + fontName = CTFontCopyFullName(ctFont); + goto found; + } +#else + familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); + if (familyRef) { + fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); + goto found; + } +#endif + } + } + } +found: +#ifdef QT_MAC_USE_COCOA + if (fontName) + return new QCoreTextFontEngineMulti(fontName, req, d->kerning); +#else + if (familyRef) { + QCFString actualName; + if (ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr) + req.family = actualName; + return new QFontEngineMacMulti(familyRef, req, fontDef, d->kerning); + } +#endif + return NULL; +} + void QFontDatabase::load(const QFontPrivate *d, int script) { // sanity checks @@ -282,145 +348,38 @@ void QFontDatabase::load(const QFontPrivate *d, int script) return; // the font info and fontdef should already be filled } - //find the font - QStringList family_list = familyList(req); - - const char *stylehint = styleHint(req); - if (stylehint) - family_list << QLatin1String(stylehint); - - // add QFont::defaultFamily() to the list, for compatibility with - // previous versions - family_list << QApplication::font().defaultFamily(); - - ATSFontFamilyRef familyRef = 0; - ATSFontRef fontRef = 0; - - QMutexLocker locker(fontDatabaseMutex()); - QFontDatabasePrivate *db = privateDb(); - if (!db->count) - initializeDb(); - for(int i = 0; i < family_list.size(); ++i) { - for (int k = 0; k < db->count; ++k) { - if (db->families[k]->name.compare(family_list.at(i), Qt::CaseInsensitive) == 0) { - QByteArray family_name = db->families[k]->name.toUtf8(); - familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); - if (familyRef) { - fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); - goto FamilyFound; - } else { + QFontEngine *engine = NULL; #if defined(QT_MAC_USE_COCOA) - // ATS and CT disagrees on what the family name should be, - // use CT to look up the font if ATS fails. - QCFString familyName = QString::fromAscii(family_name); - QCFType<CTFontRef> CTfontRef = CTFontCreateWithName(familyName, 12, NULL); - QCFType<CTFontDescriptorRef> fontDescriptor = CTFontCopyFontDescriptor(CTfontRef); - QCFString displayName = (CFStringRef)CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontDisplayNameAttribute); - - familyRef = ATSFontFamilyFindFromName(displayName, kATSOptionFlagsDefault); - if (familyRef) { - fontRef = ATSFontFindFromName(displayName, kATSOptionFlagsDefault); - goto FamilyFound; - } -#endif - } + // Shortcut to get the font directly without going through the font database + if (!req.family.isEmpty() && !req.styleName.isEmpty()) { + QCFString expectedFamily = QCFString(req.family); + QCFString expectedStyle = QCFString(req.styleName); + + QCFType<CFMutableDictionaryRef> attributes = CFDictionaryCreateMutable(NULL, 0, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, expectedFamily); + CFDictionaryAddValue(attributes, kCTFontStyleNameAttribute, expectedStyle); + + QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithAttributes(attributes); + CGAffineTransform transform = qt_transform_from_fontdef(req); + QCFType<CTFontRef> ctFont = CTFontCreateWithFontDescriptor(descriptor, req.pixelSize, &transform); + if (ctFont) { + QCFString familyName = CTFontCopyFamilyName(ctFont); + // Only accept the font if the family name is exactly the same as we specified + if (CFEqual(expectedFamily, familyName)) { + engine = new QCoreTextFontEngineMulti(ctFont, req, d->kerning); } } } -FamilyFound: - //fill in the engine's font definition - QFontDef fontDef = d->request; //copy.. - if(fontDef.pointSize < 0) - fontDef.pointSize = qt_mac_pointsize(fontDef, d->dpi); - else - fontDef.pixelSize = qt_mac_pixelsize(fontDef, d->dpi); -#if 0 - ItemCount name_count; - if(ATSUCountFontNames(fontID, &name_count) == noErr && name_count) { - ItemCount actualName_size; - if(ATSUGetIndFontName(fontID, 0, 0, 0, &actualName_size, 0, 0, 0, 0) == noErr && actualName_size) { - QByteArray actualName(actualName_size); - if(ATSUGetIndFontName(fontID, 0, actualName_size, actualName.data(), &actualName_size, 0, 0, 0, 0) == noErr && actualName_size) - fontDef.family = QString::fromUtf8(actualName); - } - } -#else - { - QCFString actualName; - if(ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr) - fontDef.family = actualName; - } #endif + if (!engine) + engine = loadFromDatabase(req, d); -#ifdef QT_MAC_USE_COCOA - QFontEngine *engine = new QCoreTextFontEngineMulti(familyRef, fontRef, fontDef, d->kerning); -#elif 1 - QFontEngine *engine = new QFontEngineMacMulti(familyRef, fontRef, fontDef, d->kerning); -#else - ATSFontFamilyRef atsFamily = familyRef; - ATSFontFamilyRef atsFontRef = fontRef; - - FMFont fontID; - FMFontFamily fmFamily; - FMFontStyle fntStyle = 0; - fmFamily = FMGetFontFamilyFromATSFontFamilyRef(atsFamily); - if (fmFamily == kInvalidFontFamily) { - // Use the ATSFont then... - fontID = FMGetFontFromATSFontRef(atsFontRef); - } else { - if (fontDef.weight >= QFont::Bold) - fntStyle |= ::bold; - if (fontDef.style != QFont::StyleNormal) - fntStyle |= ::italic; - - FMFontStyle intrinsicStyle; - FMFont fnt = 0; - if (FMGetFontFromFontFamilyInstance(fmFamily, fntStyle, &fnt, &intrinsicStyle) == noErr) - fontID = FMGetATSFontRefFromFont(fnt); - } - - OSStatus status; - - const int maxAttributeCount = 5; - ATSUAttributeTag tags[maxAttributeCount + 1]; - ByteCount sizes[maxAttributeCount + 1]; - ATSUAttributeValuePtr values[maxAttributeCount + 1]; - int attributeCount = 0; - - Fixed size = FixRatio(fontDef.pixelSize, 1); - tags[attributeCount] = kATSUSizeTag; - sizes[attributeCount] = sizeof(size); - values[attributeCount] = &size; - ++attributeCount; - - tags[attributeCount] = kATSUFontTag; - sizes[attributeCount] = sizeof(fontID); - values[attributeCount] = &fontID; - ++attributeCount; - - CGAffineTransform transform = CGAffineTransformIdentity; - if (fontDef.stretch != 100) { - transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); - tags[attributeCount] = kATSUFontMatrixTag; - sizes[attributeCount] = sizeof(transform); - values[attributeCount] = &transform; - ++attributeCount; + if (engine) { + d->engineData->engine = engine; + engine->ref.ref(); + QFontCache::instance()->insertEngine(key, engine); } - - ATSUStyle style; - status = ATSUCreateStyle(&style); - Q_ASSERT(status == noErr); - - Q_ASSERT(attributeCount < maxAttributeCount + 1); - status = ATSUSetAttributes(style, attributeCount, tags, sizes, values); - Q_ASSERT(status == noErr); - - QFontEngine *engine = new QFontEngineMac(style, fontID, fontDef, /*multiEngine*/ 0); - ATSUDisposeStyle(style); -#endif - d->engineData->engine = engine; - engine->ref.ref(); //a ref for the engineData->engine - QFontCache::instance()->insertEngine(key, engine); } static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) @@ -431,7 +390,6 @@ static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) if(fnt->data.isEmpty()) { #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { - extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp FSRef ref; if(qt_mac_create_fsref(fnt->fileName, &ref) != noErr) return; @@ -441,7 +399,6 @@ static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) #endif { #ifndef Q_WS_MAC64 - extern Q_CORE_EXPORT OSErr qt_mac_create_fsspec(const QString &, FSSpec *); // global.cpp FSSpec spec; if(qt_mac_create_fsspec(fnt->fileName, &spec) != noErr) return; diff --git a/src/gui/text/qfontdatabase_qpa.cpp b/src/gui/text/qfontdatabase_qpa.cpp new file mode 100644 index 0000000..6b6f4f1 --- /dev/null +++ b/src/gui/text/qfontdatabase_qpa.cpp @@ -0,0 +1,394 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlibraryinfo.h" +#include <QtCore/qsettings.h> + +#include "qfontengine_qpa_p.h" +#include "qplatformdefs.h" + +#include <QtGui/private/qapplication_p.h> +#include <QtGui/qplatformfontdatabase_qpa.h> + +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +Q_GUI_EXPORT void qt_registerFont(const QString &familyName, const QString &foundryname, int weight, + QFont::Style style, int stretch, bool antialiased, bool scalable, int pixelSize, + const QSupportedWritingSystems &writingSystems, void *handle) +{ + QFontDatabasePrivate *d = privateDb(); + // qDebug() << "Adding font" << familyname << weight << italic << pixelSize << file << fileIndex << antialiased; + QtFontStyle::Key styleKey; + styleKey.style = style; + styleKey.weight = weight; + styleKey.stretch = stretch; + QtFontFamily *f = d->family(familyName, true); + + for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) { + if (writingSystems.supported(QFontDatabase::WritingSystem(i))) { + f->writingSystems[i] = QtFontFamily::Supported; + } else { + f->writingSystems[i] = QtFontFamily::Unsupported; + } + } + + QtFontFoundry *foundry = f->foundry(foundryname, true); + QtFontStyle *fontStyle = foundry->style(styleKey, QString(), true); + fontStyle->smoothScalable = scalable; + fontStyle->antialiased = antialiased; + QtFontSize *size = fontStyle->pixelSize(pixelSize?pixelSize:SMOOTH_SCALABLE, true); + size->handle = handle; +} + +static QStringList fallbackFamilies(const QString &family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) +{ + QStringList retList = QApplicationPrivate::platformIntegration()->fontDatabase()->fallbacksForFamily(family,style,styleHint,script); + QFontDatabasePrivate *db = privateDb(); + + QStringList::iterator i; + for (i = retList.begin(); i != retList.end(); ++i) { + bool contains = false; + for (int j = 0; j < db->count; j++) { + QtFontFamily *qtFamily = db->families[j]; + if (!(i->compare(qtFamily->name,Qt::CaseInsensitive))) { + contains = true; + break; + } + } + if (!contains) { + i = retList.erase(i); + i--; + } + } + return retList; +} + +static void initializeDb() +{ + static int initialized = false; + + if (!initialized) { + //init by asking for the platformfontdb for the first time :) + QApplicationPrivate::platformIntegration()->fontDatabase()->populateFontDatabase(); + initialized = true; + } +} + +#ifndef QT_NO_SETTINGS +// called from qapplication_qws.cpp +void qt_applyFontDatabaseSettings(const QSettings &settings) +{ + initializeDb(); + QFontDatabasePrivate *db = privateDb(); + for (int i = 0; i < db->count; ++i) { + QtFontFamily *family = db->families[i]; + if (settings.contains(family->name)) + family->fallbackFamilies = settings.value(family->name).toStringList(); + } + + if (settings.contains(QLatin1String("Global Fallbacks"))) + db->fallbackFamilies = settings.value(QLatin1String("Global Fallbacks")).toStringList(); +} +#endif // QT_NO_SETTINGS + +static inline void load(const QString & = QString(), int = -1) +{ + initializeDb(); +} + +static +QFontEngine *loadSingleEngine(int script, + const QFontDef &request, + QtFontFoundry *foundry, + QtFontStyle *style, QtFontSize *size) +{ + Q_UNUSED(foundry); + + Q_ASSERT(size); + int pixelSize = size->pixelSize; + if (!pixelSize || (style->smoothScalable && pixelSize == SMOOTH_SCALABLE)) + pixelSize = request.pixelSize; + + QFontDef def = request; + def.pixelSize = pixelSize; + + QFontCache::Key key(def,script); + QFontEngine *engine = QFontCache::instance()->findEngine(key); + if (!engine) { + QPlatformFontDatabase *pfdb = QApplicationPrivate::platformIntegration()->fontDatabase(); + engine = pfdb->fontEngine(def,QUnicodeTables::Script(script),size->handle); + if (engine) { + QFontCache::Key key(def,script); + QFontCache::instance()->instance()->insertEngine(key,engine); + } + } + return engine; +} + +static +QFontEngine *loadEngine(int script, const QFontDef &request, + QtFontFamily *family, QtFontFoundry *foundry, + QtFontStyle *style, QtFontSize *size) +{ + + QFontEngine *engine = loadSingleEngine(script, request, foundry, style, size); + //make sure that the db has all fallback families + if (engine + && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol ) { + + if (family && !family->askedForFallback) { + QFont::Style fontStyle = QFont::Style(style->key.style); + QFont::StyleHint styleHint = QFont::StyleHint(request.styleHint); + if (styleHint == QFont::AnyStyle && request.fixedPitch) + styleHint = QFont::TypeWriter; + family->fallbackFamilies = fallbackFamilies(family->name,fontStyle,styleHint,QUnicodeTables::Script(script)); + + family->askedForFallback = true; + } + + QStringList fallbacks = privateDb()->fallbackFamilies; + if (family && !family->fallbackFamilies.isEmpty()) + fallbacks = family->fallbackFamilies; + + engine = new QFontEngineMultiQPA(engine, script, fallbacks); + } + + return engine; +} + +static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) +{ + QFontDatabasePrivate *db = privateDb(); + + fnt->families = QApplicationPrivate::platformIntegration()->fontDatabase()->addApplicationFont(fnt->data,fnt->fileName); + + db->reregisterAppFonts = true; +} + +bool QFontDatabase::removeApplicationFont(int handle) +{ + QMutexLocker locker(fontDatabaseMutex()); + + QFontDatabasePrivate *db = privateDb(); + if (handle < 0 || handle >= db->applicationFonts.count()) + return false; + + db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont(); + + db->reregisterAppFonts = true; + db->invalidate(); + return true; +} + +bool QFontDatabase::removeAllApplicationFonts() +{ + QMutexLocker locker(fontDatabaseMutex()); + + QFontDatabasePrivate *db = privateDb(); + if (db->applicationFonts.isEmpty()) + return false; + + db->applicationFonts.clear(); + db->invalidate(); + return true; +} + +bool QFontDatabase::supportsThreadedFontRendering() +{ + return true; +} + +/*! + \internal +*/ +QFontEngine * +QFontDatabase::findFont(int script, const QFontPrivate *fp, + const QFontDef &request) +{ + QMutexLocker locker(fontDatabaseMutex()); + + const int force_encoding_id = -1; + + if (!privateDb()->count) + initializeDb(); + + QFontEngine *engine; + QFontCache::Key key(request, script); + engine = QFontCache::instance()->findEngine(key); + if (engine) { + qDebug() << "Cache hit level 1"; + return engine; + } + + QString family_name, foundry_name; + + parseFontName(request.family, foundry_name, family_name); + + if (qt_enable_test_font && request.family == QLatin1String("__Qt__Box__Engine__")) { + engine =new QTestFontEngine(request.pixelSize); + engine->fontDef = request; + } + + QtFontDesc desc; + match(script, request, family_name, foundry_name, force_encoding_id, &desc); + if (desc.family != 0 && desc.foundry != 0 && desc.style != 0) { + engine = loadEngine(script, request, desc.family, desc.foundry, desc.style, desc.size); + } else { + FM_DEBUG(" NO MATCH FOUND\n"); + } + + if (engine) { + initFontDef(desc, request, &engine->fontDef); + + if (fp) { + QFontDef def = request; + if (def.family.isEmpty()) { + def.family = fp->request.family; + def.family = def.family.left(def.family.indexOf(QLatin1Char(','))); + } + } + } + + if (!engine) { + if (!request.family.isEmpty()) { + QStringList fallbacks = fallbackFamilies(request.family,QFont::Style(request.style),QFont::StyleHint(request.styleHint),QUnicodeTables::Script(script)); + for (int i = 0; i < fallbacks.size(); i++) { + QFontDef def = request; + def.family = fallbacks.at(i); + QFontCache::Key key(def,script); + engine = QFontCache::instance()->findEngine(key); + if (!engine) { + QtFontDesc desc; + match(script, def, def.family, QLatin1String(""), 0, &desc); + if (desc.family == 0 && desc.foundry == 0 && desc.style == 0) { + continue; + } + engine = loadEngine(script, def, desc.family, desc.foundry, desc.style, desc.size); + if (engine) { + initFontDef(desc, def, &engine->fontDef); + break; + } + } + } + } + + if (!engine) + engine = new QFontEngineBox(request.pixelSize); + + FM_DEBUG("returning box engine"); + } + + if (fp && fp->dpi > 0) { + engine->fontDef.pointSize = qreal(double((engine->fontDef.pixelSize * 72) / fp->dpi)); + } else { + engine->fontDef.pointSize = request.pointSize; + } + + return engine; +} + +void QFontDatabase::load(const QFontPrivate *d, int script) +{ + QFontDef req = d->request; + + if (req.pixelSize == -1) { + req.pixelSize = floor(((req.pointSize * d->dpi) / 72) * 100 + 0.5) / 100; + req.pixelSize = qRound(req.pixelSize); + } + if (req.pointSize < 0) + req.pointSize = req.pixelSize*72.0/d->dpi; + if (req.weight == 0) + req.weight = QFont::Normal; + if (req.stretch == 0) + req.stretch = 100; + + QFontCache::Key key(req, script); + + if (!d->engineData) + getEngineData(d, key); + + // the cached engineData could have already loaded the engine we want + if (d->engineData->engines[script]) + return; + + QFontEngine *fe = QFontCache::instance()->findEngine(key); + + // list of families to try + QStringList family_list; + + if (!req.family.isEmpty()) { + family_list = familyList(req); + + // add the default family + QString defaultFamily = QApplication::font().family(); + if (! family_list.contains(defaultFamily)) + family_list << defaultFamily; + + } + + // null family means find the first font matching the specified script + family_list << QString(); + + QStringList::ConstIterator it = family_list.constBegin(), end = family_list.constEnd(); + for (; !fe && it != end; ++it) { + req.family = *it; + + fe = QFontDatabase::findFont(script, d, req); + if (fe && (fe->type()==QFontEngine::Box) && !req.family.isEmpty()) + fe = 0; + } + + if (fe->symbol || (d->request.styleStrategy & QFont::NoFontMerging)) { + for (int i = 0; i < QUnicodeTables::ScriptCount; ++i) { + if (!d->engineData->engines[i]) { + d->engineData->engines[i] = fe; + fe->ref.ref(); + } + } + } else { + d->engineData->engines[script] = fe; + fe->ref.ref(); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/text/qfontdatabase_qws.cpp b/src/gui/text/qfontdatabase_qws.cpp index 3ad784e..313000f 100644 --- a/src/gui/text/qfontdatabase_qws.cpp +++ b/src/gui/text/qfontdatabase_qws.cpp @@ -40,8 +40,10 @@ ****************************************************************************/ #include "qdir.h" +#if defined(Q_WS_QWS) #include "qscreen_qws.h" //so we can check for rotation #include "qwindowsystem_qws.h" +#endif #include "qlibraryinfo.h" #include "qabstractfileengine.h" #include <QtCore/qsettings.h> @@ -73,6 +75,11 @@ #include <qresource.h> #endif +#ifdef Q_OS_QNX +// ### using QFontEngineQPF leads to artifacts on QNX +# define QT_NO_QWS_SHARE_FONTS +#endif + QT_BEGIN_NAMESPACE #ifndef QT_NO_LIBRARY @@ -153,7 +160,11 @@ extern QString qws_fontCacheDir(); #ifndef QT_FONTS_ARE_RESOURCES bool QFontDatabasePrivate::loadFromCache(const QString &fontPath) { +#ifdef Q_WS_QWS const bool weAreTheServer = QWSServer::instance(); +#else + const bool weAreTheServer = true; // assume single-process +#endif QString fontDirFile = fontPath + QLatin1String("/fontdir"); @@ -382,12 +393,14 @@ static void initializeDb() } #endif +#ifndef QT_NO_QWS_QPF2 QDir dir(fontpath, QLatin1String("*.qpf2")); for (int i = 0; i < int(dir.count()); ++i) { const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); //qDebug() << "looking at" << file; db->addQPF2File(file); } +#endif #endif //QT_FONTS_ARE_RESOURCES @@ -688,6 +701,7 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, { QScopedPointer<QFontEngine> engine(loadSingleEngine(script, fp, request, family, foundry, style, size)); +#ifndef QT_NO_QWS_QPF if (!engine.isNull() && script == QUnicodeTables::Common && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol) { @@ -701,6 +715,7 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, engine.take(); engine.reset(fe); } +#endif return engine.take(); } @@ -749,9 +764,6 @@ bool QFontDatabase::supportsThreadedFontRendering() return true; } -/*! - \internal -*/ QFontEngine * QFontDatabase::findFont(int script, const QFontPrivate *fp, const QFontDef &request) diff --git a/src/gui/text/qfontdatabase_s60.cpp b/src/gui/text/qfontdatabase_s60.cpp index d209726..ffa4e59 100644 --- a/src/gui/text/qfontdatabase_s60.cpp +++ b/src/gui/text/qfontdatabase_s60.cpp @@ -486,6 +486,7 @@ void QFontEngineMultiS60::loadEngine(int at) Q_ASSERT(engines[at]); } +#ifdef QT_NO_FREETYPE static bool registerScreenDeviceFont(int screenDeviceFontIndex, const QSymbianFontDatabaseExtrasImplementation *dbExtras) { @@ -524,7 +525,7 @@ static bool registerScreenDeviceFont(int screenDeviceFontIndex, QtFontFamily *family = privateDb()->family(familyName, true); family->fixedPitch = faceAttrib.IsMonoWidth(); QtFontFoundry *foundry = family->foundry(QString(), true); - QtFontStyle *style = foundry->style(styleKey, true); + QtFontStyle *style = foundry->style(styleKey, QString(), true); style->smoothScalable = typefaceSupport.iIsScalable; style->pixelSize(0, true); @@ -545,11 +546,12 @@ static bool registerScreenDeviceFont(int screenDeviceFontIndex, qFromBigEndian<quint32>(ulCodePageRange + 4) }; const QList<QFontDatabase::WritingSystem> writingSystems = - determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange); foreach (const QFontDatabase::WritingSystem system, writingSystems) family->writingSystems[system] = QtFontFamily::Supported; return true; } +#endif static void initializeDb() { diff --git a/src/gui/text/qfontdatabase_win.cpp b/src/gui/text/qfontdatabase_win.cpp index 9bd26e5..788eb30 100644 --- a/src/gui/text/qfontdatabase_win.cpp +++ b/src/gui/text/qfontdatabase_win.cpp @@ -49,6 +49,11 @@ #include "qabstractfileengine.h" #include "qendian.h" +#if !defined(QT_NO_DIRECTWRITE) +# include "qsettings.h" +# include "qfontenginedirectwrite_p.h" +#endif + #ifdef Q_OS_WINCE # include <QTemporaryFile> #endif @@ -237,6 +242,8 @@ error: return i18n_name; } +extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp + static void addFontToDatabase(QString familyName, const QString &scriptName, TEXTMETRIC *textmetric, @@ -269,16 +276,7 @@ void addFontToDatabase(QString familyName, const QString &scriptName, if (familyName[0] != QLatin1Char('@') && !familyName.startsWith(QLatin1String("WST_"))) { QtFontStyle::Key styleKey; styleKey.style = italic ? QFont::StyleItalic : QFont::StyleNormal; - if (weight < 400) - styleKey.weight = QFont::Light; - else if (weight < 600) - styleKey.weight = QFont::Normal; - else if (weight < 700) - styleKey.weight = QFont::DemiBold; - else if (weight < 800) - styleKey.weight = QFont::Bold; - else - styleKey.weight = QFont::Black; + styleKey.weight = weightFromInteger(weight); QtFontFamily *family = privateDb()->family(familyName, true); @@ -286,7 +284,7 @@ void addFontToDatabase(QString familyName, const QString &scriptName, family->english_name = getEnglishName(familyName); QtFontFoundry *foundry = family->foundry(foundryName, true); - QtFontStyle *style = foundry->style(styleKey, true); + QtFontStyle *style = foundry->style(styleKey, QString(), true); style->smoothScalable = scalable; style->pixelSize( size, TRUE); @@ -294,14 +292,14 @@ void addFontToDatabase(QString familyName, const QString &scriptName, if (styleKey.weight <= QFont::DemiBold) { QtFontStyle::Key key(styleKey); key.weight = QFont::Bold; - QtFontStyle *style = foundry->style(key, true); + QtFontStyle *style = foundry->style(key, QString(), true); style->smoothScalable = scalable; style->pixelSize( size, TRUE); } if (styleKey.style != QFont::StyleItalic) { QtFontStyle::Key key(styleKey); key.style = QFont::StyleItalic; - QtFontStyle *style = foundry->style(key, true); + QtFontStyle *style = foundry->style(key, QString(), true); style->smoothScalable = scalable; style->pixelSize( size, TRUE); } @@ -309,7 +307,7 @@ void addFontToDatabase(QString familyName, const QString &scriptName, QtFontStyle::Key key(styleKey); key.weight = QFont::Bold; key.style = QFont::StyleItalic; - QtFontStyle *style = foundry->style(key, true); + QtFontStyle *style = foundry->style(key, QString(), true); style->smoothScalable = scalable; style->pixelSize( size, TRUE); } @@ -335,7 +333,7 @@ void addFontToDatabase(QString familyName, const QString &scriptName, quint32 codePageRange[2] = { signature->fsCsb[0], signature->fsCsb[1] }; - QList<QFontDatabase::WritingSystem> systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + QList<QFontDatabase::WritingSystem> systems = qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange); for (int i = 0; i < systems.count(); ++i) { QFontDatabase::WritingSystem writingSystem = systems.at(i); @@ -525,23 +523,82 @@ static inline void load(const QString &family = QString(), int = -1) -static void initFontInfo(QFontEngineWin *fe, const QFontDef &request, const QFontPrivate *fp) +static void initFontInfo(QFontEngineWin *fe, const QFontDef &request, HDC fontHdc, int dpi) { fe->fontDef = request; // most settings are equal - HDC dc = ((request.styleStrategy & QFont::PreferDevice) && fp->hdc) ? fp->hdc : shared_dc(); + HDC dc = ((request.styleStrategy & QFont::PreferDevice) && fontHdc) ? fontHdc : shared_dc(); SelectObject(dc, fe->hfont); wchar_t n[64]; GetTextFace(dc, 64, n); fe->fontDef.family = QString::fromWCharArray(n); fe->fontDef.fixedPitch = !(fe->tm.tmPitchAndFamily & TMPF_FIXED_PITCH); if (fe->fontDef.pointSize < 0) { - fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / fp->dpi; + fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / dpi; } else if (fe->fontDef.pixelSize == -1) { - fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * fp->dpi / 72.); + fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * dpi / 72.); } } +#if !defined(QT_NO_DIRECTWRITE) +static void initFontInfo(QFontEngineDirectWrite *fe, const QFontDef &request, + int dpi, IDWriteFont *font) +{ + fe->fontDef = request; + + IDWriteFontFamily *fontFamily = NULL; + HRESULT hr = font->GetFontFamily(&fontFamily); + + IDWriteLocalizedStrings *familyNames = NULL; + if (SUCCEEDED(hr)) + hr = fontFamily->GetFamilyNames(&familyNames); + + UINT32 index = 0; + BOOL exists = false; + + wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; + + if (SUCCEEDED(hr)) { + int defaultLocaleSuccess = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH); + + if (defaultLocaleSuccess) + hr = familyNames->FindLocaleName(localeName, &index, &exists); + + if (SUCCEEDED(hr) && !exists) + hr = familyNames->FindLocaleName(L"en-us", &index, &exists); + } + + if (!exists) + index = 0; + + UINT32 length = 0; + if (SUCCEEDED(hr)) + hr = familyNames->GetStringLength(index, &length); + + wchar_t *name = new (std::nothrow) wchar_t[length+1]; + if (name == NULL) + hr = E_OUTOFMEMORY; + + // Get the family name. + if (SUCCEEDED(hr)) + hr = familyNames->GetString(index, name, length + 1); + + if (SUCCEEDED(hr)) + fe->fontDef.family = QString::fromWCharArray(name); + + delete[] name; + if (familyNames != NULL) + familyNames->Release(); + + if (FAILED(hr)) + qErrnoWarning(hr, "initFontInfo: Failed to get family name"); + + if (fe->fontDef.pointSize < 0) + fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / dpi; + else if (fe->fontDef.pixelSize == -1) + fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * dpi / 72.); +} +#endif static const char *other_tryFonts[] = { "Arial", @@ -595,6 +652,14 @@ static const char *kr_tryFonts[] = { static const char **tryFonts = 0; +#if !defined(QT_NO_DIRECTWRITE) +static QString fontNameSubstitute(const QString &familyName) +{ + QLatin1String key("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\" + "FontSubstitutes"); + return QSettings(key, QSettings::NativeFormat).value(familyName, familyName).toString(); +} +#endif static inline HFONT systemFont() { @@ -607,20 +672,21 @@ static inline HFONT systemFont() #define DEFAULT_GUI_FONT 17 #endif -static -QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &request, const QtFontDesc *desc, - const QStringList &family_list) +static QFontEngine *loadEngine(int script, const QFontDef &request, + HDC fontHdc, int dpi, bool rawMode, + const QtFontDesc *desc, + const QStringList &family_list) { LOGFONT lf; memset(&lf, 0, sizeof(LOGFONT)); - bool useDevice = (request.styleStrategy & QFont::PreferDevice) && fp->hdc; + bool useDevice = (request.styleStrategy & QFont::PreferDevice) && fontHdc; HDC hdc = shared_dc(); - QString font_name = desc->family->name; + QString font_name = desc != 0 ? desc->family->name : request.family; if (useDevice) { - hdc = fp->hdc; + hdc = fontHdc; font_name = request.family; } @@ -629,9 +695,18 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ HFONT hfont = 0; - if (fp->rawMode) { // will choose a stock font + +#if !defined(QT_NO_DIRECTWRITE) + bool useDirectWrite = (request.hintingPreference == QFont::PreferNoHinting) + || (request.hintingPreference == QFont::PreferVerticalHinting); + IDWriteFont *directWriteFont = 0; +#else + bool useDirectWrite = false; +#endif + + if (rawMode) { // will choose a stock font int f, deffnt = SYSTEM_FONT; - QString fam = desc->family->name.toLower(); + QString fam = desc != 0 ? desc->family->name.toLower() : request.family.toLower(); if (fam == QLatin1String("default")) f = deffnt; else if (fam == QLatin1String("system")) @@ -685,11 +760,11 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ lf.lfWidth = 0; lf.lfEscapement = 0; lf.lfOrientation = 0; - if (desc->style->key.weight == 50) + if (desc == 0 || desc->style->key.weight == 50) lf.lfWeight = FW_DONTCARE; else lf.lfWeight = (desc->style->key.weight*900)/99; - lf.lfItalic = (desc->style->key.style != QFont::StyleNormal); + lf.lfItalic = (desc != 0 && desc->style->key.style != QFont::StyleNormal); lf.lfCharSet = DEFAULT_CHARSET; int strat = OUT_DEFAULT_PRECIS; @@ -745,6 +820,7 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ fam = QLatin1String("Courier New"); memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded + hfont = CreateFontIndirect(&lf); if (!hfont) qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect failed"); @@ -759,54 +835,130 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ res = GetTextMetrics(hdc, &tm); avWidth = tm.tmAveCharWidth; ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE; - SelectObject(hdc, oldObj); - if (hfont && (!ttf || request.stretch != 100)) { - DeleteObject(hfont); - if (!res) - qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed"); - lf.lfWidth = avWidth * request.stretch/100; - hfont = CreateFontIndirect(&lf); - if (!hfont) - qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed"); - } + if (!ttf || !useDirectWrite) { + useDirectWrite = false; + + if (hfont && (!ttf || request.stretch != 100)) { + DeleteObject(hfont); + if (!res) + qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed"); + lf.lfWidth = avWidth * request.stretch/100; + hfont = CreateFontIndirect(&lf); + if (!hfont) + qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed"); + } #ifndef Q_WS_WINCE - if (hfont == 0) { - hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); - stockFont = true; - } + if (hfont == 0) { + hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); + stockFont = true; + } #else - if (hfont == 0) { - hfont = (HFONT)GetStockObject(SYSTEM_FONT); - stockFont = true; + if (hfont == 0) { + hfont = (HFONT)GetStockObject(SYSTEM_FONT); + stockFont = true; + } +#endif + + } + +#if !defined(QT_NO_DIRECTWRITE) + else { + // Default to false for DirectWrite (and re-enable once/if everything + // turns out okay) + useDirectWrite = false; + + QFontDatabasePrivate *db = privateDb(); + if (db->directWriteFactory == 0) { + HRESULT hr = DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown **>(&db->directWriteFactory) + ); + if (FAILED(hr)) { + qErrnoWarning("QFontEngine::loadEngine: DWriteCreateFactory failed"); + } else { + hr = db->directWriteFactory->GetGdiInterop(&db->directWriteGdiInterop); + if (FAILED(hr)) + qErrnoWarning("QFontEngine::loadEngine: GetGdiInterop failed"); + } + } + + if (db->directWriteGdiInterop != 0) { + QString nameSubstitute = fontNameSubstitute(QString::fromWCharArray(lf.lfFaceName)); + memcpy(lf.lfFaceName, nameSubstitute.utf16(), + sizeof(wchar_t) * qMin(nameSubstitute.length() + 1, LF_FACESIZE)); + + HRESULT hr = db->directWriteGdiInterop->CreateFontFromLOGFONT( + &lf, + &directWriteFont); + if (FAILED(hr)) { +#ifndef QT_NO_DEBUG + qErrnoWarning("QFontEngine::loadEngine: CreateFontFromLOGFONT failed " + "for %ls (0x%lx)", + lf.lfFaceName, hr); +#endif + } else { + DeleteObject(hfont); + useDirectWrite = true; + } + } } #endif } - QFontEngineWin *few = new QFontEngineWin(font_name, hfont, stockFont, lf); - - if (preferClearTypeAA) - few->glyphFormat = QFontEngineGlyphCache::Raster_RGBMask; - - // Also check for OpenType tables when using complex scripts - // ### TODO: This only works for scripts that require OpenType. More generally - // for scripts that do not require OpenType we should just look at the list of - // supported writing systems in the font's OS/2 table. - if (scriptRequiresOpenType(script)) { - HB_Face hbFace = few->harfbuzzFace(); - if (!hbFace || !hbFace->supported_scripts[script]) { - FM_DEBUG(" OpenType support missing for script\n"); - delete few; - return 0; + + QFontEngine *fe = 0; + if (!useDirectWrite) { + QFontEngineWin *few = new QFontEngineWin(font_name, hfont, stockFont, lf); + if (preferClearTypeAA) + few->glyphFormat = QFontEngineGlyphCache::Raster_RGBMask; + + // Also check for OpenType tables when using complex scripts + // ### TODO: This only works for scripts that require OpenType. More generally + // for scripts that do not require OpenType we should just look at the list of + // supported writing systems in the font's OS/2 table. + if (scriptRequiresOpenType(script)) { + HB_Face hbFace = few->harfbuzzFace(); + if (!hbFace || !hbFace->supported_scripts[script]) { + FM_DEBUG(" OpenType support missing for script\n"); + delete few; + return 0; + } + } + + initFontInfo(few, request, fontHdc, dpi); + fe = few; + } + +#if !defined(QT_NO_DIRECTWRITE) + else { + QFontDatabasePrivate *db = privateDb(); + + IDWriteFontFace *directWriteFontFace = NULL; + HRESULT hr = directWriteFont->CreateFontFace(&directWriteFontFace); + if (SUCCEEDED(hr)) { + QFontEngineDirectWrite *fedw = new QFontEngineDirectWrite(db->directWriteFactory, + directWriteFontFace, + request.pixelSize); + + initFontInfo(fedw, request, dpi, directWriteFont); + + fe = fedw; + } else { + qErrnoWarning(hr, "QFontEngine::loadEngine: CreateFontFace failed"); } } - QFontEngine *fe = few; - initFontInfo(few, request, fp); + if (directWriteFont != 0) + directWriteFont->Release(); +#endif + if(script == QUnicodeTables::Common && !(request.styleStrategy & QFont::NoFontMerging) + && desc != 0 && !(desc->family->writingSystems[QFontDatabase::Symbol] & QtFontFamily::Supported)) { if(!tryFonts) { LANGID lid = GetUserDefaultLangID(); @@ -836,13 +988,27 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ list << QLatin1String(*tf); ++tf; } - QFontEngine *mfe = new QFontEngineMultiWin(few, list); + QFontEngine *mfe = new QFontEngineMultiWin(fe, list); mfe->fontDef = fe->fontDef; fe = mfe; } return fe; } +QFontEngine *qt_load_font_engine_win(const QFontDef &request) +{ + // From qfont.cpp + extern int qt_defaultDpi(); + + QFontCache::Key key(request, QUnicodeTables::Common); + QFontEngine *fe = QFontCache::instance()->findEngine(key); + if (fe != 0) + return fe; + else + return loadEngine(QUnicodeTables::Common, request, 0, qt_defaultDpi(), false, 0, + QStringList()); +} + const char *styleHint(const QFontDef &request) { const char *stylehint = 0; @@ -903,7 +1069,7 @@ static QFontEngine *loadWin(const QFontPrivate *d, int script, const QFontDef &r } if (!desc.family) break; - fe = loadEngine(script, d, req, &desc, family_list); + fe = loadEngine(script, req, d->hdc, d->dpi, d->rawMode, &desc, family_list); if (!fe) blacklistedFamilies.append(desc.familyIndex); } diff --git a/src/gui/text/qfontdatabase_x11.cpp b/src/gui/text/qfontdatabase_x11.cpp index 70f72eb..922a97f 100644 --- a/src/gui/text/qfontdatabase_x11.cpp +++ b/src/gui/text/qfontdatabase_x11.cpp @@ -680,7 +680,7 @@ static void loadXlfds(const char *reqFamily, int encoding_id) family->fontFileIndex = -1; family->symbol_checked = true; QtFontFoundry *foundry = family->foundry(QLatin1String(foundryName), true); - QtFontStyle *style = foundry->style(styleKey, true); + QtFontStyle *style = foundry->style(styleKey, QString(), true); delete [] style->weightName; style->weightName = qstrdup(tokens[Weight]); @@ -746,6 +746,7 @@ QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &request) QFontDef fontDef; fontDef.styleStrategy = request.styleStrategy; + fontDef.hintingPreference = request.hintingPreference; FcChar8 *value = 0; if (FcPatternGetString(pattern, FC_FAMILY, 0, &value) == FcResultMatch) { fontDef.family = QString::fromUtf8(reinterpret_cast<const char *>(value)); @@ -1018,6 +1019,13 @@ static void loadFontConfig() QFontDatabasePrivate *db = privateDb(); FcFontSet *fonts; + FcPattern *pattern = FcPatternCreate(); + FcDefaultSubstitute(pattern); + FcChar8 *lang = 0; + if (FcPatternGetString(pattern, FC_LANG, 0, &lang) == FcResultMatch) + db->systemLang = QString::fromUtf8((const char *) lang); + FcPatternDestroy(pattern); + QString familyName; FcChar8 *value = 0; int weight_value; @@ -1026,13 +1034,14 @@ static void loadFontConfig() FcChar8 *file_value; int index_value; FcChar8 *foundry_value; + FcChar8 *style_value; FcBool scalable; { FcObjectSet *os = FcObjectSetCreate(); FcPattern *pattern = FcPatternCreate(); const char *properties [] = { - FC_FAMILY, FC_WEIGHT, FC_SLANT, + FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT, FC_SPACING, FC_FILE, FC_INDEX, FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE, FC_WEIGHT, FC_WIDTH, @@ -1077,6 +1086,8 @@ static void loadFontConfig() scalable = FcTrue; if (FcPatternGetString(fonts->fonts[i], FC_FOUNDRY, 0, &foundry_value) != FcResultMatch) foundry_value = 0; + if (FcPatternGetString(fonts->fonts[i], FC_STYLE, 0, &style_value) != FcResultMatch) + style_value = 0; QtFontFamily *family = db->family(familyName, true); FcLangSet *langset = 0; @@ -1134,6 +1145,7 @@ static void loadFontConfig() family->fontFileIndex = index_value; QtFontStyle::Key styleKey; + QString styleName = style_value ? QString::fromUtf8((const char *) style_value) : QString(); styleKey.style = (slant_value == FC_SLANT_ITALIC) ? QFont::StyleItalic : ((slant_value == FC_SLANT_OBLIQUE) @@ -1148,7 +1160,7 @@ static void loadFontConfig() QtFontFoundry *foundry = family->foundry(foundry_value ? QString::fromUtf8((const char *)foundry_value) : QString(), true); - QtFontStyle *style = foundry->style(styleKey, true); + QtFontStyle *style = foundry->style(styleKey, styleName, true); if (spacing_value < FC_MONO) family->fixedPitch = false; @@ -1200,7 +1212,7 @@ static void loadFontConfig() for (int i = 0; i < 4; ++i) { styleKey.style = (i%2) ? QFont::StyleNormal : QFont::StyleItalic; styleKey.weight = (i > 1) ? QFont::Bold : QFont::Normal; - QtFontStyle *style = foundry->style(styleKey, true); + QtFontStyle *style = foundry->style(styleKey, QString(), true); style->smoothScalable = true; QtFontSize *size = style->pixelSize(SMOOTH_SCALABLE, true); QtFontEncoding *enc = size->encodingID(-1, 0, 0, 0, 0, true); @@ -1348,7 +1360,7 @@ static void initializeDb() if (equiv) continue; // let's fake one... - equiv = foundry->style(key, true); + equiv = foundry->style(key, QString(), true); equiv->smoothScalable = true; QtFontSize *equiv_size = equiv->pixelSize(SMOOTH_SCALABLE, true); @@ -1444,6 +1456,35 @@ static const char *styleHint(const QFontDef &request) void qt_addPatternProps(FcPattern *pattern, int screen, int script, const QFontDef &request) { + double size_value = qMax(qreal(1.), request.pixelSize); + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size_value); + + if (X11->display && QX11Info::appDepth(screen) <= 8) { + FcPatternDel(pattern, FC_ANTIALIAS); + // can't do antialiasing on 8bpp + FcPatternAddBool(pattern, FC_ANTIALIAS, false); + } else if (request.styleStrategy & (QFont::PreferAntialias|QFont::NoAntialias)) { + FcPatternDel(pattern, FC_ANTIALIAS); + FcPatternAddBool(pattern, FC_ANTIALIAS, + !(request.styleStrategy & QFont::NoAntialias)); + } + + if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') { + Q_ASSERT(script < QUnicodeTables::ScriptCount); + FcLangSet *ls = FcLangSetCreate(); + FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]); + FcPatternDel(pattern, FC_LANG); + FcPatternAddLangSet(pattern, FC_LANG, ls); + FcLangSetDestroy(ls); + } + + if (!request.styleName.isEmpty()) { + QByteArray cs = request.styleName.toUtf8(); + FcPatternAddString(pattern, FC_STYLE, (const FcChar8 *) cs.constData()); + return; + } + int weight_value = FC_WEIGHT_BLACK; if (request.weight == 0) weight_value = FC_WEIGHT_MEDIUM; @@ -1455,6 +1496,7 @@ void qt_addPatternProps(FcPattern *pattern, int screen, int script, const QFontD weight_value = FC_WEIGHT_DEMIBOLD; else if (request.weight < (QFont::Bold + QFont::Black) / 2) weight_value = FC_WEIGHT_BOLD; + FcPatternDel(pattern, FC_WEIGHT); FcPatternAddInteger(pattern, FC_WEIGHT, weight_value); int slant_value = FC_SLANT_ROMAN; @@ -1462,31 +1504,14 @@ void qt_addPatternProps(FcPattern *pattern, int screen, int script, const QFontD slant_value = FC_SLANT_ITALIC; else if (request.style == QFont::StyleOblique) slant_value = FC_SLANT_OBLIQUE; + FcPatternDel(pattern, FC_SLANT); FcPatternAddInteger(pattern, FC_SLANT, slant_value); - double size_value = qMax(qreal(1.), request.pixelSize); - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size_value); - int stretch = request.stretch; if (!stretch) stretch = 100; + FcPatternDel(pattern, FC_WIDTH); FcPatternAddInteger(pattern, FC_WIDTH, stretch); - - if (X11->display && QX11Info::appDepth(screen) <= 8) { - // can't do antialiasing on 8bpp - FcPatternAddBool(pattern, FC_ANTIALIAS, false); - } else if (request.styleStrategy & (QFont::PreferAntialias|QFont::NoAntialias)) { - FcPatternAddBool(pattern, FC_ANTIALIAS, - !(request.styleStrategy & QFont::NoAntialias)); - } - - if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') { - Q_ASSERT(script < QUnicodeTables::ScriptCount); - FcLangSet *ls = FcLangSetCreate(); - FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]); - FcPatternAddLangSet(pattern, FC_LANG, ls); - FcLangSetDestroy(ls); - } } static bool preferScalable(const QFontDef &request) @@ -1545,9 +1570,8 @@ static FcPattern *getFcPattern(const QFontPrivate *fp, int script, const QFontDe qt_addPatternProps(pattern, fp->screen, script, request); - FcDefaultSubstitute(pattern); FcConfigSubstitute(0, pattern, FcMatchPattern); - FcConfigSubstitute(0, pattern, FcMatchFont); + FcDefaultSubstitute(pattern); // these should only get added to the pattern _after_ substitution // append the default fallback font for the specified script @@ -1585,35 +1609,20 @@ static void FcFontSetRemove(FcFontSet *fs, int at) memmove(fs->fonts + at, fs->fonts + at + 1, len); } -static QFontEngine *tryPatternLoad(FcPattern *p, int screen, - const QFontDef &request, int script, FcPattern **matchedPattern = 0) +static QFontEngine *tryPatternLoad(FcPattern *match, int screen, + const QFontDef &request, int script) { #ifdef FONT_MATCH_DEBUG FcChar8 *fam; - FcPatternGetString(p, FC_FAMILY, 0, &fam); + FcPatternGetString(match, FC_FAMILY, 0, &fam); FM_DEBUG("==== trying %s\n", fam); #endif FM_DEBUG("passes charset test\n"); - FcPattern *pattern = FcPatternDuplicate(p); - // add properties back in as the font selected from the - // list doesn't contain them. - qt_addPatternProps(pattern, screen, script, request); - - FcConfigSubstitute(0, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - FcResult res; - FcPattern *match = FcFontMatch(0, pattern, &res); - - if (matchedPattern) - *matchedPattern = 0; QFontEngineX11FT *engine = 0; if (!match) // probably no fonts available. goto done; - if (matchedPattern) - *matchedPattern = FcPatternDuplicate(match); - if (script != QUnicodeTables::Common) { // skip font if it doesn't support the language we want if (specialChars[script]) { @@ -1652,11 +1661,6 @@ static QFontEngine *tryPatternLoad(FcPattern *p, int screen, } } done: - FcPatternDestroy(pattern); - if (!engine && matchedPattern && *matchedPattern) { - FcPatternDestroy(*matchedPattern); - *matchedPattern = 0; - } return engine; } @@ -1705,14 +1709,26 @@ static QFontEngine *loadFc(const QFontPrivate *fp, int script, const QFontDef &r #endif QFontEngine *fe = 0; - FcPattern *matchedPattern = 0; - fe = tryPatternLoad(pattern, fp->screen, request, script, &matchedPattern); + FcResult res; + FcPattern *match = FcFontMatch(0, pattern, &res); + fe = tryPatternLoad(match, fp->screen, request, script); if (!fe) { FcFontSet *fs = qt_fontSetForPattern(pattern, request); + if (match) { + FcPatternDestroy(match); + match = 0; + } + if (fs) { - for (int i = 0; !fe && i < fs->nfont; ++i) - fe = tryPatternLoad(fs->fonts[i], fp->screen, request, script, &matchedPattern); + for (int i = 0; !fe && i < fs->nfont; ++i) { + match = FcFontRenderPrepare(NULL, pattern, fs->fonts[i]); + fe = tryPatternLoad(match, fp->screen, request, script); + if (fe) + break; + FcPatternDestroy(match); + match = 0; + } FcFontSetDestroy(fs); } FM_DEBUG("engine for script %d is %s\n", script, fe ? fe->fontDef.family.toLatin1().data(): "(null)"); @@ -1720,11 +1736,11 @@ static QFontEngine *loadFc(const QFontPrivate *fp, int script, const QFontDef &r if (fe && script == QUnicodeTables::Common && !(request.styleStrategy & QFont::NoFontMerging) && !fe->symbol) { - fe = new QFontEngineMultiFT(fe, matchedPattern, pattern, fp->screen, request); + fe = new QFontEngineMultiFT(fe, match, pattern, fp->screen, request); } else { FcPatternDestroy(pattern); - if (matchedPattern) - FcPatternDestroy(matchedPattern); + if (match) + FcPatternDestroy(match); } return fe; } @@ -1958,17 +1974,6 @@ void QFontDatabase::load(const QFontPrivate *d, int script) #ifndef QT_NO_FONTCONFIG } else if (X11->has_fontconfig) { fe = loadFc(d, script, req); - if (fe != 0 && fe->fontDef.pixelSize != req.pixelSize && mainThread && qt_is_gui_used) { - QFontEngine *xlfdFontEngine = loadXlfd(d->screen, script, req); - if (xlfdFontEngine->fontDef.family == fe->fontDef.family) { - delete fe; - fe = xlfdFontEngine; - } else { - delete xlfdFontEngine; - } - } - - #endif } else if (mainThread && qt_is_gui_used) { fe = loadXlfd(d->screen, script, req); @@ -1992,6 +1997,11 @@ void QFontDatabase::load(const QFontPrivate *d, int script) QFontCache::instance()->insertEngine(key, fe); } +// Needed for fontconfig version < 2.2.97 +#ifndef FC_FAMILYLANG +#define FC_FAMILYLANG "familylang" +#endif + static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) { #if defined(QT_NO_FONTCONFIG) @@ -2030,6 +2040,7 @@ static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) int count = 0; QStringList families; + QFontDatabasePrivate *db = privateDb(); FcPattern *pattern = 0; do { @@ -2039,10 +2050,22 @@ static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) return; FcPatternDel(pattern, FC_FILE); - FcPatternAddString(pattern, FC_FILE, (const FcChar8 *)fnt->fileName.toUtf8().constData()); + QByteArray cs = fnt->fileName.toUtf8(); + FcPatternAddString(pattern, FC_FILE, (const FcChar8 *) cs.constData()); - FcChar8 *fam = 0; - if (FcPatternGetString(pattern, FC_FAMILY, 0, &fam) == FcResultMatch) { + FcChar8 *fam = 0, *familylang = 0; + int i, n = 0; + for (i = 0; ; i++) { + if (FcPatternGetString(pattern, FC_FAMILYLANG, i, &familylang) != FcResultMatch) + break; + QString familyLang = QString::fromUtf8((const char *) familylang); + if (familyLang.compare(db->systemLang, Qt::CaseInsensitive) == 0) { + n = i; + break; + } + } + + if (FcPatternGetString(pattern, FC_FAMILY, n, &fam) == FcResultMatch) { QString family = QString::fromUtf8(reinterpret_cast<const char *>(fam)); families << family; } @@ -2105,4 +2128,27 @@ bool QFontDatabase::supportsThreadedFontRendering() #endif } +QString QFontDatabase::resolveFontFamilyAlias(const QString &family) +{ +#if defined(QT_NO_FONTCONFIG) + return family; +#else + FcPattern *pattern = FcPatternCreate(); + if (!pattern) + return family; + + QByteArray cs = family.toUtf8(); + FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) cs.constData()); + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + FcChar8 *familyAfterSubstitution; + FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution); + QString resolved = QString::fromUtf8((const char *) familyAfterSubstitution); + FcPatternDestroy(pattern); + + return resolved; +#endif +} + QT_END_NAMESPACE diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 4d0e4f5..c9b672b 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -184,10 +184,6 @@ QFontEngine::QFontEngine() QFontEngine::~QFontEngine() { - for (QLinkedList<GlyphCacheEntry>::const_iterator it = m_glyphCaches.constBegin(), - end = m_glyphCaches.constEnd(); it != end; ++it) { - delete it->cache; - } m_glyphCaches.clear(); qHBFreeFace(hbFace); } @@ -284,6 +280,8 @@ void QFontEngine::getGlyphPositions(const QGlyphLayout &glyphs, const QTransform int i = glyphs.numGlyphs; int totalKashidas = 0; while(i--) { + if (glyphs.attributes[i].dontPrint) + continue; xpos += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6); ypos += glyphs.advances_y[i]; totalKashidas += glyphs.justifications[i].nKashidas; @@ -480,7 +478,7 @@ static void collectSingleContour(qreal x0, qreal y0, uint *grid, int x, int y, i path->closeSubpath(); } -void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path) +Q_GUI_EXPORT void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path) { uint *grid = new uint[(w+1)*(h+1)]; // set up edges @@ -591,6 +589,12 @@ void QFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int n addBitmapFontToPath(x, y, g, path, flags); } +QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition*/) +{ + // For font engines don't support subpixel positioning + return alphaMapForGlyph(glyph); +} + QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t) { QImage i = alphaMapForGlyph(glyph); @@ -601,7 +605,20 @@ QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t) return i; } -QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, int /* margin */, const QTransform &t) +QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t) +{ + if (! supportsSubPixelPositions()) + return alphaMapForGlyph(glyph, t); + + QImage i = alphaMapForGlyph(glyph, subPixelPosition); + if (t.type() > QTransform::TxTranslate) + i = i.transformed(t).convertToFormat(QImage::Format_Indexed8); + Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format... + + return i; +} + +QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed /*subPixelPosition*/, int /* margin */, const QTransform &t) { QImage alphaMask = alphaMapForGlyph(glyph, t); QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32); @@ -711,14 +728,16 @@ void QFontEngine::setGlyphCache(void *key, QFontEngineGlyphCache *data) { Q_ASSERT(data); - GlyphCacheEntry entry = { key, data }; + GlyphCacheEntry entry; + entry.context = key; + entry.cache = data; if (m_glyphCaches.contains(entry)) return; // Limit the glyph caches to 4. This covers all 90 degree rotations and limits // memory use when there is continuous or random rotation if (m_glyphCaches.size() == 4) - delete m_glyphCaches.takeLast().cache; + m_glyphCaches.removeLast(); m_glyphCaches.push_front(entry); @@ -727,7 +746,7 @@ void QFontEngine::setGlyphCache(void *key, QFontEngineGlyphCache *data) QFontEngineGlyphCache *QFontEngine::glyphCache(void *key, QFontEngineGlyphCache::Type type, const QTransform &transform) const { for (QLinkedList<GlyphCacheEntry>::const_iterator it = m_glyphCaches.constBegin(), end = m_glyphCaches.constEnd(); it != end; ++it) { - QFontEngineGlyphCache *c = it->cache; + QFontEngineGlyphCache *c = it->cache.data(); if (key == it->context && type == c->cacheType() && qtransform_equals_no_translate(c->m_transform, transform)) { @@ -737,7 +756,7 @@ QFontEngineGlyphCache *QFontEngine::glyphCache(void *key, QFontEngineGlyphCache: return 0; } -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN) static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs) { uint left_right = (left << 16) + right; @@ -1181,7 +1200,7 @@ glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs) return overall; } -#if defined(Q_WS_QWS) +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti) { if (!ti.glyphs.numGlyphs) @@ -1318,8 +1337,7 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, int glyph_pos = 0; for (int i = 0; i < len; ++i) { - bool surrogate = (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && i < len-1 - && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000); + bool surrogate = (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()); if (glyphs->glyphs[glyph_pos] == 0 && str[i].category() != QChar::Separator_Line) { QGlyphLayoutInstance tmp = glyphs->instance(glyph_pos); diff --git a/src/gui/text/qfontengine_coretext.mm b/src/gui/text/qfontengine_coretext.mm new file mode 100644 index 0000000..153451e --- /dev/null +++ b/src/gui/text/qfontengine_coretext.mm @@ -0,0 +1,875 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfontengine_coretext_p.h" + +#include <QtCore/qendian.h> +#include <QtCore/qsettings.h> + +#include <private/qimage_p.h> + +#if !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + +QT_BEGIN_NAMESPACE + +static float SYNTHETIC_ITALIC_SKEW = tanf(14 * acosf(0) / 90); + +static void loadAdvancesForGlyphs(CTFontRef ctfont, + QVarLengthArray<CGGlyph> &cgGlyphs, + QGlyphLayout *glyphs, int len, + QTextEngine::ShaperFlags flags, + const QFontDef &fontDef) +{ + Q_UNUSED(flags); + QVarLengthArray<CGSize> advances(len); + CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, cgGlyphs.data(), advances.data(), len); + + for (int i = 0; i < len; ++i) { + if (glyphs->glyphs[i] & 0xff000000) + continue; + glyphs->advances_x[i] = QFixed::fromReal(advances[i].width); + glyphs->advances_y[i] = QFixed::fromReal(advances[i].height); + } + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + for (int i = 0; i < len; ++i) { + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + glyphs->advances_y[i] = glyphs->advances_y[i].round(); + } + } +} + +QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning) + : QFontEngineMulti(0) +{ + this->fontDef = fontDef; + CTFontSymbolicTraits symbolicTraits = 0; + if (fontDef.weight >= QFont::Bold) + symbolicTraits |= kCTFontBoldTrait; + switch (fontDef.style) { + case QFont::StyleNormal: + break; + case QFont::StyleItalic: + case QFont::StyleOblique: + symbolicTraits |= kCTFontItalicTrait; + break; + } + + transform = CGAffineTransformIdentity; + if (fontDef.stretch != 100) { + transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); + } + + QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithNameAndSize(name, fontDef.pixelSize); + QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pixelSize, &transform); + ctfont = NULL; + // There is a side effect in Core Text: if we apply 0 as symbolic traits to a font in normal weight, + // we will get the light version of that font (while the way supposed to work doesn't: + // setting kCTFontWeightTrait to some value between -1.0 to 0.0 has no effect on font selection) + if (fontDef.weight != QFont::Normal || symbolicTraits) + ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pixelSize, &transform, symbolicTraits, symbolicTraits); + + // CTFontCreateCopyWithSymbolicTraits returns NULL if we ask for a trait that does + // not exist for the given font. (for example italic) + if (ctfont == 0) { + ctfont = baseFont; + CFRetain(ctfont); + } + init(kerning); +} + +QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(CTFontRef ctFontRef, const QFontDef &fontDef, bool kerning) + : QFontEngineMulti(0) +{ + this->fontDef = fontDef; + ctfont = (CTFontRef) CFRetain(ctFontRef); + init(kerning); +} + +QCoreTextFontEngineMulti::~QCoreTextFontEngineMulti() +{ + CFRelease(ctfont); +} + +void QCoreTextFontEngineMulti::init(bool kerning) +{ + Q_ASSERT(ctfont != NULL); + attributeDict = CFDictionaryCreateMutable(0, 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attributeDict, kCTFontAttributeName, ctfont); + if (!kerning) { + float zero = 0.0; + QCFType<CFNumberRef> noKern = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &zero); + CFDictionaryAddValue(attributeDict, kCTKernAttributeName, noKern); + } + + QCoreTextFontEngine *fe = new QCoreTextFontEngine(ctfont, fontDef); + fontDef.family = fe->fontDef.family; + fontDef.styleName = fe->fontDef.styleName; + transform = fe->transform; + fe->ref.ref(); + engines.append(fe); +} + +uint QCoreTextFontEngineMulti::fontIndexForFont(CTFontRef font) const +{ + for (int i = 0; i < engines.count(); ++i) { + if (CFEqual(engineAt(i)->ctfont, font)) + return i; + } + + QCoreTextFontEngineMulti *that = const_cast<QCoreTextFontEngineMulti *>(this); + QCoreTextFontEngine *fe = new QCoreTextFontEngine(font, fontDef); + fe->ref.ref(); + that->engines.append(fe); + return engines.count() - 1; +} + +bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, + int *nglyphs, QTextEngine::ShaperFlags flags, + unsigned short *logClusters, const HB_CharAttributes *, + QScriptItem *si) const +{ + QCFType<CFStringRef> cfstring = CFStringCreateWithCharactersNoCopy(0, + reinterpret_cast<const UniChar *>(str), + len, kCFAllocatorNull); + QCFType<CFAttributedStringRef> attributedString = CFAttributedStringCreate(0, cfstring, attributeDict); + QCFType<CTTypesetterRef> typeSetter; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + if (flags & QTextEngine::RightToLeft) { + const void *optionKeys[] = { kCTTypesetterOptionForcedEmbeddingLevel }; + const short rtlForcedEmbeddingLevelValue = 1; + const void *rtlOptionValues[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &rtlForcedEmbeddingLevelValue) }; + QCFType<CFDictionaryRef> options = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, 1, + &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + typeSetter = CTTypesetterCreateWithAttributedStringAndOptions(attributedString, options); + } else +#else + Q_UNUSED(flags); +#endif + typeSetter = CTTypesetterCreateWithAttributedString(attributedString); + + CFRange range = {0, 0}; + QCFType<CTLineRef> line = CTTypesetterCreateLine(typeSetter, range); + CFArrayRef array = CTLineGetGlyphRuns(line); + uint arraySize = CFArrayGetCount(array); + glyph_t *outGlyphs = glyphs->glyphs; + HB_GlyphAttributes *outAttributes = glyphs->attributes; + QFixed *outAdvances_x = glyphs->advances_x; + QFixed *outAdvances_y = glyphs->advances_y; + glyph_t *initialGlyph = outGlyphs; + + if (arraySize == 0) { + // CoreText failed to shape the text we gave it, so we assume one glyph + // per character and build a list of invalid glyphs with zero advance + *nglyphs = len; + for (int i = 0; i < len; ++i) { + outGlyphs[i] = 0; + if (logClusters) + logClusters[i] = i; + outAdvances_x[i] = QFixed(); + outAdvances_y[i] = QFixed(); + outAttributes[i].clusterStart = true; + } + return true; + } + + const bool rtl = (CTRunGetStatus(static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, 0))) & kCTRunStatusRightToLeft); + + bool outOBounds = false; + for (uint i = 0; i < arraySize; ++i) { + CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, rtl ? (arraySize - 1 - i) : i)); + CFIndex glyphCount = CTRunGetGlyphCount(run); + if (glyphCount == 0) + continue; + + Q_ASSERT((CTRunGetStatus(run) & kCTRunStatusRightToLeft) == rtl); + CFRange stringRange = CTRunGetStringRange(run); + int prepend = 0; +#if MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5 + UniChar beginGlyph = CFStringGetCharacterAtIndex(cfstring, stringRange.location); + QChar dir = QChar::direction(beginGlyph); + bool beginWithOverride = dir == QChar::DirLRO || dir == QChar::DirRLO || dir == QChar::DirLRE || dir == QChar::DirRLE; + if (beginWithOverride) { + logClusters[stringRange.location] = 0; + outGlyphs[0] = 0xFFFF; + outAdvances_x[0] = 0; + outAdvances_y[0] = 0; + outAttributes[0].clusterStart = true; + outAttributes[0].dontPrint = true; + outGlyphs++; + outAdvances_x++; + outAdvances_y++; + outAttributes++; + prepend = 1; + } +#endif + UniChar endGlyph = CFStringGetCharacterAtIndex(cfstring, stringRange.location + stringRange.length - 1); + bool endWithPDF = QChar::direction(endGlyph) == QChar::DirPDF; + if (endWithPDF) + glyphCount++; + + if (!outOBounds && outGlyphs + glyphCount - initialGlyph > *nglyphs) { + outOBounds = true; + } + if (!outOBounds) { + CFDictionaryRef runAttribs = CTRunGetAttributes(run); + //NSLog(@"Dictionary %@", runAttribs); + if (!runAttribs) + runAttribs = attributeDict; + CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(runAttribs, kCTFontAttributeName)); + uint fontIndex = fontIndexForFont(runFont); + const QFontEngine *engine = engineAt(fontIndex); + fontIndex <<= 24; + si->ascent = qMax(engine->ascent(), si->ascent); + si->descent = qMax(engine->descent(), si->descent); + si->leading = qMax(engine->leading(), si->leading); + //NSLog(@"Run Font Name = %@", CTFontCopyFamilyName(runFont)); + if (endWithPDF) + glyphCount--; + + QVarLengthArray<CGGlyph, 512> cgglyphs(0); + const CGGlyph *tmpGlyphs = CTRunGetGlyphsPtr(run); + if (!tmpGlyphs) { + cgglyphs.resize(glyphCount); + CTRunGetGlyphs(run, range, cgglyphs.data()); + tmpGlyphs = cgglyphs.constData(); + } + QVarLengthArray<CGPoint, 512> cgpoints(0); + const CGPoint *tmpPoints = CTRunGetPositionsPtr(run); + if (!tmpPoints) { + cgpoints.resize(glyphCount); + CTRunGetPositions(run, range, cgpoints.data()); + tmpPoints = cgpoints.constData(); + } + + const int rtlOffset = rtl ? (glyphCount - 1) : 0; + const int rtlSign = rtl ? -1 : 1; + + if (logClusters) { + CFRange stringRange = CTRunGetStringRange(run); + QVarLengthArray<CFIndex, 512> stringIndices(0); + const CFIndex *tmpIndices = CTRunGetStringIndicesPtr(run); + if (!tmpIndices) { + stringIndices.resize(glyphCount); + CTRunGetStringIndices(run, range, stringIndices.data()); + tmpIndices = stringIndices.constData(); + } + + const int firstGlyphIndex = outGlyphs - initialGlyph; + outAttributes[0].clusterStart = true; + + CFIndex k = 0; + CFIndex i = 0; + for (i = stringRange.location + prepend; + (i < stringRange.location + stringRange.length) && (k < glyphCount); ++i) { + if (tmpIndices[k * rtlSign + rtlOffset] == i || i == stringRange.location + prepend) { + logClusters[i] = k + firstGlyphIndex; + outAttributes[k].clusterStart = true; + ++k; + } else { + logClusters[i] = k + firstGlyphIndex - 1; + } + } + // in case of a ligature at the end, fill the remaining logcluster entries + for (;i < stringRange.location + stringRange.length; i++) { + logClusters[i] = k + firstGlyphIndex - 1; + } + } + for (CFIndex i = 0; i < glyphCount - 1; ++i) { + int idx = rtlOffset + rtlSign * i; + outGlyphs[idx] = tmpGlyphs[i] | fontIndex; + outAdvances_x[idx] = QFixed::fromReal(tmpPoints[i + 1].x - tmpPoints[i].x); + // Use negative y advance for flipped coordinate system + outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i].y - tmpPoints[i + 1].y); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + outAdvances_x[idx] = outAdvances_x[idx].round(); + outAdvances_y[idx] = outAdvances_y[idx].round(); + } + } + CGSize lastGlyphAdvance; + CTFontGetAdvancesForGlyphs(runFont, kCTFontHorizontalOrientation, tmpGlyphs + glyphCount - 1, &lastGlyphAdvance, 1); + + outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex; + outAdvances_x[rtl ? 0 : (glyphCount - 1)] = + (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(lastGlyphAdvance.width).round() + : QFixed::fromReal(lastGlyphAdvance.width); + + if (endWithPDF) { + logClusters[stringRange.location + stringRange.length - 1] = glyphCount + prepend; + outGlyphs[glyphCount] = 0xFFFF; + outAdvances_x[glyphCount] = 0; + outAdvances_y[glyphCount] = 0; + outAttributes[glyphCount].clusterStart = true; + outAttributes[glyphCount].dontPrint = true; + glyphCount++; + } + } + outGlyphs += glyphCount; + outAttributes += glyphCount; + outAdvances_x += glyphCount; + outAdvances_y += glyphCount; + } + *nglyphs = (outGlyphs - initialGlyph); + return !outOBounds; +} + +bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, + int *nglyphs, QTextEngine::ShaperFlags flags) const +{ + *nglyphs = len; + QCFType<CFStringRef> cfstring; + + QVarLengthArray<CGGlyph> cgGlyphs(len); + CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len); + + for (int i = 0; i < len; ++i) { + if (cgGlyphs[i]) { + glyphs->glyphs[i] = cgGlyphs[i]; + } else { + if (!cfstring) + cfstring = CFStringCreateWithCharactersNoCopy(0, reinterpret_cast<const UniChar *>(str), len, kCFAllocatorNull); + QCFType<CTFontRef> substituteFont = CTFontCreateForString(ctfont, cfstring, CFRangeMake(i, 1)); + CGGlyph substituteGlyph = 0; + CTFontGetGlyphsForCharacters(substituteFont, (const UniChar*)str + i, &substituteGlyph, 1); + if (substituteGlyph) { + const uint fontIndex = (fontIndexForFont(substituteFont) << 24); + glyphs->glyphs[i] = substituteGlyph | fontIndex; + if (!(flags & QTextEngine::GlyphIndicesOnly)) { + CGSize advance; + CTFontGetAdvancesForGlyphs(substituteFont, kCTFontHorizontalOrientation, &substituteGlyph, &advance, 1); + glyphs->advances_x[i] = QFixed::fromReal(advance.width); + glyphs->advances_y[i] = QFixed::fromReal(advance.height); + } + } + } + } + + if (flags & QTextEngine::GlyphIndicesOnly) + return true; + + loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, len, flags, fontDef); + return true; +} + +void QCoreTextFontEngineMulti::loadEngine(int) +{ + // Do nothing + Q_ASSERT(false); +} + +extern int qt_antialiasing_threshold; // from qapplication.cpp + +CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef) +{ + CGAffineTransform transform = CGAffineTransformIdentity; + if (fontDef.stretch != 100) + transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); + return transform; +} + +QCoreTextFontEngine::QCoreTextFontEngine(CTFontRef font, const QFontDef &def) +{ + fontDef = def; + transform = qt_transform_from_fontdef(fontDef); + ctfont = font; + CFRetain(ctfont); + cgFont = CTFontCopyGraphicsFont(font, NULL); + init(); +} + +QCoreTextFontEngine::QCoreTextFontEngine(CGFontRef font, const QFontDef &def) +{ + fontDef = def; + transform = qt_transform_from_fontdef(fontDef); + cgFont = font; + // Keep reference count balanced + CFRetain(cgFont); + ctfont = CTFontCreateWithGraphicsFont(font, fontDef.pixelSize, &transform, NULL); + init(); +} + +QCoreTextFontEngine::~QCoreTextFontEngine() +{ + CFRelease(cgFont); + CFRelease(ctfont); +} + +extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp + +int getTraitValue(CFDictionaryRef allTraits, CFStringRef trait) +{ + if (CFDictionaryContainsKey(allTraits, trait)) { + CFNumberRef traitNum = (CFNumberRef) CFDictionaryGetValue(allTraits, trait); + float v = 0; + CFNumberGetValue(traitNum, kCFNumberFloatType, &v); + // the value we get from CFNumberRef is from -1.0 to 1.0 + int value = v * 500 + 500; + return value; + } + + return 0; +} + +void QCoreTextFontEngine::init() +{ + Q_ASSERT(ctfont != NULL); + Q_ASSERT(cgFont != NULL); + + QCFString family = CTFontCopyFamilyName(ctfont); + fontDef.family = family; + + QCFString styleName = (CFStringRef) CTFontCopyAttribute(ctfont, kCTFontStyleNameAttribute); + fontDef.styleName = styleName; + + synthesisFlags = 0; + CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont); + if (traits & kCTFontItalicTrait) + fontDef.style = QFont::StyleItalic; + + CFDictionaryRef allTraits = CTFontCopyTraits(ctfont); + fontDef.weight = weightFromInteger(getTraitValue(allTraits, kCTFontWeightTrait)); + int slant = getTraitValue(allTraits, kCTFontSlantTrait); + if (slant > 500 && !(traits & kCTFontItalicTrait)) + fontDef.style = QFont::StyleOblique; + CFRelease(allTraits); + + if (fontDef.weight >= QFont::Bold && !(traits & kCTFontBoldTrait)) + synthesisFlags |= SynthesizedBold; + // XXX: we probably don't need to synthesis italic for oblique font + if (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait)) + synthesisFlags |= SynthesizedItalic; + + avgCharWidth = 0; + QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); + unsigned emSize = CTFontGetUnitsPerEm(ctfont); + if (os2Table.size() >= 10) { + fsType = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 8)); + // qAbs is a workaround for weird fonts like Lucida Grande + qint16 width = qAbs(qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 2))); + avgCharWidth = QFixed::fromReal(width * fontDef.pixelSize / emSize); + } else + avgCharWidth = QFontEngine::averageCharWidth(); +} + +bool QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, + int *nglyphs, QTextEngine::ShaperFlags flags) const +{ + *nglyphs = len; + QCFType<CFStringRef> cfstring; + + QVarLengthArray<CGGlyph> cgGlyphs(len); + CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len); + + for (int i = 0; i < len; ++i) + if (cgGlyphs[i]) + glyphs->glyphs[i] = cgGlyphs[i]; + + if (flags & QTextEngine::GlyphIndicesOnly) + return true; + + loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, len, flags, fontDef); + return true; +} + +glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs) +{ + QFixed w; + bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics; + + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += round ? glyphs.effectiveAdvance(i).round() + : glyphs.effectiveAdvance(i); + } + return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), ascent()+descent(), w, 0); +} + +glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph) +{ + glyph_metrics_t ret; + CGGlyph g = glyph; + CGRect rect = CTFontGetBoundingRectsForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, 0, 1); + if (synthesisFlags & QFontEngine::SynthesizedItalic) { + rect.size.width += rect.size.height * SYNTHETIC_ITALIC_SKEW; + } + ret.width = QFixed::fromReal(rect.size.width); + ret.height = QFixed::fromReal(rect.size.height); + ret.x = QFixed::fromReal(rect.origin.x); + ret.y = -QFixed::fromReal(rect.origin.y) - ret.height; + CGSize advances[1]; + CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, advances, 1); + ret.xoff = QFixed::fromReal(advances[0].width); + ret.yoff = QFixed::fromReal(advances[0].height); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + ret.xoff = ret.xoff.round(); + ret.yoff = ret.yoff.round(); + } + return ret; +} + +QFixed QCoreTextFontEngine::ascent() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetAscent(ctfont)).round() + : QFixed::fromReal(CTFontGetAscent(ctfont)); +} +QFixed QCoreTextFontEngine::descent() const +{ + QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont)); + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + d = d.round(); + + // subtract a pixel to even out the historical +1 in QFontMetrics::height(). + // Fix in Qt 5. + return d - 1; +} +QFixed QCoreTextFontEngine::leading() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetLeading(ctfont)).round() + : QFixed::fromReal(CTFontGetLeading(ctfont)); +} +QFixed QCoreTextFontEngine::xHeight() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetXHeight(ctfont)).round() + : QFixed::fromReal(CTFontGetXHeight(ctfont)); +} + +QFixed QCoreTextFontEngine::averageCharWidth() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? avgCharWidth.round() : avgCharWidth; +} + +qreal QCoreTextFontEngine::maxCharWidth() const +{ + return 0; +} + +qreal QCoreTextFontEngine::minLeftBearing() const +{ + return 0; +} + +qreal QCoreTextFontEngine::minRightBearing() const +{ + return 0; +} + +void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight) +{ + QVarLengthArray<QFixedPoint> positions; + QVarLengthArray<glyph_t> glyphs; + QTransform matrix; + matrix.translate(x, y); + getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); + if (glyphs.size() == 0) + return; + + CGContextSetFontSize(ctx, fontDef.pixelSize); + + CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx); + + CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight); + + CGAffineTransformConcat(cgMatrix, oldTextMatrix); + + if (synthesisFlags & QFontEngine::SynthesizedItalic) + cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0)); + + cgMatrix = CGAffineTransformConcat(cgMatrix, transform); + + CGContextSetTextMatrix(ctx, cgMatrix); + + CGContextSetTextDrawingMode(ctx, kCGTextFill); + + + QVarLengthArray<CGSize> advances(glyphs.size()); + QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size()); + + for (int i = 0; i < glyphs.size() - 1; ++i) { + advances[i].width = (positions[i + 1].x - positions[i].x).toReal(); + advances[i].height = (positions[i + 1].y - positions[i].y).toReal(); + cgGlyphs[i] = glyphs[i]; + } + advances[glyphs.size() - 1].width = 0; + advances[glyphs.size() - 1].height = 0; + cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1]; + + CGContextSetFont(ctx, cgFont); + //NSLog(@"Font inDraw %@ ctfont %@", CGFontCopyFullName(cgFont), CTFontCopyFamilyName(ctfont)); + + CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal()); + + CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size()); + + if (synthesisFlags & QFontEngine::SynthesizedBold) { + CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(), + positions[0].y.toReal()); + + CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size()); + } + + CGContextSetTextMatrix(ctx, oldTextMatrix); +} + +struct ConvertPathInfo +{ + ConvertPathInfo(QPainterPath *newPath, const QPointF &newPos) : path(newPath), pos(newPos) {} + QPainterPath *path; + QPointF pos; +}; + +static void convertCGPathToQPainterPath(void *info, const CGPathElement *element) +{ + ConvertPathInfo *myInfo = static_cast<ConvertPathInfo *>(info); + switch(element->type) { + case kCGPathElementMoveToPoint: + myInfo->path->moveTo(element->points[0].x + myInfo->pos.x(), + element->points[0].y + myInfo->pos.y()); + break; + case kCGPathElementAddLineToPoint: + myInfo->path->lineTo(element->points[0].x + myInfo->pos.x(), + element->points[0].y + myInfo->pos.y()); + break; + case kCGPathElementAddQuadCurveToPoint: + myInfo->path->quadTo(element->points[0].x + myInfo->pos.x(), + element->points[0].y + myInfo->pos.y(), + element->points[1].x + myInfo->pos.x(), + element->points[1].y + myInfo->pos.y()); + break; + case kCGPathElementAddCurveToPoint: + myInfo->path->cubicTo(element->points[0].x + myInfo->pos.x(), + element->points[0].y + myInfo->pos.y(), + element->points[1].x + myInfo->pos.x(), + element->points[1].y + myInfo->pos.y(), + element->points[2].x + myInfo->pos.x(), + element->points[2].y + myInfo->pos.y()); + break; + case kCGPathElementCloseSubpath: + myInfo->path->closeSubpath(); + break; + default: + qDebug() << "Unhandled path transform type: " << element->type; + } + +} + +void QCoreTextFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs, + QPainterPath *path, QTextItem::RenderFlags) +{ + CGAffineTransform cgMatrix = CGAffineTransformIdentity; + cgMatrix = CGAffineTransformScale(cgMatrix, 1, -1); + + if (synthesisFlags & QFontEngine::SynthesizedItalic) + cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0)); + + for (int i = 0; i < nGlyphs; ++i) { + QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, glyphs[i], &cgMatrix); + ConvertPathInfo info(path, positions[i].toPointF()); + CGPathApply(cgpath, &info, convertCGPathToQPainterPath); + } +} + +QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, QFixed subPixelPosition, int margin, bool aa) +{ + Q_UNUSED(margin); + const glyph_metrics_t br = boundingBox(glyph); + QImage im(qRound(br.width) + 2, qRound(br.height) + 2, QImage::Format_RGB32); + im.fill(0); + + CGColorSpaceRef colorspace = +#ifdef Q_WS_MAC + CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); +#else + CGColorSpaceCreateDeviceRGB(); +#endif + uint cgflags = kCGImageAlphaNoneSkipFirst; +#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version + cgflags |= kCGBitmapByteOrder32Host; +#endif + CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(), + 8, im.bytesPerLine(), colorspace, + cgflags); + CGContextSetFontSize(ctx, fontDef.pixelSize); + CGContextSetShouldAntialias(ctx, (aa || fontDef.pointSize > qt_antialiasing_threshold) + && !(fontDef.styleStrategy & QFont::NoAntialias)); + CGContextSetShouldSmoothFonts(ctx, aa); + CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx); + CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0); + + CGAffineTransformConcat(cgMatrix, oldTextMatrix); + + if (synthesisFlags & QFontEngine::SynthesizedItalic) + cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, SYNTHETIC_ITALIC_SKEW, 1, 0, 0)); + + cgMatrix = CGAffineTransformConcat(cgMatrix, transform); + + CGContextSetTextMatrix(ctx, cgMatrix); + CGContextSetRGBFillColor(ctx, 1, 1, 1, 1); + CGContextSetTextDrawingMode(ctx, kCGTextFill); + + CGContextSetFont(ctx, cgFont); + + qreal pos_x = -br.x.truncate() + subPixelPosition.toReal(); + qreal pos_y = im.height() + br.y.toReal(); + CGContextSetTextPosition(ctx, pos_x, pos_y); + + CGSize advance; + advance.width = 0; + advance.height = 0; + CGGlyph cgGlyph = glyph; + CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1); + + if (synthesisFlags & QFontEngine::SynthesizedBold) { + CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y); + CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1); + } + + CGContextRelease(ctx); + + return im; +} + +QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) +{ + QImage im = imageForGlyph(glyph, subPixelPosition, 0, false); + + QImage indexed(im.width(), im.height(), QImage::Format_Indexed8); + QVector<QRgb> colors(256); + for (int i=0; i<256; ++i) + colors[i] = qRgba(0, 0, 0, i); + indexed.setColorTable(colors); + + for (int y=0; y<im.height(); ++y) { + uint *src = (uint*) im.scanLine(y); + uchar *dst = indexed.scanLine(y); + for (int x=0; x<im.width(); ++x) { + *dst = qGray(*src); + ++dst; + ++src; + } + } + + return indexed; +} + +QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed subPixelPosition, int margin, const QTransform &x) +{ + if (x.type() >= QTransform::TxScale) + return QFontEngine::alphaRGBMapForGlyph(glyph, subPixelPosition, margin, x); + + QImage im = imageForGlyph(glyph, subPixelPosition, margin, true); + qGamma_correct_back_to_linear_cs(&im); + return im; +} + +void QCoreTextFontEngine::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const +{ + int i, numGlyphs = glyphs->numGlyphs; + QVarLengthArray<CGGlyph> cgGlyphs(numGlyphs); + + for (i = 0; i < numGlyphs; ++i) { + if (glyphs->glyphs[i] & 0xff000000) + cgGlyphs[i] = 0; + else + cgGlyphs[i] = glyphs->glyphs[i]; + } + + loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, numGlyphs, flags, fontDef); +} + +QFontEngine::FaceId QCoreTextFontEngine::faceId() const +{ + return QFontEngine::FaceId(); +} + +bool QCoreTextFontEngine::canRender(const QChar *string, int len) +{ + QVarLengthArray<CGGlyph> cgGlyphs(len); + return CTFontGetGlyphsForCharacters(ctfont, (const UniChar *) string, cgGlyphs.data(), len); +} + +bool QCoreTextFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + QCFType<CFDataRef> table = CTFontCopyTable(ctfont, tag, 0); + if (!table || !length) + return false; + CFIndex tableLength = CFDataGetLength(table); + int availableLength = *length; + *length = tableLength; + if (buffer) { + if (tableLength > availableLength) + return false; + CFDataGetBytes(table, CFRangeMake(0, tableLength), buffer); + } + return true; +} + +void QCoreTextFontEngine::getUnscaledGlyph(glyph_t, QPainterPath *, glyph_metrics_t *) +{ + // ### +} + +QFixed QCoreTextFontEngine::emSquareSize() const +{ + return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); +} + +QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const +{ + QFontDef newFontDef = fontDef; + newFontDef.pixelSize = pixelSize; + newFontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi(); + + return new QCoreTextFontEngine(cgFont, newFontDef); +} + +QT_END_NAMESPACE + +#endif// !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + diff --git a/src/gui/text/qfontengine_coretext_p.h b/src/gui/text/qfontengine_coretext_p.h new file mode 100644 index 0000000..4bd80be --- /dev/null +++ b/src/gui/text/qfontengine_coretext_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFONTENGINE_CORETEXT_P_H +#define QFONTENGINE_CORETEXT_P_H + +#include <private/qfontengine_p.h> + +#ifdef QT_NO_CORESERVICES +#include <CoreText/CoreText.h> +#include <CoreGraphics/CoreGraphics.h> +#include <private/qcore_mac_p.h> +#endif + +#if !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QRawFontPrivate; +class QCoreTextFontEngineMulti; +class QCoreTextFontEngine : public QFontEngine +{ +public: + QCoreTextFontEngine(CTFontRef font, const QFontDef &def); + QCoreTextFontEngine(CGFontRef font, const QFontDef &def); + ~QCoreTextFontEngine(); + + virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const; + virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const; + + virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs); + virtual glyph_metrics_t boundingBox(glyph_t glyph); + + virtual QFixed ascent() const; + virtual QFixed descent() const; + virtual QFixed leading() const; + virtual QFixed xHeight() const; + virtual qreal maxCharWidth() const; + virtual QFixed averageCharWidth() const; + + virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, + QPainterPath *path, QTextItem::RenderFlags); + + virtual const char *name() const { return "QCoreTextFontEngine"; } + + virtual bool canRender(const QChar *string, int len); + + virtual int synthesized() const { return synthesisFlags; } + virtual bool supportsSubPixelPositions() const { return true; } + + virtual Type type() const { return QFontEngine::Mac; } + + void draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight); + + virtual FaceId faceId() const; + virtual bool getSfntTableData(uint /*tag*/, uchar * /*buffer*/, uint * /*length*/) const; + virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics); + virtual QImage alphaMapForGlyph(glyph_t, QFixed subPixelPosition); + virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); + virtual qreal minRightBearing() const; + virtual qreal minLeftBearing() const; + virtual QFixed emSquareSize() const; + + virtual QFontEngine *cloneWithSize(qreal pixelSize) const; + +private: + friend class QRawFontPrivate; + + void init(); + QImage imageForGlyph(glyph_t glyph, QFixed subPixelPosition, int margin, bool colorful); + CTFontRef ctfont; + CGFontRef cgFont; + int synthesisFlags; + CGAffineTransform transform; + QFixed avgCharWidth; + friend class QCoreTextFontEngineMulti; +}; + +class QCoreTextFontEngineMulti : public QFontEngineMulti +{ +public: + QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning); + QCoreTextFontEngineMulti(CTFontRef ctFontRef, const QFontDef &fontDef, bool kerning); + ~QCoreTextFontEngineMulti(); + + virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, + QTextEngine::ShaperFlags flags) const; + bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, + QTextEngine::ShaperFlags flags, + unsigned short *logClusters, const HB_CharAttributes *charAttributes, + QScriptItem *si) const; + + virtual const char *name() const { return "CoreText"; } + inline CTFontRef macFontID() const { return ctfont; } + +protected: + virtual void loadEngine(int at); + +private: + void init(bool kerning); + inline const QCoreTextFontEngine *engineAt(int i) const + { return static_cast<const QCoreTextFontEngine *>(engines.at(i)); } + + uint fontIndexForFont(CTFontRef font) const; + CTFontRef ctfont; + mutable QCFType<CFMutableDictionaryRef> attributeDict; + CGAffineTransform transform; + friend class QFontDialogPrivate; +}; + +CGAffineTransform qt_transform_from_fontdef(const QFontDef &fontDef); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif// !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + +#endif // QFONTENGINE_CORETEXT_P_H diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index 1056aed..e20aa25 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -78,6 +78,10 @@ #include FT_ERRORS_H #endif +#if !defined(QT_MAX_CACHED_GLYPH_SIZE) +# define QT_MAX_CACHED_GLYPH_SIZE 64 +#endif + QT_BEGIN_NAMESPACE /* @@ -174,9 +178,7 @@ int QFreetypeFace::fsType() const HB_Error QFreetypeFace::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints) { - int load_flags = (flags & HB_ShaperFlag_UseDesignMetrics) ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT; - - if (HB_Error error = (HB_Error)FT_Load_Glyph(face, glyph, load_flags)) + if (HB_Error error = (HB_Error)FT_Load_Glyph(face, glyph, flags)) return error; if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) @@ -202,9 +204,10 @@ HB_Error QFreetypeFace::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 p * Returns the freetype face or 0 in case of an empty file or any other problems * (like not being able to open the file) */ -QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id) +QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id, + const QByteArray &fontData) { - if (face_id.filename.isEmpty()) + if (face_id.filename.isEmpty() && fontData.isEmpty()) return 0; QtFreetypeData *freetypeData = qt_getFreetypeData(); @@ -217,21 +220,25 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id) } else { QScopedPointer<QFreetypeFace> newFreetype(new QFreetypeFace); FT_Face face; - QFile file(QString::fromUtf8(face_id.filename)); - if (face_id.filename.startsWith(":qmemoryfonts/")) { - // from qfontdatabase.cpp - extern QByteArray qt_fontdata_from_index(int); - QByteArray idx = face_id.filename; - idx.remove(0, 14); // remove ':qmemoryfonts/' - bool ok = false; - newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok)); - if (!ok) - newFreetype->fontData = QByteArray(); - } else if (!(file.fileEngine()->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)) { - if (!file.open(QIODevice::ReadOnly)) { - return 0; + if (!face_id.filename.isEmpty()) { + QFile file(QString::fromUtf8(face_id.filename)); + if (face_id.filename.startsWith(":qmemoryfonts/")) { + // from qfontdatabase.cpp + extern QByteArray qt_fontdata_from_index(int); + QByteArray idx = face_id.filename; + idx.remove(0, 14); // remove ':qmemoryfonts/' + bool ok = false; + newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok)); + if (!ok) + newFreetype->fontData = QByteArray(); + } else if (!(file.fileEngine()->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::LocalDiskFlag)) { + if (!file.open(QIODevice::ReadOnly)) { + return 0; + } + newFreetype->fontData = file.readAll(); } - newFreetype->fontData = file.readAll(); + } else { + newFreetype->fontData = fontData; } if (!newFreetype->fontData.isEmpty()) { if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)newFreetype->fontData.constData(), newFreetype->fontData.size(), face_id.index, &face)) { @@ -370,7 +377,7 @@ void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, *xsize = *ysize = 0; } } else { - *outline_drawing = (*xsize > (64<<6) || *ysize > (64<<6)); + *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6)); } } @@ -653,8 +660,21 @@ void QFontEngineFT::freeGlyphSets() freeServerGlyphSet(transformedGlyphSets.at(i).id); } -bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format) +bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, + const QByteArray &fontData) { + return init(faceId, antialias, format, QFreetypeFace::getFace(faceId, fontData)); +} + +bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, + QFreetypeFace *freetypeFace) +{ + freetype = freetypeFace; + if (!freetype) { + xsize = 0; + ysize = 0; + return false; + } defaultFormat = format; this->antialias = antialias; @@ -666,12 +686,6 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format) glyphFormat = QFontEngineGlyphCache::Raster_RGBMask; face_id = faceId; - freetype = QFreetypeFace::getFace(face_id); - if (!freetype) { - xsize = 0; - ysize = 0; - return false; - } symbol = freetype->symbol_map != 0; PS_FontInfoRec psrec; @@ -723,7 +737,7 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format) metrics = face->size->metrics; -#if defined(Q_WS_QWS) +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) /* TrueType fonts with embedded bitmaps may have a bitmap font specific ascent/descent in the EBLC table. There is no direct public API @@ -748,6 +762,8 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format) } #endif + fontDef.styleName = QString::fromUtf8(face->style_name); + unlockFace(); fsType = freetype->fsType(); @@ -755,116 +771,48 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format) return true; } -QFontEngineFT::Glyph *QFontEngineFT::loadGlyphMetrics(QGlyphSet *set, uint glyph) const +void QFontEngineFT::setDefaultHintStyle(HintStyle style) { - Glyph *g = set->getGlyph(glyph); - if (g) - return g; + default_hint_style = style; +} +int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags, + bool &hsubpixel, int &vfactor) const +{ int load_flags = FT_LOAD_DEFAULT | default_load_flags; int load_target = default_hint_style == HintLight ? FT_LOAD_TARGET_LIGHT : FT_LOAD_TARGET_NORMAL; - if (set->outline_drawing) + if (format == Format_Mono) { + load_target = FT_LOAD_TARGET_MONO; + } else if (format == Format_A32) { + if (subpixelType == QFontEngineFT::Subpixel_RGB || subpixelType == QFontEngineFT::Subpixel_BGR) { + if (default_hint_style == HintFull) + load_target = FT_LOAD_TARGET_LCD; + hsubpixel = true; + } else if (subpixelType == QFontEngineFT::Subpixel_VRGB || subpixelType == QFontEngineFT::Subpixel_VBGR) { + if (default_hint_style == HintFull) + load_target = FT_LOAD_TARGET_LCD_V; + vfactor = 3; + } + } + + if (set && set->outline_drawing) load_flags = FT_LOAD_NO_BITMAP; - if (default_hint_style == HintNone) + if (default_hint_style == HintNone || (flags & HB_ShaperFlag_UseDesignMetrics) || set->outline_drawing) load_flags |= FT_LOAD_NO_HINTING; else load_flags |= load_target; - // apply our matrix to this, but note that the metrics will not be affected by this. - FT_Face face = lockFace(); - FT_Matrix matrix = this->matrix; - FT_Matrix_Multiply(&set->transformationMatrix, &matrix); - FT_Set_Transform(face, &matrix, 0); - freetype->matrix = matrix; - - bool transform = matrix.xx != 0x10000 || matrix.yy != 0x10000 || matrix.xy != 0 || matrix.yx != 0; - if (transform) - load_flags |= FT_LOAD_NO_BITMAP; - - FT_Error err = FT_Load_Glyph(face, glyph, load_flags); - if (err && (load_flags & FT_LOAD_NO_BITMAP)) { - load_flags &= ~FT_LOAD_NO_BITMAP; - err = FT_Load_Glyph(face, glyph, load_flags); - } - if (err == FT_Err_Too_Few_Arguments) { - // this is an error in the bytecode interpreter, just try to run without it - load_flags |= FT_LOAD_FORCE_AUTOHINT; - err = FT_Load_Glyph(face, glyph, load_flags); - } - if (err != FT_Err_Ok) - qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph); - - unlockFace(); - if (set->outline_drawing) - return 0; - - if (!g) { - g = new Glyph; - g->uploadedToServer = false; - g->data = 0; - } - - FT_GlyphSlot slot = face->glyph; - if (embolden) Q_FT_GLYPHSLOT_EMBOLDEN(slot); - int left = slot->metrics.horiBearingX; - int right = slot->metrics.horiBearingX + slot->metrics.width; - int top = slot->metrics.horiBearingY; - int bottom = slot->metrics.horiBearingY - slot->metrics.height; - if(transform && slot->format != FT_GLYPH_FORMAT_BITMAP) { // freetype doesn't apply the transformation on the metrics - int l, r, t, b; - FT_Vector vector; - vector.x = left; - vector.y = top; - FT_Vector_Transform(&vector, &matrix); - l = r = vector.x; - t = b = vector.y; - vector.x = right; - vector.y = top; - FT_Vector_Transform(&vector, &matrix); - if (l > vector.x) l = vector.x; - if (r < vector.x) r = vector.x; - if (t < vector.y) t = vector.y; - if (b > vector.y) b = vector.y; - vector.x = right; - vector.y = bottom; - FT_Vector_Transform(&vector, &matrix); - if (l > vector.x) l = vector.x; - if (r < vector.x) r = vector.x; - if (t < vector.y) t = vector.y; - if (b > vector.y) b = vector.y; - vector.x = left; - vector.y = bottom; - FT_Vector_Transform(&vector, &matrix); - if (l > vector.x) l = vector.x; - if (r < vector.x) r = vector.x; - if (t < vector.y) t = vector.y; - if (b > vector.y) b = vector.y; - left = l; - right = r; - top = t; - bottom = b; - } - left = FLOOR(left); - right = CEIL(right); - bottom = FLOOR(bottom); - top = CEIL(top); - - g->linearAdvance = face->glyph->linearHoriAdvance >> 10; - g->width = TRUNC(right-left); - g->height = TRUNC(top-bottom); - g->x = TRUNC(left); - g->y = TRUNC(top); - g->advance = TRUNC(ROUND(face->glyph->advance.x)); - g->format = Format_None; - - return g; + return load_flags; } -QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, GlyphFormat format, bool fetchMetricsOnly) const +QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, + QFixed subPixelPosition, + GlyphFormat format, + bool fetchMetricsOnly) const { // Q_ASSERT(freetype->lock == 1); @@ -879,10 +827,10 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph } } - Glyph *g = set->getGlyph(glyph); + Glyph *g = set->getGlyph(glyph, subPixelPosition); if (g && g->format == format) { if (uploadToServer && !g->uploadedToServer) { - set->setGlyph(glyph, 0); + set->setGlyph(glyph, subPixelPosition, 0); delete g; g = 0; } else { @@ -895,33 +843,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph Q_ASSERT(format != Format_None); bool hsubpixel = false; int vfactor = 1; - int load_flags = FT_LOAD_DEFAULT | default_load_flags; - - int load_target = default_hint_style == HintLight - ? FT_LOAD_TARGET_LIGHT - : FT_LOAD_TARGET_NORMAL; - - if (set->outline_drawing) - load_flags |= FT_LOAD_NO_BITMAP; - - if (format == Format_Mono) { - load_target = FT_LOAD_TARGET_MONO; - } else if (format == Format_A32) { - if (subpixelType == QFontEngineFT::Subpixel_RGB || subpixelType == QFontEngineFT::Subpixel_BGR) { - if (default_hint_style == HintFull) - load_target = FT_LOAD_TARGET_LCD; - hsubpixel = true; - } else if (subpixelType == QFontEngineFT::Subpixel_VRGB || subpixelType == QFontEngineFT::Subpixel_VBGR) { - if (default_hint_style == HintFull) - load_target = FT_LOAD_TARGET_LCD_V; - vfactor = 3; - } - } - - if (default_hint_style == HintNone || set->outline_drawing) - load_flags |= FT_LOAD_NO_HINTING; - else - load_flags |= load_target; + int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor); #ifndef Q_WS_QWS if (format != Format_Mono && !embeddedbitmap) @@ -938,6 +860,12 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph load_flags |= FT_LOAD_NO_BITMAP; FT_Face face = freetype->face; + + FT_Vector v; + v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.toReal() * 64); + v.y = 0; + FT_Set_Transform(face, &freetype->matrix, &v); + FT_Error err = FT_Load_Glyph(face, glyph, load_flags); if (err && (load_flags & FT_LOAD_NO_BITMAP)) { load_flags &= ~FT_LOAD_NO_BITMAP; @@ -1038,6 +966,10 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph top = CEIL(top); int hpixels = TRUNC(right - left); + // subpixel position requires one more pixel + if (subPixelPosition > 0 && format != Format_Mono) + hpixels++; + if (hsubpixel) hpixels = hpixels*3 + 8; info.width = hpixels; @@ -1180,7 +1112,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph uploadGlyphToServer(set, glyph, g, &info, glyph_buffer_size); } - set->setGlyph(glyph, g); + set->setGlyph(glyph, subPixelPosition, g); return g; } @@ -1389,7 +1321,7 @@ QFontEngineFT::QGlyphSet *QFontEngineFT::loadTransformedGlyphSet(const QTransfor if (!gs) { // don't try to load huge fonts - bool draw_as_outline = fontDef.pixelSize * qSqrt(qAbs(matrix.det())) >= 64; + bool draw_as_outline = fontDef.pixelSize * qSqrt(qAbs(matrix.det())) >= QT_MAX_CACHED_GLYPH_SIZE; if (draw_as_outline) return 0; @@ -1413,12 +1345,30 @@ QFontEngineFT::QGlyphSet *QFontEngineFT::loadTransformedGlyphSet(const QTransfor return gs; } -bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, GlyphFormat format) +QFixed QFontEngineFT::subPixelPositionForX(QFixed x) +{ + int m_subPixelPositionCount = 4; + if (!supportsSubPixelPositions()) + return 0; + + QFixed subPixelPosition; + if (x != 0) { + subPixelPosition = x - x.floor(); + QFixed fraction = (subPixelPosition / QFixed::fromReal(1.0 / m_subPixelPositionCount)).floor(); + subPixelPosition = fraction / QFixed(m_subPixelPositionCount); + } + return subPixelPosition; +} + +bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, const glyph_t *glyphs, int num_glyphs, + const QFixedPoint *positions, + GlyphFormat format) { FT_Face face = 0; for (int i = 0; i < num_glyphs; ++i) { - Glyph *glyph = gs->getGlyph(glyphs[i]); + QFixed spp = subPixelPositionForX(positions[i].x); + Glyph *glyph = gs->getGlyph(glyphs[i], spp); if (glyph == 0 || glyph->format != format) { if (!face) { face = lockFace(); @@ -1427,7 +1377,7 @@ bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, G FT_Set_Transform(face, &m, 0); freetype->matrix = m; } - if (!loadGlyph(gs, glyphs[i], format)) { + if (!loadGlyph(gs, glyphs[i], spp, format)) { unlockFace(); return false; } @@ -1472,15 +1422,12 @@ void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_me static inline unsigned int getChar(const QChar *str, int &i, const int len) { - unsigned int uc = str[i].unicode(); - if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) { - uint low = str[i+1].unicode(); - if (low >= 0xdc00 && low < 0xe000) { - uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000; - ++i; - } + uint ucs4 = str[i].unicode(); + if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) { + ++i; + ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode()); } - return uc; + return ucs4; } bool QFontEngineFT::canRender(const QChar *string, int len) @@ -1579,7 +1526,7 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs mtx->lock(); } - if (FcCharSetHasChar(freetype->charset, uc)) { + if (freetype->charset != 0 && FcCharSetHasChar(freetype->charset, uc)) { #else if (false) { #endif @@ -1614,7 +1561,7 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs mtx->lock(); } - if (FcCharSetHasChar(freetype->charset, uc)) + if (freetype->charset == 0 || FcCharSetHasChar(freetype->charset, uc)) #endif { redo: @@ -1651,32 +1598,23 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const { FT_Face face = 0; - if (flags & QTextEngine::DesignMetrics) { - for (int i = 0; i < glyphs->numGlyphs; i++) { - Glyph *g = defaultGlyphSet.getGlyph(glyphs->glyphs[i]); - if (g) { - glyphs->advances_x[i] = QFixed::fromFixed(g->linearAdvance); - } else { - if (!face) - face = lockFace(); - g = loadGlyph(glyphs->glyphs[i], Format_None, true); - glyphs->advances_x[i] = QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10); - } - glyphs->advances_y[i] = 0; - } - } else { - for (int i = 0; i < glyphs->numGlyphs; i++) { - Glyph *g = defaultGlyphSet.getGlyph(glyphs->glyphs[i]); - if (g) { - glyphs->advances_x[i] = QFixed(g->advance); - } else { - if (!face) - face = lockFace(); - g = loadGlyph(glyphs->glyphs[i], Format_None, true); - glyphs->advances_x[i] = QFixed::fromFixed(face->glyph->metrics.horiAdvance).round(); - } - glyphs->advances_y[i] = 0; + bool design = (default_hint_style == HintNone || + default_hint_style == HintLight || + (flags & HB_ShaperFlag_UseDesignMetrics)) && FT_IS_SCALABLE(freetype->face); + for (int i = 0; i < glyphs->numGlyphs; i++) { + Glyph *g = defaultGlyphSet.getGlyph(glyphs->glyphs[i]); + if (g) { + glyphs->advances_x[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance); + } else { + if (!face) + face = lockFace(); + g = loadGlyph(glyphs->glyphs[i], 0, Format_None, true); + glyphs->advances_x[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10) + : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round(); } + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + glyphs->advances_y[i] = 0; } if (face) unlockFace(); @@ -1699,7 +1637,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) if (!g) { if (!face) face = lockFace(); - g = loadGlyph(glyphs.glyphs[i], Format_None, true); + g = loadGlyph(glyphs.glyphs[i], 0, Format_None, true); } if (g) { QFixed x = overall.xoff + glyphs.offsets[i].x + g->x; @@ -1740,7 +1678,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) Glyph *g = defaultGlyphSet.getGlyph(glyph); if (!g) { face = lockFace(); - g = loadGlyph(glyph, Format_None, true); + g = loadGlyph(glyph, 0, Format_None, true); } if (g) { overall.x = g->x; @@ -1748,6 +1686,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) overall.width = g->width; overall.height = g->height; overall.xoff = g->advance; + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + overall.xoff = overall.xoff.round(); } else { int left = FLOOR(face->glyph->metrics.horiBearingX); int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); @@ -1767,6 +1707,11 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix) { + return alphaMapBoundingBox(glyph, 0, matrix, QFontEngine::Format_None); +} + +glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format) +{ FT_Face face = 0; glyph_metrics_t overall; QGlyphSet *glyphSet = 0; @@ -1809,12 +1754,13 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matr } else { glyphSet = &defaultGlyphSet; } - bool needsDelete = false; Glyph * g = glyphSet->getGlyph(glyph); - if (!g) { + if (!g || g->format != format) { face = lockFace(); - g = loadGlyphMetrics(glyphSet, glyph); - needsDelete = true; + FT_Matrix m = this->matrix; + FT_Matrix_Multiply(&glyphSet->transformationMatrix, &m); + freetype->matrix = m; + g = loadGlyph(glyphSet, glyph, subPixelPosition, format); } if (g) { @@ -1823,8 +1769,6 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matr overall.width = g->width; overall.height = g->height; overall.xoff = g->advance; - if (needsDelete) - delete g; } else { int left = FLOOR(face->glyph->metrics.horiBearingX); int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); @@ -1842,13 +1786,13 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matr return overall; } -QImage QFontEngineFT::alphaMapForGlyph(glyph_t g) +QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition) { lockFace(); GlyphFormat glyph_format = antialias ? Format_A8 : Format_Mono; - Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, glyph_format); + Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, subPixelPosition, glyph_format); if (!glyph) { unlockFace(); return QFontEngine::alphaMapForGlyph(g); @@ -1878,19 +1822,19 @@ QImage QFontEngineFT::alphaMapForGlyph(glyph_t g) return img; } -QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, int margin, const QTransform &t) +QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, int margin, const QTransform &t) { if (t.type() > QTransform::TxTranslate) - return QFontEngine::alphaRGBMapForGlyph(g, margin, t); + return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, margin, t); lockFace(); GlyphFormat glyph_format = Format_A32; - Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, glyph_format); + Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, subPixelPosition, glyph_format); if (!glyph) { unlockFace(); - return QFontEngine::alphaRGBMapForGlyph(g, margin, t); + return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, margin, t); } QImage img(glyph->width, glyph->height, QImage::Format_RGB32); @@ -1902,7 +1846,7 @@ QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, int margin, const QTransfor void QFontEngineFT::removeGlyphFromCache(glyph_t glyph) { - defaultGlyphSet.removeGlyphFromCache(glyph); + defaultGlyphSet.removeGlyphFromCache(glyph, 0); } int QFontEngineFT::glyphCount() const @@ -1982,9 +1926,9 @@ void QFontEngineFT::QGlyphSet::clear() glyph_data.clear(); } -void QFontEngineFT::QGlyphSet::removeGlyphFromCache(int index) +void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPixelPosition) { - if (index < 256) { + if (useFastGlyphData(index, subPixelPosition)) { if (fast_glyph_data[index]) { delete fast_glyph_data[index]; fast_glyph_data[index] = 0; @@ -1992,18 +1936,18 @@ void QFontEngineFT::QGlyphSet::removeGlyphFromCache(int index) --fast_glyph_count; } } else { - delete glyph_data.take(index); + delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition)); } } -void QFontEngineFT::QGlyphSet::setGlyph(int index, Glyph *glyph) +void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index, QFixed subPixelPosition, Glyph *glyph) { - if (index < 256) { + if (useFastGlyphData(index, subPixelPosition)) { if (!fast_glyph_data[index]) ++fast_glyph_count; fast_glyph_data[index] = glyph; } else { - glyph_data.insert(index, glyph); + glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph); } } @@ -2020,11 +1964,49 @@ void QFontEngineFT::freeServerGlyphSet(unsigned long id) HB_Error QFontEngineFT::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints) { lockFace(); - HB_Error result = freetype->getPointInOutline(glyph, flags, point, xpos, ypos, nPoints); + bool hsubpixel = true; + int vfactor = 1; + int load_flags = loadFlags(0, Format_A8, flags, hsubpixel, vfactor); + HB_Error result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints); unlockFace(); return result; } +bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe) +{ + if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype)) + return false; + + // Increase the reference of this QFreetypeFace since one more QFontEngineFT + // will be using it + freetype->ref.ref(); + + default_load_flags = fe->default_load_flags; + default_hint_style = fe->default_hint_style; + antialias = fe->antialias; + transform = fe->transform; + embolden = fe->embolden; + subpixelType = fe->subpixelType; + lcdFilterType = fe->lcdFilterType; + canUploadGlyphsToServer = fe->canUploadGlyphsToServer; + embeddedbitmap = fe->embeddedbitmap; + + return true; +} + +QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const +{ + QFontDef fontDef; + fontDef.pixelSize = pixelSize; + QFontEngineFT *fe = new QFontEngineFT(fontDef); + if (!fe->initFromFontEngine(this)) { + delete fe; + return 0; + } else { + return fe; + } +} + QT_END_NAMESPACE #endif // QT_NO_FREETYPE diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h index da8a62a..57811fa 100644 --- a/src/gui/text/qfontengine_ft_p.h +++ b/src/gui/text/qfontengine_ft_p.h @@ -74,6 +74,8 @@ QT_BEGIN_NAMESPACE +class QFontEngineFTRawFont; + /* * This struct represents one font file on disk (like Arial.ttf) and is shared between all the font engines * that show this font file (at different pixel sizes). @@ -84,7 +86,8 @@ struct QFreetypeFace QFontEngine::Properties properties() const; bool getSfntTable(uint tag, uchar *buffer, uint *length) const; - static QFreetypeFace *getFace(const QFontEngine::FaceId &face_id); + static QFreetypeFace *getFace(const QFontEngine::FaceId &face_id, + const QByteArray &fontData = QByteArray()); void release(const QFontEngine::FaceId &face_id); // locks the struct for usage. Any read/write operations require locking. @@ -119,6 +122,7 @@ struct QFreetypeFace static void addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path, bool = false); private: + friend class QFontEngineFT; friend class QScopedPointerDeleter<QFreetypeFace>; QFreetypeFace() : _lock(QMutex::Recursive) {} ~QFreetypeFace() {} @@ -130,13 +134,6 @@ private: class Q_GUI_EXPORT QFontEngineFT : public QFontEngine { public: - enum GlyphFormat { - Format_None, - Format_Render = Format_None, - Format_Mono, - Format_A8, - Format_A32 - }; /* we don't cache glyphs that are too large anyway, so we can make this struct rather small */ struct Glyph { @@ -173,6 +170,19 @@ public: }; #endif + struct GlyphAndSubPixelPosition + { + GlyphAndSubPixelPosition(glyph_t g, QFixed spp) : glyph(g), subPixelPosition(spp) {} + + bool operator==(const GlyphAndSubPixelPosition &other) const + { + return glyph == other.glyph && subPixelPosition == other.subPixelPosition; + } + + glyph_t glyph; + QFixed subPixelPosition; + }; + struct QGlyphSet { QGlyphSet(); @@ -181,18 +191,21 @@ public: unsigned long id; // server sided id, GlyphSet for X11 bool outline_drawing; - void removeGlyphFromCache(int index); + void removeGlyphFromCache(glyph_t index, QFixed subPixelPosition); void clear(); - inline Glyph *getGlyph(int index) const + inline bool useFastGlyphData(glyph_t index, QFixed subPixelPosition) const { + return (index < 256 && subPixelPosition == 0); + } + inline Glyph *getGlyph(glyph_t index, QFixed subPixelPosition = 0) const { - if (index < 256) + if (useFastGlyphData(index, subPixelPosition)) return fast_glyph_data[index]; - return glyph_data.value(index); + return glyph_data.value(GlyphAndSubPixelPosition(index, subPixelPosition)); } - void setGlyph(int index, Glyph *glyph); + void setGlyph(glyph_t index, QFixed spp, Glyph *glyph); private: - mutable QHash<int, Glyph *> glyph_data; // maps from glyph index to glyph data + mutable QHash<GlyphAndSubPixelPosition, Glyph *> glyph_data; // maps from glyph index to glyph data mutable Glyph *fast_glyph_data[256]; // for fast lookup of glyphs < 256 mutable int fast_glyph_count; }; @@ -200,6 +213,11 @@ private: virtual QFontEngine::FaceId faceId() const; virtual QFontEngine::Properties properties() const; virtual QFixed emSquareSize() const; + virtual bool supportsSubPixelPositions() const + { + return default_hint_style == HintLight || + default_hint_style == HintNone; + } virtual bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; virtual int synthesized() const; @@ -240,8 +258,13 @@ private: virtual glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix); virtual void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const; - virtual QImage alphaMapForGlyph(glyph_t); - virtual QImage alphaRGBMapForGlyph(glyph_t, int margin, const QTransform &t); + virtual QImage alphaMapForGlyph(glyph_t g) { return alphaMapForGlyph(g, 0); } + virtual QImage alphaMapForGlyph(glyph_t, QFixed); + virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); + virtual glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, + QFixed subPixelPosition, + const QTransform &matrix, + QFontEngine::GlyphFormat format); virtual void removeGlyphFromCache(glyph_t glyph); virtual int glyphCount() const; @@ -259,17 +282,20 @@ private: inline bool invalid() const { return xsize == 0 && ysize == 0; } inline bool isBitmapFont() const { return defaultFormat == Format_Mono; } - inline Glyph *loadGlyph(uint glyph, GlyphFormat format = Format_None, bool fetchMetricsOnly = false) const - { return loadGlyph(&defaultGlyphSet, glyph, format, fetchMetricsOnly); } - Glyph *loadGlyph(QGlyphSet *set, uint glyph, GlyphFormat = Format_None, bool fetchMetricsOnly = false) const; + inline Glyph *loadGlyph(uint glyph, QFixed subPixelPosition, GlyphFormat format = Format_None, bool fetchMetricsOnly = false) const + { return loadGlyph(&defaultGlyphSet, glyph, subPixelPosition, format, fetchMetricsOnly); } + Glyph *loadGlyph(QGlyphSet *set, uint glyph, QFixed subPixelPosition, GlyphFormat = Format_None, bool fetchMetricsOnly = false) const; QGlyphSet *defaultGlyphs() { return &defaultGlyphSet; } GlyphFormat defaultGlyphFormat() const { return defaultFormat; } - inline Glyph *cachedGlyph(glyph_t g) const { return defaultGlyphSet.getGlyph(g); } + inline Glyph *cachedGlyph(glyph_t g) const { return defaultGlyphSet.getGlyph(g, 0); } QGlyphSet *loadTransformedGlyphSet(const QTransform &matrix); - bool loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, GlyphFormat format = Format_Render); + QFixed subPixelPositionForX(QFixed x); + bool loadGlyphs(QGlyphSet *gs, const glyph_t *glyphs, int num_glyphs, + const QFixedPoint *positions, + GlyphFormat format = Format_Render); #if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) virtual void draw(QPaintEngine * /*p*/, qreal /*x*/, qreal /*y*/, const QTextItemInt & /*si*/) {} @@ -278,10 +304,26 @@ private: QFontEngineFT(const QFontDef &fd); virtual ~QFontEngineFT(); - bool init(FaceId faceId, bool antiaalias, GlyphFormat defaultFormat = Format_None); + bool init(FaceId faceId, bool antiaalias, GlyphFormat defaultFormat = Format_None, + const QByteArray &fontData = QByteArray()); + bool init(FaceId faceId, bool antialias, GlyphFormat format, + QFreetypeFace *freetypeFace); virtual HB_Error getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints); + enum HintStyle { + HintNone, + HintLight, + HintMedium, + HintFull + }; + + void setDefaultHintStyle(HintStyle style); + + virtual QFontEngine *cloneWithSize(qreal pixelSize) const; + bool initFromFontEngine(const QFontEngineFT *fontEngine); + + HintStyle defaultHintStyle() const { return default_hint_style; } protected: void freeGlyphSets(); @@ -293,12 +335,6 @@ protected: QFreetypeFace *freetype; int default_load_flags; - enum HintStyle { - HintNone, - HintLight, - HintMedium, - HintFull - }; HintStyle default_hint_style; @@ -311,7 +347,9 @@ protected: bool embeddedbitmap; private: - QFontEngineFT::Glyph *loadGlyphMetrics(QGlyphSet *set, uint glyph) const; + friend class QFontEngineFTRawFont; + + int loadFlags(QGlyphSet *set, GlyphFormat format, int flags, bool &hsubpixel, int &vfactor) const; GlyphFormat defaultFormat; FT_Matrix matrix; @@ -333,6 +371,11 @@ private: mutable bool kerning_pairs_loaded; }; +inline uint qHash(const QFontEngineFT::GlyphAndSubPixelPosition &g) +{ + return (g.glyph << 8) | (g.subPixelPosition * 10).round().toInt(); +} + QT_END_NAMESPACE #endif // QT_NO_FREETYPE diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm index 3c6ec1d..6186b2f 100644 --- a/src/gui/text/qfontengine_mac.mm +++ b/src/gui/text/qfontengine_mac.mm @@ -39,6 +39,8 @@ ** ****************************************************************************/ +#include "qfontengine_mac_p.h" + #include <private/qapplication_p.h> #include <private/qfontengine_p.h> #include <private/qpainter_p.h> @@ -53,14 +55,13 @@ #include <qdebug.h> #include <qendian.h> #include <qmath.h> +#include <private/qimage_p.h> #include <ApplicationServices/ApplicationServices.h> #include <AppKit/AppKit.h> QT_BEGIN_NAMESPACE -static float SYNTHETIC_ITALIC_SKEW = tanf(14 * acosf(0) / 90); - /***************************************************************************** QFontEngine debug facilities *****************************************************************************/ @@ -121,684 +122,6 @@ OSStatus QMacFontPath::closePath(void *data) } - -void qmacfontengine_gamma_correct(QImage *image) -{ - extern uchar qt_pow_rgb_gamma[256]; - - // gamma correct the pixels back to linear color space... - int h = image->height(); - int w = image->width(); - - for (int y=0; y<h; ++y) { - uint *pixels = (uint *) image->scanLine(y); - for (int x=0; x<w; ++x) { - uint p = pixels[x]; - uint r = qt_pow_rgb_gamma[qRed(p)]; - uint g = qt_pow_rgb_gamma[qGreen(p)]; - uint b = qt_pow_rgb_gamma[qBlue(p)]; - pixels[x] = (r << 16) | (g << 8) | b | 0xff000000; - } - } -} - - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const ATSFontFamilyRef &atsFamilyRef, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning) - : QFontEngineMulti(0) -{ - this->fontDef = fontDef; - CTFontSymbolicTraits symbolicTraits = 0; - if (fontDef.weight >= QFont::Bold) - symbolicTraits |= kCTFontBoldTrait; - switch (fontDef.style) { - case QFont::StyleNormal: - break; - case QFont::StyleItalic: - case QFont::StyleOblique: - symbolicTraits |= kCTFontItalicTrait; - break; - } - - QCFString name; - ATSFontGetName(atsFontRef, kATSOptionFlagsDefault, &name); - if (QString(name).isEmpty()) - ATSFontFamilyGetName(atsFamilyRef, kATSOptionFlagsDefault, &name); - - transform = CGAffineTransformIdentity; - if (fontDef.stretch != 100) { - transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); - } - - QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithNameAndSize(name, fontDef.pixelSize); - QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pixelSize, &transform); - ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pixelSize, &transform, symbolicTraits, symbolicTraits); - - // CTFontCreateCopyWithSymbolicTraits returns NULL if we ask for a trait that does - // not exist for the given font. (for example italic) - if (ctfont == 0) { - ctfont = baseFont; - CFRetain(ctfont); - } - - attributeDict = CFDictionaryCreateMutable(0, 2, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(attributeDict, NSFontAttributeName, ctfont); - if (!kerning) { - float zero = 0.0; - QCFType<CFNumberRef> noKern = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &zero); - CFDictionaryAddValue(attributeDict, kCTKernAttributeName, noKern); - } - - QCoreTextFontEngine *fe = new QCoreTextFontEngine(ctfont, fontDef, this); - fe->ref.ref(); - engines.append(fe); - -} - -QCoreTextFontEngineMulti::~QCoreTextFontEngineMulti() -{ - CFRelease(ctfont); -} - -uint QCoreTextFontEngineMulti::fontIndexForFont(CTFontRef id) const -{ - for (int i = 0; i < engines.count(); ++i) { - if (CFEqual(engineAt(i)->ctfont, id)) - return i; - } - - QCoreTextFontEngineMulti *that = const_cast<QCoreTextFontEngineMulti *>(this); - QCoreTextFontEngine *fe = new QCoreTextFontEngine(id, fontDef, that); - fe->ref.ref(); - that->engines.append(fe); - return engines.count() - 1; -} - -bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags, - unsigned short *logClusters, const HB_CharAttributes *) const -{ - QCFType<CFStringRef> cfstring = CFStringCreateWithCharactersNoCopy(0, - reinterpret_cast<const UniChar *>(str), - len, kCFAllocatorNull); - QCFType<CFAttributedStringRef> attributedString = CFAttributedStringCreate(0, cfstring, attributeDict); - QCFType<CTTypesetterRef> typeSetter = CTTypesetterCreateWithAttributedString(attributedString); - CFRange range = {0, 0}; - QCFType<CTLineRef> line = CTTypesetterCreateLine(typeSetter, range); - CFArrayRef array = CTLineGetGlyphRuns(line); - uint arraySize = CFArrayGetCount(array); - glyph_t *outGlyphs = glyphs->glyphs; - HB_GlyphAttributes *outAttributes = glyphs->attributes; - QFixed *outAdvances_x = glyphs->advances_x; - QFixed *outAdvances_y = glyphs->advances_y; - glyph_t *initialGlyph = outGlyphs; - - if (arraySize == 0) { - // CoreText failed to shape the text we gave it, so we assume one glyph - // per character and build a list of invalid glyphs with zero advance - *nglyphs = len; - for (int i = 0; i < len; ++i) { - outGlyphs[i] = 0; - if (logClusters) - logClusters[i] = i; - outAdvances_x[i] = QFixed(); - outAdvances_y[i] = QFixed(); - outAttributes[i].clusterStart = true; - } - return true; - } - - const bool rtl = (CTRunGetStatus(static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, 0))) & kCTRunStatusRightToLeft); - - bool outOBounds = false; - for (uint i = 0; i < arraySize; ++i) { - CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex(array, rtl ? (arraySize - 1 - i) : i)); - CFIndex glyphCount = CTRunGetGlyphCount(run); - if (glyphCount == 0) - continue; - - Q_ASSERT((CTRunGetStatus(run) & kCTRunStatusRightToLeft) == rtl); - - if (!outOBounds && outGlyphs + glyphCount - initialGlyph > *nglyphs) { - outOBounds = true; - } - if (!outOBounds) { - CFDictionaryRef runAttribs = CTRunGetAttributes(run); - //NSLog(@"Dictionary %@", runAttribs); - if (!runAttribs) - runAttribs = attributeDict; - CTFontRef runFont = static_cast<CTFontRef>(CFDictionaryGetValue(runAttribs, NSFontAttributeName)); - const uint fontIndex = (fontIndexForFont(runFont) << 24); - //NSLog(@"Run Font Name = %@", CTFontCopyFamilyName(runFont)); - QVarLengthArray<CGGlyph, 512> cgglyphs(0); - const CGGlyph *tmpGlyphs = CTRunGetGlyphsPtr(run); - if (!tmpGlyphs) { - cgglyphs.resize(glyphCount); - CTRunGetGlyphs(run, range, cgglyphs.data()); - tmpGlyphs = cgglyphs.constData(); - } - QVarLengthArray<CGPoint, 512> cgpoints(0); - const CGPoint *tmpPoints = CTRunGetPositionsPtr(run); - if (!tmpPoints) { - cgpoints.resize(glyphCount); - CTRunGetPositions(run, range, cgpoints.data()); - tmpPoints = cgpoints.constData(); - } - - const int rtlOffset = rtl ? (glyphCount - 1) : 0; - const int rtlSign = rtl ? -1 : 1; - - if (logClusters) { - CFRange stringRange = CTRunGetStringRange(run); - QVarLengthArray<CFIndex, 512> stringIndices(0); - const CFIndex *tmpIndices = CTRunGetStringIndicesPtr(run); - if (!tmpIndices) { - stringIndices.resize(glyphCount); - CTRunGetStringIndices(run, range, stringIndices.data()); - tmpIndices = stringIndices.constData(); - } - - const int firstGlyphIndex = outGlyphs - initialGlyph; - outAttributes[0].clusterStart = true; - - CFIndex k = 0; - CFIndex i = 0; - for (i = stringRange.location; - (i < stringRange.location + stringRange.length) && (k < glyphCount); ++i) { - if (tmpIndices[k * rtlSign + rtlOffset] == i || i == stringRange.location) { - logClusters[i] = k + firstGlyphIndex; - outAttributes[k].clusterStart = true; - ++k; - } else { - logClusters[i] = k + firstGlyphIndex - 1; - } - } - // in case of a ligature at the end, fill the remaining logcluster entries - for (;i < stringRange.location + stringRange.length; i++) { - logClusters[i] = k + firstGlyphIndex - 1; - } - } - for (CFIndex i = 0; i < glyphCount - 1; ++i) { - int idx = rtlOffset + rtlSign * i; - outGlyphs[idx] = tmpGlyphs[i] | fontIndex; - outAdvances_x[idx] = QFixed::fromReal(tmpPoints[i + 1].x - tmpPoints[i].x); - outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i + 1].y - tmpPoints[i].y); - - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { - outAdvances_x[idx] = outAdvances_x[idx].round(); - outAdvances_y[idx] = outAdvances_y[idx].round(); - } - } - CGSize lastGlyphAdvance; - CTFontGetAdvancesForGlyphs(runFont, kCTFontHorizontalOrientation, tmpGlyphs + glyphCount - 1, &lastGlyphAdvance, 1); - - outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex; - outAdvances_x[rtl ? 0 : (glyphCount - 1)] = - (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? QFixed::fromReal(lastGlyphAdvance.width).round() - : QFixed::fromReal(lastGlyphAdvance.width); - } - outGlyphs += glyphCount; - outAttributes += glyphCount; - outAdvances_x += glyphCount; - outAdvances_y += glyphCount; - } - *nglyphs = (outGlyphs - initialGlyph); - return !outOBounds; -} - -bool QCoreTextFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, - int *nglyphs, QTextEngine::ShaperFlags flags) const -{ - *nglyphs = len; - QCFType<CFStringRef> cfstring; - - QVarLengthArray<CGGlyph> cgGlyphs(len); - CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len); - - for (int i = 0; i < len; ++i) { - if (cgGlyphs[i]) { - glyphs->glyphs[i] = cgGlyphs[i]; - } else { - if (!cfstring) - cfstring = CFStringCreateWithCharactersNoCopy(0, reinterpret_cast<const UniChar *>(str), len, kCFAllocatorNull); - QCFType<CTFontRef> substituteFont = CTFontCreateForString(ctfont, cfstring, CFRangeMake(i, 1)); - CGGlyph substituteGlyph = 0; - CTFontGetGlyphsForCharacters(substituteFont, (const UniChar*)str + i, &substituteGlyph, 1); - if (substituteGlyph) { - const uint fontIndex = (fontIndexForFont(substituteFont) << 24); - glyphs->glyphs[i] = substituteGlyph | fontIndex; - if (!(flags & QTextEngine::GlyphIndicesOnly)) { - CGSize advance; - CTFontGetAdvancesForGlyphs(substituteFont, kCTFontHorizontalOrientation, &substituteGlyph, &advance, 1); - glyphs->advances_x[i] = QFixed::fromReal(advance.width); - glyphs->advances_y[i] = QFixed::fromReal(advance.height); - } - } - } - } - - if (flags & QTextEngine::GlyphIndicesOnly) - return true; - - QVarLengthArray<CGSize> advances(len); - CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, cgGlyphs.data(), advances.data(), len); - - for (int i = 0; i < len; ++i) { - if (glyphs->glyphs[i] & 0xff000000) - continue; - glyphs->advances_x[i] = QFixed::fromReal(advances[i].width); - glyphs->advances_y[i] = QFixed::fromReal(advances[i].height); - } - - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { - for (int i = 0; i < len; ++i) { - glyphs->advances_x[i] = glyphs->advances_x[i].round(); - glyphs->advances_y[i] = glyphs->advances_y[i].round(); - } - } - - return true; -} - -void QCoreTextFontEngineMulti::recalcAdvances(int , QGlyphLayout *, QTextEngine::ShaperFlags) const -{ -} -void QCoreTextFontEngineMulti::doKerning(int , QGlyphLayout *, QTextEngine::ShaperFlags) const -{ -} - -void QCoreTextFontEngineMulti::loadEngine(int) -{ - // Do nothing - Q_ASSERT(false); -} - - - -QCoreTextFontEngine::QCoreTextFontEngine(CTFontRef font, const QFontDef &def, - QCoreTextFontEngineMulti *multiEngine) -{ - fontDef = def; - parentEngine = multiEngine; - synthesisFlags = 0; - ctfont = font; - CFRetain(ctfont); - ATSFontRef atsfont = CTFontGetPlatformFont(ctfont, 0); - cgFont = CGFontCreateWithPlatformFont(&atsfont); - CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont); - if (fontDef.weight >= QFont::Bold && !(traits & kCTFontBoldTrait)) { - synthesisFlags |= SynthesizedBold; - } - - if (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait)) { - synthesisFlags |= SynthesizedItalic; - } - transform = CGAffineTransformIdentity; - if (fontDef.stretch != 100) { - transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); - } - QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); - if (os2Table.size() >= 10) - fsType = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 8)); -} - -QCoreTextFontEngine::~QCoreTextFontEngine() -{ - CFRelease(ctfont); - CFRelease(cgFont); -} - -bool QCoreTextFontEngine::stringToCMap(const QChar *, int, QGlyphLayout *, int *, QTextEngine::ShaperFlags) const -{ - return false; -} - -glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs) -{ - QFixed w; - bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics; - - for (int i = 0; i < glyphs.numGlyphs; ++i) { - w += round ? glyphs.effectiveAdvance(i).round() - : glyphs.effectiveAdvance(i); - } - return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), ascent()+descent(), w, 0); -} -glyph_metrics_t QCoreTextFontEngine::boundingBox(glyph_t glyph) -{ - glyph_metrics_t ret; - CGGlyph g = glyph; - CGRect rect = CTFontGetBoundingRectsForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, 0, 1); - if (synthesisFlags & QFontEngine::SynthesizedItalic) { - rect.size.width += rect.size.height * SYNTHETIC_ITALIC_SKEW; - } - ret.width = QFixed::fromReal(rect.size.width); - ret.height = QFixed::fromReal(rect.size.height); - ret.x = QFixed::fromReal(rect.origin.x); - ret.y = -QFixed::fromReal(rect.origin.y) - ret.height; - CGSize advances[1]; - CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, &g, advances, 1); - ret.xoff = QFixed::fromReal(advances[0].width); - ret.yoff = QFixed::fromReal(advances[0].height); - - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { - ret.xoff = ret.xoff.round(); - ret.yoff = ret.yoff.round(); - } - - return ret; -} - -QFixed QCoreTextFontEngine::ascent() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? QFixed::fromReal(CTFontGetAscent(ctfont)).round() - : QFixed::fromReal(CTFontGetAscent(ctfont)); -} -QFixed QCoreTextFontEngine::descent() const -{ - QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont)); - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - d = d.round(); - - // subtract a pixel to even out the historical +1 in QFontMetrics::height(). - // Fix in Qt 5. - return d - 1; -} -QFixed QCoreTextFontEngine::leading() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? QFixed::fromReal(CTFontGetLeading(ctfont)).round() - : QFixed::fromReal(CTFontGetLeading(ctfont)); -} -QFixed QCoreTextFontEngine::xHeight() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? QFixed::fromReal(CTFontGetXHeight(ctfont)).round() - : QFixed::fromReal(CTFontGetXHeight(ctfont)); -} -QFixed QCoreTextFontEngine::averageCharWidth() const -{ - // ### Need to implement properly and get the information from the OS/2 Table. - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? QFontEngine::averageCharWidth().round() - : QFontEngine::averageCharWidth(); -} - -qreal QCoreTextFontEngine::maxCharWidth() const -{ - // ### Max Help! - return 0; - -} -qreal QCoreTextFontEngine::minLeftBearing() const -{ - // ### Min Help! - return 0; - -} -qreal QCoreTextFontEngine::minRightBearing() const -{ - // ### Max Help! (even thought it's right) - return 0; - -} - -void QCoreTextFontEngine::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight) -{ - QVarLengthArray<QFixedPoint> positions; - QVarLengthArray<glyph_t> glyphs; - QTransform matrix; - matrix.translate(x, y); - getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); - if (glyphs.size() == 0) - return; - - CGContextSetFontSize(ctx, fontDef.pixelSize); - - CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx); - - CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight); - - CGAffineTransformConcat(cgMatrix, oldTextMatrix); - - if (synthesisFlags & QFontEngine::SynthesizedItalic) - cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0)); - - cgMatrix = CGAffineTransformConcat(cgMatrix, transform); - - CGContextSetTextMatrix(ctx, cgMatrix); - - CGContextSetTextDrawingMode(ctx, kCGTextFill); - - - QVarLengthArray<CGSize> advances(glyphs.size()); - QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size()); - - for (int i = 0; i < glyphs.size() - 1; ++i) { - advances[i].width = (positions[i + 1].x - positions[i].x).toReal(); - advances[i].height = (positions[i + 1].y - positions[i].y).toReal(); - cgGlyphs[i] = glyphs[i]; - } - advances[glyphs.size() - 1].width = 0; - advances[glyphs.size() - 1].height = 0; - cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1]; - - CGContextSetFont(ctx, cgFont); - //NSLog(@"Font inDraw %@ ctfont %@", CGFontCopyFullName(cgFont), CTFontCopyFamilyName(ctfont)); - - CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal()); - - CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size()); - - if (synthesisFlags & QFontEngine::SynthesizedBold) { - CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(), - positions[0].y.toReal()); - - CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size()); - } - - CGContextSetTextMatrix(ctx, oldTextMatrix); -} - -struct ConvertPathInfo -{ - ConvertPathInfo(QPainterPath *newPath, const QPointF &newPos) : path(newPath), pos(newPos) {} - QPainterPath *path; - QPointF pos; -}; - -static void convertCGPathToQPainterPath(void *info, const CGPathElement *element) -{ - ConvertPathInfo *myInfo = static_cast<ConvertPathInfo *>(info); - switch(element->type) { - case kCGPathElementMoveToPoint: - myInfo->path->moveTo(element->points[0].x + myInfo->pos.x(), - element->points[0].y + myInfo->pos.y()); - break; - case kCGPathElementAddLineToPoint: - myInfo->path->lineTo(element->points[0].x + myInfo->pos.x(), - element->points[0].y + myInfo->pos.y()); - break; - case kCGPathElementAddQuadCurveToPoint: - myInfo->path->quadTo(element->points[0].x + myInfo->pos.x(), - element->points[0].y + myInfo->pos.y(), - element->points[1].x + myInfo->pos.x(), - element->points[1].y + myInfo->pos.y()); - break; - case kCGPathElementAddCurveToPoint: - myInfo->path->cubicTo(element->points[0].x + myInfo->pos.x(), - element->points[0].y + myInfo->pos.y(), - element->points[1].x + myInfo->pos.x(), - element->points[1].y + myInfo->pos.y(), - element->points[2].x + myInfo->pos.x(), - element->points[2].y + myInfo->pos.y()); - break; - case kCGPathElementCloseSubpath: - myInfo->path->closeSubpath(); - break; - default: - qDebug() << "Unhandled path transform type: " << element->type; - } - -} - -void QCoreTextFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs, - QPainterPath *path, QTextItem::RenderFlags) -{ - - CGAffineTransform cgMatrix = CGAffineTransformIdentity; - cgMatrix = CGAffineTransformScale(cgMatrix, 1, -1); - - if (synthesisFlags & QFontEngine::SynthesizedItalic) - cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0)); - - - for (int i = 0; i < nGlyphs; ++i) { - QCFType<CGPathRef> cgpath = CTFontCreatePathForGlyph(ctfont, glyphs[i], &cgMatrix); - ConvertPathInfo info(path, positions[i].toPointF()); - CGPathApply(cgpath, &info, convertCGPathToQPainterPath); - } -} - -QImage QCoreTextFontEngine::imageForGlyph(glyph_t glyph, int margin, bool aa) -{ - const glyph_metrics_t br = boundingBox(glyph); - QImage im(qRound(br.width)+2, qRound(br.height)+2, QImage::Format_RGB32); - im.fill(0); - - CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace(); - uint cgflags = kCGImageAlphaNoneSkipFirst; -#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version - cgflags |= kCGBitmapByteOrder32Host; -#endif - CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(), - 8, im.bytesPerLine(), colorspace, - cgflags); - CGContextSetFontSize(ctx, fontDef.pixelSize); - CGContextSetShouldAntialias(ctx, aa || - (fontDef.pointSize > qt_antialiasing_threshold - && !(fontDef.styleStrategy & QFont::NoAntialias))); - CGContextSetShouldSmoothFonts(ctx, aa); - CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx); - CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0); - - CGAffineTransformConcat(cgMatrix, oldTextMatrix); - - if (synthesisFlags & QFontEngine::SynthesizedItalic) - cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, SYNTHETIC_ITALIC_SKEW, 1, 0, 0)); - - cgMatrix = CGAffineTransformConcat(cgMatrix, transform); - - CGContextSetTextMatrix(ctx, cgMatrix); - CGContextSetRGBFillColor(ctx, 1, 1, 1, 1); - CGContextSetTextDrawingMode(ctx, kCGTextFill); - - ATSFontRef atsfont = CTFontGetPlatformFont(ctfont, 0); - QCFType<CGFontRef> cgFont = CGFontCreateWithPlatformFont(&atsfont); - CGContextSetFont(ctx, cgFont); - - qreal pos_x = -br.x.toReal()+1, pos_y = im.height()+br.y.toReal(); - CGContextSetTextPosition(ctx, pos_x, pos_y); - - CGSize advance; - advance.width = 0; - advance.height = 0; - CGGlyph cgGlyph = glyph; - CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1); - - if (synthesisFlags & QFontEngine::SynthesizedBold) { - CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y); - CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1); - } - - CGContextRelease(ctx); - - return im; -} - -QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph) -{ - QImage im = imageForGlyph(glyph, 0, false); - - QImage indexed(im.width(), im.height(), QImage::Format_Indexed8); - QVector<QRgb> colors(256); - for (int i=0; i<256; ++i) - colors[i] = qRgba(0, 0, 0, i); - indexed.setColorTable(colors); - - for (int y=0; y<im.height(); ++y) { - uint *src = (uint*) im.scanLine(y); - uchar *dst = indexed.scanLine(y); - for (int x=0; x<im.width(); ++x) { - *dst = qGray(*src); - ++dst; - ++src; - } - } - - return indexed; -} - -QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTransform &x) -{ - if (x.type() >= QTransform::TxScale) - return QFontEngine::alphaRGBMapForGlyph(glyph, margin, x); - - QImage im = imageForGlyph(glyph, margin, true); - qmacfontengine_gamma_correct(&im); - return im; -} - -void QCoreTextFontEngine::recalcAdvances(int numGlyphs, QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const -{ - Q_ASSERT(false); - Q_UNUSED(numGlyphs); - Q_UNUSED(glyphs); - Q_UNUSED(flags); -} - -QFontEngine::FaceId QCoreTextFontEngine::faceId() const -{ - return QFontEngine::FaceId(); -} - -bool QCoreTextFontEngine::canRender(const QChar *string, int len) -{ - QCFType<CTFontRef> retFont = CTFontCreateForString(ctfont, - QCFType<CFStringRef>(CFStringCreateWithCharactersNoCopy(0, - reinterpret_cast<const UniChar *>(string), - len, kCFAllocatorNull)), - CFRangeMake(0, len)); - return retFont != 0; - return false; -} - - bool QCoreTextFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const - { - QCFType<CFDataRef> table = CTFontCopyTable(ctfont, tag, 0); - if (!table || !length) - return false; - CFIndex tableLength = CFDataGetLength(table); - int availableLength = *length; - *length = tableLength; - if (buffer) { - if (tableLength > availableLength) - return false; - CFDataGetBytes(table, CFRangeMake(0, tableLength), buffer); - } - return true; - } - -void QCoreTextFontEngine::getUnscaledGlyph(glyph_t, QPainterPath *, glyph_metrics_t *) -{ - // ### -} - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - #ifndef QT_MAC_USE_COCOA QFontEngineMacMulti::QFontEngineMacMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning) : QFontEngineMulti(0) @@ -934,10 +257,8 @@ static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATS #if !defined(QT_NO_DEBUG) int surrogates = 0; const QChar *str = item->string; - for (int i = item->from; i < item->from + item->length - 1; ++i) { - surrogates += (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 - && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000); - } + for (int i = item->from; i < item->from + item->length - 1; ++i) + surrogates += (str[i].isHighSurrogate() && str[i+1].isLowSurrogate()); #endif for (nextCharStop = item->from; nextCharStop < item->from + item->length; ++nextCharStop) if (item->charAttributes[nextCharStop].charStop) @@ -1005,10 +326,8 @@ static OSStatus atsuPostLayoutCallback(ATSULayoutOperationSelector selector, ATS if (charOffset < item->length - 1) { QChar current = item->string[item->from + charOffset]; QChar next = item->string[item->from + charOffset + 1]; - if (current.unicode() >= 0xd800 && current.unicode() < 0xdc00 - && next.unicode() >= 0xdc00 && next.unicode() < 0xe000) { + if (current.isHighSurrogate() && next.isLowSurrogate()) item->log_clusters[charOffset + 1] = currentClusterGlyph; - } } } } @@ -1054,7 +373,7 @@ bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout * } bool QFontEngineMacMulti::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags, - unsigned short *logClusters, const HB_CharAttributes *charAttributes) const + unsigned short *logClusters, const HB_CharAttributes *charAttributes, QScriptItem *) const { if (*nglyphs < len) { *nglyphs = len; @@ -1415,17 +734,15 @@ QFontEngineMac::~QFontEngineMac() static inline unsigned int getChar(const QChar *str, int &i, const int len) { - unsigned int uc = str[i].unicode(); - if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) { - uint low = str[i+1].unicode(); - if (low >= 0xdc00 && low < 0xe000) { - uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000; - ++i; - } + uint ucs4 = str[i].unicode(); + if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) { + ++i; + ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode()); } - return uc; + return ucs4; } +// Not used directly for shaping, only used to calculate m_averageCharWidth bool QFontEngineMac::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const { if (!cmap) { @@ -1632,7 +949,7 @@ QImage QFontEngineMac::imageForGlyph(glyph_t glyph, int margin, bool colorful) CGAffineTransformConcat(cgMatrix, oldTextMatrix); if (synthesisFlags & QFontEngine::SynthesizedItalic) - cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, SYNTHETIC_ITALIC_SKEW, 1, 0, 0)); + cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0)); cgMatrix = CGAffineTransformConcat(cgMatrix, transform); @@ -1684,7 +1001,7 @@ QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph) return indexed; } -QImage QFontEngineMac::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTransform &t) +QImage QFontEngineMac::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, const QTransform &t) { QImage im = imageForGlyph(glyph, margin, true); @@ -1692,7 +1009,7 @@ QImage QFontEngineMac::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTra im = im.transformed(t); } - qmacfontengine_gamma_correct(&im); + qGamma_correct_back_to_linear_cs(&im); return im; } @@ -1725,7 +1042,7 @@ void QFontEngineMac::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt CGAffineTransformConcat(cgMatrix, oldTextMatrix); if (synthesisFlags & QFontEngine::SynthesizedItalic) - cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -SYNTHETIC_ITALIC_SKEW, 1, 0, 0)); + cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0)); cgMatrix = CGAffineTransformConcat(cgMatrix, transform); diff --git a/src/gui/text/qfontengine_mac_p.h b/src/gui/text/qfontengine_mac_p.h new file mode 100644 index 0000000..10561e5 --- /dev/null +++ b/src/gui/text/qfontengine_mac_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFONTENGINE_MAC_P_H +#define QFONTENGINE_MAC_P_H + +#include <private/qfontengine_p.h> + +#ifndef QT_MAC_USE_COCOA +class QFontEngineMacMulti; +class QFontEngineMac : public QFontEngine +{ + friend class QFontEngineMacMulti; +public: + QFontEngineMac(ATSUStyle baseStyle, ATSUFontID fontID, const QFontDef &def, QFontEngineMacMulti *multiEngine = 0); + virtual ~QFontEngineMac(); + + virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *numGlyphs, QTextEngine::ShaperFlags flags) const; + virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const; + + virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs); + virtual glyph_metrics_t boundingBox(glyph_t glyph); + + virtual QFixed ascent() const; + virtual QFixed descent() const; + virtual QFixed leading() const; + virtual QFixed xHeight() const; + virtual qreal maxCharWidth() const; + virtual QFixed averageCharWidth() const; + + virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, + QPainterPath *path, QTextItem::RenderFlags); + + virtual const char *name() const { return "QFontEngineMac"; } + + virtual bool canRender(const QChar *string, int len); + + virtual int synthesized() const { return synthesisFlags; } + + virtual Type type() const { return QFontEngine::Mac; } + + void draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight); + + virtual FaceId faceId() const; + virtual QByteArray getSfntTable(uint tag) const; + virtual Properties properties() const; + virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics); + virtual QImage alphaMapForGlyph(glyph_t); + virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); + +private: + QImage imageForGlyph(glyph_t glyph, int margin, bool colorful); + + ATSUFontID fontID; + QCFType<CGFontRef> cgFont; + ATSUStyle style; + int synthesisFlags; + mutable QGlyphLayout kashidaGlyph; + QFontEngineMacMulti *multiEngine; + mutable const unsigned char *cmap; + mutable bool symbolCMap; + mutable QByteArray cmapTable; + CGAffineTransform transform; + QFixed m_ascent; + QFixed m_descent; + QFixed m_leading; + qreal m_maxCharWidth; + QFixed m_xHeight; + QFixed m_averageCharWidth; +}; + +class QFontEngineMacMulti : public QFontEngineMulti +{ + friend class QFontEngineMac; +public: + // internal + struct ShaperItem + { + inline ShaperItem() : string(0), from(0), length(0), + log_clusters(0), charAttributes(0) {} + + const QChar *string; + int from; + int length; + QGlyphLayout glyphs; + unsigned short *log_clusters; + const HB_CharAttributes *charAttributes; + QTextEngine::ShaperFlags flags; + }; + + QFontEngineMacMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning); + virtual ~QFontEngineMacMulti(); + + virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const; + bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags, + unsigned short *logClusters, const HB_CharAttributes *charAttributes, QScriptItem *) const; + + virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const; + virtual void doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const; + + virtual const char *name() const { return "ATSUI"; } + + virtual bool canRender(const QChar *string, int len); + + inline ATSUFontID macFontID() const { return fontID; } + +protected: + virtual void loadEngine(int at); + +private: + inline const QFontEngineMac *engineAt(int i) const + { return static_cast<const QFontEngineMac *>(engines.at(i)); } + + bool stringToCMapInternal(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags, ShaperItem *item) const; + + int fontIndexForFontID(ATSUFontID id) const; + + ATSUFontID fontID; + uint kerning : 1; + + mutable ATSUTextLayout textLayout; + mutable ATSUStyle style; + CGAffineTransform transform; +}; +#endif //!QT_MAC_USE_COCOA + +#endif // QFONTENGINE_MAC_P_H diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index c043929..8d81acb 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -117,9 +117,19 @@ public: // S60 types S60FontEngine, // Cannot be simply called "S60". Reason is qt_s60Data.h + DirectWrite, + TestFontEngine = 0x1000 }; + enum GlyphFormat { + Format_None, + Format_Render = Format_None, + Format_Mono, + Format_A8, + Format_A32 + }; + QFontEngine(); virtual ~QFontEngine(); @@ -145,6 +155,7 @@ public: struct FaceId { FaceId() : index(0), encoding(0) {} QByteArray filename; + QByteArray uuid; int index; int encoding; }; @@ -155,6 +166,7 @@ public: SynthesizedStretch = 0x4 }; virtual int synthesized() const { return 0; } + virtual bool supportsSubPixelPositions() const { return false; } virtual QFixed emSquareSize() const { return ascent(); } @@ -168,11 +180,12 @@ public: virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const {} virtual void doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const; -#if !defined(Q_WS_X11) && !defined(Q_WS_WIN) && !defined(Q_WS_MAC) && !defined(Q_OS_SYMBIAN) +#if !defined(Q_WS_X11) && !defined(Q_WS_WIN) && !defined(Q_WS_MAC) && !defined(Q_OS_SYMBIAN) && !defined(Q_WS_QPA) virtual void draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &si) = 0; #endif virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, QPainterPath *path, QTextItem::RenderFlags flags); + void getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags, QVarLengthArray<glyph_t> &glyphs_out, QVarLengthArray<QFixedPoint> &positions); @@ -183,8 +196,15 @@ public: * Returns an image indexed_8 with index values ranging from 0=fully transparent to 255=opaque */ virtual QImage alphaMapForGlyph(glyph_t); + virtual QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition); virtual QImage alphaMapForGlyph(glyph_t, const QTransform &t); - virtual QImage alphaRGBMapForGlyph(glyph_t, int margin, const QTransform &t); + virtual QImage alphaMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t); + virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); + + virtual glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, QFixed /*subPixelPosition*/, const QTransform &matrix, GlyphFormat /*format*/) + { + return boundingBox(glyph, matrix); + } virtual void removeGlyphFromCache(glyph_t); @@ -216,6 +236,8 @@ public: virtual int glyphCount() const; + virtual QFontEngine *cloneWithSize(qreal /*pixelSize*/) const { return 0; } + HB_Font harfbuzzFont() const; HB_Face harfbuzzFace() const; @@ -237,7 +259,7 @@ public: bool symbol; mutable HB_FontRec hbFont; mutable HB_Face hbFace; -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_QPA) || defined(Q_OS_SYMBIAN) struct KernPair { uint left_right; QFixed adjust; @@ -260,7 +282,7 @@ protected: private: struct GlyphCacheEntry { void *context; - QFontEngineGlyphCache *cache; + QExplicitlySharedDataPointer<QFontEngineGlyphCache> cache; bool operator==(const GlyphCacheEntry &other) { return context == other.context && cache == other.cache; } }; @@ -274,7 +296,7 @@ inline bool operator ==(const QFontEngine::FaceId &f1, const QFontEngine::FaceId inline uint qHash(const QFontEngine::FaceId &f) { - return qHash((f.index << 16) + f.encoding) + qHash(f.filename); + return qHash((f.index << 16) + f.encoding) + qHash(f.filename + f.uuid); } @@ -408,218 +430,11 @@ public: protected: friend class QPSPrintEnginePrivate; friend class QPSPrintEngineFontMulti; + friend class QRawFont; virtual void loadEngine(int at) = 0; QVector<QFontEngine *> engines; }; -#if defined(Q_WS_MAC) - -struct QCharAttributes; -class QFontEngineMacMulti; -# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 -class QCoreTextFontEngineMulti; -class QCoreTextFontEngine : public QFontEngine -{ -public: - QCoreTextFontEngine(CTFontRef font, const QFontDef &def, - QCoreTextFontEngineMulti *multiEngine = 0); - ~QCoreTextFontEngine(); - virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const; - virtual void recalcAdvances(int , QGlyphLayout *, QTextEngine::ShaperFlags) const; - - virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs); - virtual glyph_metrics_t boundingBox(glyph_t glyph); - - virtual QFixed ascent() const; - virtual QFixed descent() const; - virtual QFixed leading() const; - virtual QFixed xHeight() const; - virtual qreal maxCharWidth() const; - virtual QFixed averageCharWidth() const; - - virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, - QPainterPath *path, QTextItem::RenderFlags); - - virtual const char *name() const { return "QCoreTextFontEngine"; } - - virtual bool canRender(const QChar *string, int len); - - virtual int synthesized() const { return synthesisFlags; } - - virtual Type type() const { return QFontEngine::Mac; } - - void draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight); - - virtual FaceId faceId() const; - virtual bool getSfntTableData(uint /*tag*/, uchar * /*buffer*/, uint * /*length*/) const; - virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics); - virtual QImage alphaMapForGlyph(glyph_t); - virtual QImage alphaRGBMapForGlyph(glyph_t, int margin, const QTransform &t); - virtual qreal minRightBearing() const; - virtual qreal minLeftBearing() const; - - -private: - QImage imageForGlyph(glyph_t glyph, int margin, bool colorful); - CTFontRef ctfont; - CGFontRef cgFont; - QCoreTextFontEngineMulti *parentEngine; - int synthesisFlags; - CGAffineTransform transform; - friend class QCoreTextFontEngineMulti; -}; - -class QCoreTextFontEngineMulti : public QFontEngineMulti -{ -public: - QCoreTextFontEngineMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, - const QFontDef &fontDef, bool kerning); - ~QCoreTextFontEngineMulti(); - - virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, - QTextEngine::ShaperFlags flags) const; - bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, - QTextEngine::ShaperFlags flags, - unsigned short *logClusters, const HB_CharAttributes *charAttributes) const; - - - virtual void recalcAdvances(int , QGlyphLayout *, QTextEngine::ShaperFlags) const; - virtual void doKerning(int , QGlyphLayout *, QTextEngine::ShaperFlags) const; - - virtual const char *name() const { return "CoreText"; } -protected: - virtual void loadEngine(int at); - -private: - inline const QCoreTextFontEngine *engineAt(int i) const - { return static_cast<const QCoreTextFontEngine *>(engines.at(i)); } - - uint fontIndexForFont(CTFontRef id) const; - CTFontRef ctfont; - mutable QCFType<CFMutableDictionaryRef> attributeDict; - CGAffineTransform transform; - friend class QFontDialogPrivate; -}; -# endif //MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - -#ifndef QT_MAC_USE_COCOA -class QFontEngineMac : public QFontEngine -{ - friend class QFontEngineMacMulti; -public: - QFontEngineMac(ATSUStyle baseStyle, ATSUFontID fontID, const QFontDef &def, QFontEngineMacMulti *multiEngine = 0); - virtual ~QFontEngineMac(); - - virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *numGlyphs, QTextEngine::ShaperFlags flags) const; - virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const; - - virtual glyph_metrics_t boundingBox(const QGlyphLayout &glyphs); - virtual glyph_metrics_t boundingBox(glyph_t glyph); - - virtual QFixed ascent() const; - virtual QFixed descent() const; - virtual QFixed leading() const; - virtual QFixed xHeight() const; - virtual qreal maxCharWidth() const; - virtual QFixed averageCharWidth() const; - - virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs, - QPainterPath *path, QTextItem::RenderFlags); - - virtual const char *name() const { return "QFontEngineMac"; } - - virtual bool canRender(const QChar *string, int len); - - virtual int synthesized() const { return synthesisFlags; } - - virtual Type type() const { return QFontEngine::Mac; } - - void draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight); - - virtual FaceId faceId() const; - virtual QByteArray getSfntTable(uint tag) const; - virtual Properties properties() const; - virtual void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics); - virtual QImage alphaMapForGlyph(glyph_t); - virtual QImage alphaRGBMapForGlyph(glyph_t, int margin, const QTransform &t); - -private: - QImage imageForGlyph(glyph_t glyph, int margin, bool colorful); - - ATSUFontID fontID; - QCFType<CGFontRef> cgFont; - ATSUStyle style; - int synthesisFlags; - mutable QGlyphLayout kashidaGlyph; - QFontEngineMacMulti *multiEngine; - mutable const unsigned char *cmap; - mutable bool symbolCMap; - mutable QByteArray cmapTable; - CGAffineTransform transform; - QFixed m_ascent; - QFixed m_descent; - QFixed m_leading; - qreal m_maxCharWidth; - QFixed m_xHeight; - QFixed m_averageCharWidth; -}; - -class QFontEngineMacMulti : public QFontEngineMulti -{ - friend class QFontEngineMac; -public: - // internal - struct ShaperItem - { - inline ShaperItem() : string(0), from(0), length(0), - log_clusters(0), charAttributes(0) {} - - const QChar *string; - int from; - int length; - QGlyphLayout glyphs; - unsigned short *log_clusters; - const HB_CharAttributes *charAttributes; - QTextEngine::ShaperFlags flags; - }; - - QFontEngineMacMulti(const ATSFontFamilyRef &atsFamily, const ATSFontRef &atsFontRef, const QFontDef &fontDef, bool kerning); - virtual ~QFontEngineMacMulti(); - - virtual bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const; - bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags, - unsigned short *logClusters, const HB_CharAttributes *charAttributes) const; - - virtual void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const; - virtual void doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const; - - virtual const char *name() const { return "ATSUI"; } - - virtual bool canRender(const QChar *string, int len); - - inline ATSUFontID macFontID() const { return fontID; } - -protected: - virtual void loadEngine(int at); - -private: - inline const QFontEngineMac *engineAt(int i) const - { return static_cast<const QFontEngineMac *>(engines.at(i)); } - - bool stringToCMapInternal(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags, ShaperItem *item) const; - - int fontIndexForFontID(ATSUFontID id) const; - - ATSUFontID fontID; - uint kerning : 1; - - mutable ATSUTextLayout textLayout; - mutable ATSUStyle style; - CGAffineTransform transform; -}; -#endif //!QT_MAC_USE_COCOA -#endif - class QTestFontEngine : public QFontEngineBox { public: diff --git a/src/gui/text/qfontengine_qpa.cpp b/src/gui/text/qfontengine_qpa.cpp new file mode 100644 index 0000000..c829c2f --- /dev/null +++ b/src/gui/text/qfontengine_qpa.cpp @@ -0,0 +1,688 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfontengine_qpa_p.h" + +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QBuffer> + +#include <QtGui/private/qapplication_p.h> +#include <QtGui/QPlatformFontDatabase> +#include <QtGui/private/qpaintengine_raster_p.h> + +QT_BEGIN_NAMESPACE + +//#define DEBUG_HEADER +//#define DEBUG_FONTENGINE + +static QFontEngineQPA::TagType tagTypes[QFontEngineQPA::NumTags] = { + QFontEngineQPA::StringType, // FontName + QFontEngineQPA::StringType, // FileName + QFontEngineQPA::UInt32Type, // FileIndex + QFontEngineQPA::UInt32Type, // FontRevision + QFontEngineQPA::StringType, // FreeText + QFontEngineQPA::FixedType, // Ascent + QFontEngineQPA::FixedType, // Descent + QFontEngineQPA::FixedType, // Leading + QFontEngineQPA::FixedType, // XHeight + QFontEngineQPA::FixedType, // AverageCharWidth + QFontEngineQPA::FixedType, // MaxCharWidth + QFontEngineQPA::FixedType, // LineThickness + QFontEngineQPA::FixedType, // MinLeftBearing + QFontEngineQPA::FixedType, // MinRightBearing + QFontEngineQPA::FixedType, // UnderlinePosition + QFontEngineQPA::UInt8Type, // GlyphFormat + QFontEngineQPA::UInt8Type, // PixelSize + QFontEngineQPA::UInt8Type, // Weight + QFontEngineQPA::UInt8Type, // Style + QFontEngineQPA::StringType, // EndOfHeader + QFontEngineQPA::BitFieldType// WritingSystems +}; + + +#if defined(DEBUG_HEADER) +# define DEBUG_VERIFY qDebug +#else +# define DEBUG_VERIFY if (0) qDebug +#endif + +#define READ_VERIFY(type, variable) \ + if (tagPtr + sizeof(type) > endPtr) { \ + DEBUG_VERIFY() << "read verify failed in line" << __LINE__; \ + return 0; \ + } \ + variable = qFromBigEndian<type>(tagPtr); \ + DEBUG_VERIFY() << "read value" << variable << "of type " #type; \ + tagPtr += sizeof(type) + +template <typename T> +T readValue(const uchar *&data) +{ + T value = qFromBigEndian<T>(data); + data += sizeof(T); + return value; +} + +#define VERIFY(condition) \ + if (!(condition)) { \ + DEBUG_VERIFY() << "condition " #condition " failed in line" << __LINE__; \ + return 0; \ + } + +#define VERIFY_TAG(condition) \ + if (!(condition)) { \ + DEBUG_VERIFY() << "verifying tag condition " #condition " failed in line" << __LINE__ << "with tag" << tag; \ + return 0; \ + } + +static inline const uchar *verifyTag(const uchar *tagPtr, const uchar *endPtr) +{ + quint16 tag, length; + READ_VERIFY(quint16, tag); + READ_VERIFY(quint16, length); + if (tag == QFontEngineQPA::Tag_EndOfHeader) + return endPtr; + if (tag < QFontEngineQPA::NumTags) { + switch (tagTypes[tag]) { + case QFontEngineQPA::BitFieldType: + case QFontEngineQPA::StringType: + // can't do anything... + break; + case QFontEngineQPA::UInt32Type: + VERIFY_TAG(length == sizeof(quint32)); + break; + case QFontEngineQPA::FixedType: + VERIFY_TAG(length == sizeof(quint32)); + break; + case QFontEngineQPA::UInt8Type: + VERIFY_TAG(length == sizeof(quint8)); + break; + } +#if defined(DEBUG_HEADER) + if (length == 1) + qDebug() << "tag data" << hex << *tagPtr; + else if (length == 4) + qDebug() << "tag data" << hex << tagPtr[0] << tagPtr[1] << tagPtr[2] << tagPtr[3]; +#endif + } + return tagPtr + length; +} + +const QFontEngineQPA::Glyph *QFontEngineQPA::findGlyph(glyph_t g) const +{ + if (!g || g >= glyphMapEntries) + return 0; + const quint32 *gmapPtr = reinterpret_cast<const quint32 *>(fontData + glyphMapOffset); + quint32 glyphPos = qFromBigEndian<quint32>(gmapPtr[g]); + if (glyphPos > glyphDataSize) { + if (glyphPos == 0xffffffff) + return 0; +#if defined(DEBUG_FONTENGINE) + qDebug() << "glyph" << g << "outside of glyphData, remapping font file"; +#endif + if (glyphPos > glyphDataSize) + return 0; + } + return reinterpret_cast<const Glyph *>(fontData + glyphDataOffset + glyphPos); +} + +bool QFontEngineQPA::verifyHeader(const uchar *data, int size) +{ + VERIFY(size >= int(sizeof(Header))); + const Header *header = reinterpret_cast<const Header *>(data); + if (header->magic[0] != 'Q' + || header->magic[1] != 'P' + || header->magic[2] != 'F' + || header->magic[3] != '2') + return false; + + VERIFY(header->majorVersion <= CurrentMajorVersion); + const quint16 dataSize = qFromBigEndian<quint16>(header->dataSize); + VERIFY(size >= int(sizeof(Header)) + dataSize); + + const uchar *tagPtr = data + sizeof(Header); + const uchar *tagEndPtr = tagPtr + dataSize; + while (tagPtr < tagEndPtr - 3) { + tagPtr = verifyTag(tagPtr, tagEndPtr); + VERIFY(tagPtr); + } + + VERIFY(tagPtr <= tagEndPtr); + return true; +} + +QVariant QFontEngineQPA::extractHeaderField(const uchar *data, HeaderTag requestedTag) +{ + const Header *header = reinterpret_cast<const Header *>(data); + const uchar *tagPtr = data + sizeof(Header); + const uchar *endPtr = tagPtr + qFromBigEndian<quint16>(header->dataSize); + while (tagPtr < endPtr - 3) { + quint16 tag = readValue<quint16>(tagPtr); + quint16 length = readValue<quint16>(tagPtr); + if (tag == requestedTag) { + switch (tagTypes[requestedTag]) { + case StringType: + return QVariant(QString::fromUtf8(reinterpret_cast<const char *>(tagPtr), length)); + case UInt32Type: + return QVariant(readValue<quint32>(tagPtr)); + case UInt8Type: + return QVariant(uint(*tagPtr)); + case FixedType: + return QVariant(QFixed::fromFixed(readValue<quint32>(tagPtr)).toReal()); + case BitFieldType: + return QVariant(QByteArray(reinterpret_cast<const char *>(tagPtr), length)); + } + return QVariant(); + } else if (tag == Tag_EndOfHeader) { + break; + } + tagPtr += length; + } + + return QVariant(); +} + + + +static inline unsigned int getChar(const QChar *str, int &i, const int len) +{ + uint ucs4 = str[i].unicode(); + if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) { + ++i; + ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode()); + } + return ucs4; +} + +QFontEngineQPA::QFontEngineQPA(const QFontDef &def, const QByteArray &data) + : fontData(reinterpret_cast<const uchar *>(data.constData())), dataSize(data.size()) +{ + fontDef = def; + cache_cost = 100; + externalCMap = 0; + cmapOffset = 0; + cmapSize = 0; + glyphMapOffset = 0; + glyphMapEntries = 0; + glyphDataOffset = 0; + glyphDataSize = 0; + kerning_pairs_loaded = false; + readOnly = true; + +#if defined(DEBUG_FONTENGINE) + qDebug() << "QFontEngineQPA::QFontEngineQPA( fd =" << fd << ", renderingFontEngine =" << renderingFontEngine << ')'; +#endif + + if (!verifyHeader(fontData, dataSize)) { +#if defined(DEBUG_FONTENGINE) + qDebug() << "verifyHeader failed!"; +#endif + return; + } + + const Header *header = reinterpret_cast<const Header *>(fontData); + + readOnly = (header->lock == 0xffffffff); + + const uchar *imgData = fontData + sizeof(Header) + qFromBigEndian<quint16>(header->dataSize); + const uchar *endPtr = fontData + dataSize; + while (imgData <= endPtr - 8) { + quint16 blockTag = readValue<quint16>(imgData); + imgData += 2; // skip padding + quint32 blockSize = readValue<quint32>(imgData); + + if (blockTag == CMapBlock) { + cmapOffset = imgData - fontData; + cmapSize = blockSize; + } else if (blockTag == GMapBlock) { + glyphMapOffset = imgData - fontData; + glyphMapEntries = blockSize / 4; + } else if (blockTag == GlyphBlock) { + glyphDataOffset = imgData - fontData; + glyphDataSize = blockSize; + } + + imgData += blockSize; + } + + face_id.filename = QFile::encodeName(extractHeaderField(fontData, Tag_FileName).toString()); + face_id.index = extractHeaderField(fontData, Tag_FileIndex).toInt(); + + // get the real cmap + if (cmapOffset) { + int tableSize = cmapSize; + const uchar *cmapPtr = getCMap(fontData + cmapOffset, tableSize, &symbol, &cmapSize); + if (cmapPtr) + cmapOffset = cmapPtr - fontData; + else + cmapOffset = 0; + } else if (externalCMap) { + int tableSize = cmapSize; + externalCMap = getCMap(externalCMap, tableSize, &symbol, &cmapSize); + } + + // verify all the positions in the glyphMap + if (glyphMapOffset) { + const quint32 *gmapPtr = reinterpret_cast<const quint32 *>(fontData + glyphMapOffset); + for (uint i = 0; i < glyphMapEntries; ++i) { + quint32 glyphDataPos = qFromBigEndian<quint32>(gmapPtr[i]); + if (glyphDataPos == 0xffffffff) + continue; + if (glyphDataPos >= glyphDataSize) { + // error + glyphMapOffset = 0; + glyphMapEntries = 0; + break; + } + } + } + +#if defined(DEBUG_FONTENGINE) + if (!isValid()) + qDebug() << "fontData" << fontData << "dataSize" << dataSize + << "externalCMap" << externalCMap << "cmapOffset" << cmapOffset + << "glyphMapOffset" << glyphMapOffset << "glyphDataOffset" << glyphDataOffset + << "fd" << fd << "glyphDataSize" << glyphDataSize; +#endif +} + +QFontEngineQPA::~QFontEngineQPA() +{ +} + +bool QFontEngineQPA::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + Q_UNUSED(tag); + Q_UNUSED(buffer); + *length = 0; + return false; +} + +bool QFontEngineQPA::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const +{ + if (*nglyphs < len) { + *nglyphs = len; + return false; + } + +#if defined(DEBUG_FONTENGINE) + QSet<QChar> seenGlyphs; +#endif + + const uchar *cmap = externalCMap ? externalCMap : (fontData + cmapOffset); + + bool mirrored = flags & QTextEngine::RightToLeft; + int glyph_pos = 0; + if (symbol) { + for (int i = 0; i < len; ++i) { + unsigned int uc = getChar(str, i, len); + if (mirrored) + uc = QChar::mirroredChar(uc); + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc); + if(!glyphs->glyphs[glyph_pos] && uc < 0x100) + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000); + ++glyph_pos; + } + } else { + for (int i = 0; i < len; ++i) { + unsigned int uc = getChar(str, i, len); + if (mirrored) + uc = QChar::mirroredChar(uc); + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc); +#if 0 && defined(DEBUG_FONTENGINE) + QChar c(uc); + if (!findGlyph(glyphs[glyph_pos].glyph) && !seenGlyphs.contains(c)) + qDebug() << "glyph for character" << c << '/' << hex << uc << "is" << dec << glyphs[glyph_pos].glyph; + + seenGlyphs.insert(c); +#endif + ++glyph_pos; + } + } + + *nglyphs = glyph_pos; + glyphs->numGlyphs = glyph_pos; + recalcAdvances(glyphs, flags); + return true; +} + +void QFontEngineQPA::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const +{ + for (int i = 0; i < glyphs->numGlyphs; ++i) { + const Glyph *g = findGlyph(glyphs->glyphs[i]); + if (!g) { + glyphs->glyphs[i] = 0; + continue; + } + glyphs->advances_x[i] = g->advance; + glyphs->advances_y[i] = 0; + } +} + +QImage QFontEngineQPA::alphaMapForGlyph(glyph_t g) +{ + const Glyph *glyph = findGlyph(g); + if (!glyph) + return QImage(); + + const uchar *bits = ((const uchar *) glyph) + sizeof(Glyph); + + QImage image(bits,glyph->width, glyph->height, glyph->bytesPerLine, QImage::Format_Indexed8); + + return image; +} + +void QFontEngineQPA::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) +{ + addBitmapFontToPath(x, y, glyphs, path, flags); +} + +glyph_metrics_t QFontEngineQPA::boundingBox(const QGlyphLayout &glyphs) +{ + glyph_metrics_t overall; + // initialize with line height, we get the same behaviour on all platforms + overall.y = -ascent(); + overall.height = ascent() + descent() + 1; + + QFixed ymax = 0; + QFixed xmax = 0; + for (int i = 0; i < glyphs.numGlyphs; i++) { + const Glyph *g = findGlyph(glyphs.glyphs[i]); + if (!g) + continue; + + QFixed x = overall.xoff + glyphs.offsets[i].x + g->x; + QFixed y = overall.yoff + glyphs.offsets[i].y + g->y; + overall.x = qMin(overall.x, x); + overall.y = qMin(overall.y, y); + xmax = qMax(xmax, x + g->width); + ymax = qMax(ymax, y + g->height); + overall.xoff += g->advance; + } + overall.height = qMax(overall.height, ymax - overall.y); + overall.width = xmax - overall.x; + + return overall; +} + +glyph_metrics_t QFontEngineQPA::boundingBox(glyph_t glyph) +{ + glyph_metrics_t overall; + const Glyph *g = findGlyph(glyph); + if (!g) + return overall; + overall.x = g->x; + overall.y = g->y; + overall.width = g->width; + overall.height = g->height; + overall.xoff = g->advance; + return overall; +} + +QFixed QFontEngineQPA::ascent() const +{ + return QFixed::fromReal(extractHeaderField(fontData, Tag_Ascent).value<qreal>()); +} + +QFixed QFontEngineQPA::descent() const +{ + return QFixed::fromReal(extractHeaderField(fontData, Tag_Descent).value<qreal>()); +} + +QFixed QFontEngineQPA::leading() const +{ + return QFixed::fromReal(extractHeaderField(fontData, Tag_Leading).value<qreal>()); +} + +qreal QFontEngineQPA::maxCharWidth() const +{ + return extractHeaderField(fontData, Tag_MaxCharWidth).value<qreal>(); +} + +qreal QFontEngineQPA::minLeftBearing() const +{ + return extractHeaderField(fontData, Tag_MinLeftBearing).value<qreal>(); +} + +qreal QFontEngineQPA::minRightBearing() const +{ + return extractHeaderField(fontData, Tag_MinRightBearing).value<qreal>(); +} + +QFixed QFontEngineQPA::underlinePosition() const +{ + return QFixed::fromReal(extractHeaderField(fontData, Tag_UnderlinePosition).value<qreal>()); +} + +QFixed QFontEngineQPA::lineThickness() const +{ + return QFixed::fromReal(extractHeaderField(fontData, Tag_LineThickness).value<qreal>()); +} + +QFontEngine::Type QFontEngineQPA::type() const +{ + return QFontEngine::QPF2; +} + +bool QFontEngineQPA::canRender(const QChar *string, int len) +{ + const uchar *cmap = externalCMap ? externalCMap : (fontData + cmapOffset); + + if (symbol) { + for (int i = 0; i < len; ++i) { + unsigned int uc = getChar(string, i, len); + glyph_t g = getTrueTypeGlyphIndex(cmap, uc); + if(!g && uc < 0x100) + g = getTrueTypeGlyphIndex(cmap, uc + 0xf000); + if (!g) + return false; + } + } else { + for (int i = 0; i < len; ++i) { + unsigned int uc = getChar(string, i, len); + if (!getTrueTypeGlyphIndex(cmap, uc)) + return false; + } + } + return true; +} + +bool QFontEngineQPA::isValid() const +{ + return fontData && dataSize && (cmapOffset || externalCMap) + && glyphMapOffset && glyphDataOffset && glyphDataSize > 0; +} + +void QPAGenerator::generate() +{ + writeHeader(); + writeGMap(); + writeBlock(QFontEngineQPA::GlyphBlock, QByteArray()); + + dev->seek(4); // position of header.lock + writeUInt32(0); +} + +void QPAGenerator::writeHeader() +{ + QFontEngineQPA::Header header; + + header.magic[0] = 'Q'; + header.magic[1] = 'P'; + header.magic[2] = 'F'; + header.magic[3] = '2'; + header.lock = 1; + header.majorVersion = QFontEngineQPA::CurrentMajorVersion; + header.minorVersion = QFontEngineQPA::CurrentMinorVersion; + header.dataSize = 0; + dev->write((const char *)&header, sizeof(header)); + + writeTaggedString(QFontEngineQPA::Tag_FontName, fe->fontDef.family.toUtf8()); + + QFontEngine::FaceId face = fe->faceId(); + writeTaggedString(QFontEngineQPA::Tag_FileName, face.filename); + writeTaggedUInt32(QFontEngineQPA::Tag_FileIndex, face.index); + + { + uchar data[4]; + uint len = 4; + bool ok = fe->getSfntTableData(MAKE_TAG('h', 'e', 'a', 'd'), data, &len); + if (ok) { + const quint32 revision = qFromBigEndian<quint32>(data); + writeTaggedUInt32(QFontEngineQPA::Tag_FontRevision, revision); + } + } + + writeTaggedQFixed(QFontEngineQPA::Tag_Ascent, fe->ascent()); + writeTaggedQFixed(QFontEngineQPA::Tag_Descent, fe->descent()); + writeTaggedQFixed(QFontEngineQPA::Tag_Leading, fe->leading()); + writeTaggedQFixed(QFontEngineQPA::Tag_XHeight, fe->xHeight()); + writeTaggedQFixed(QFontEngineQPA::Tag_AverageCharWidth, fe->averageCharWidth()); + writeTaggedQFixed(QFontEngineQPA::Tag_MaxCharWidth, QFixed::fromReal(fe->maxCharWidth())); + writeTaggedQFixed(QFontEngineQPA::Tag_LineThickness, fe->lineThickness()); + writeTaggedQFixed(QFontEngineQPA::Tag_MinLeftBearing, QFixed::fromReal(fe->minLeftBearing())); + writeTaggedQFixed(QFontEngineQPA::Tag_MinRightBearing, QFixed::fromReal(fe->minRightBearing())); + writeTaggedQFixed(QFontEngineQPA::Tag_UnderlinePosition, fe->underlinePosition()); + writeTaggedUInt8(QFontEngineQPA::Tag_PixelSize, fe->fontDef.pixelSize); + writeTaggedUInt8(QFontEngineQPA::Tag_Weight, fe->fontDef.weight); + writeTaggedUInt8(QFontEngineQPA::Tag_Style, fe->fontDef.style); + + writeTaggedUInt8(QFontEngineQPA::Tag_GlyphFormat, QFontEngineQPA::AlphamapGlyphs); + + writeTaggedString(QFontEngineQPA::Tag_EndOfHeader, QByteArray()); + align4(); + + const quint64 size = dev->pos(); + header.dataSize = qToBigEndian<quint16>(size - sizeof(header)); + dev->seek(0); + dev->write((const char *)&header, sizeof(header)); + dev->seek(size); +} + +void QPAGenerator::writeGMap() +{ + const quint16 glyphCount = fe->glyphCount(); + + writeUInt16(QFontEngineQPA::GMapBlock); + writeUInt16(0); // padding + writeUInt32(glyphCount * 4); + + QByteArray &buffer = dev->buffer(); + const int numBytes = glyphCount * sizeof(quint32); + qint64 pos = buffer.size(); + buffer.resize(pos + numBytes); + qMemSet(buffer.data() + pos, 0xff, numBytes); + dev->seek(pos + numBytes); +} + +void QPAGenerator::writeBlock(QFontEngineQPA::BlockTag tag, const QByteArray &data) +{ + writeUInt16(tag); + writeUInt16(0); // padding + const int padSize = ((data.size() + 3) / 4) * 4 - data.size(); + writeUInt32(data.size() + padSize); + dev->write(data); + for (int i = 0; i < padSize; ++i) + writeUInt8(0); +} + +void QPAGenerator::writeTaggedString(QFontEngineQPA::HeaderTag tag, const QByteArray &string) +{ + writeUInt16(tag); + writeUInt16(string.length()); + dev->write(string); +} + +void QPAGenerator::writeTaggedUInt32(QFontEngineQPA::HeaderTag tag, quint32 value) +{ + writeUInt16(tag); + writeUInt16(sizeof(value)); + writeUInt32(value); +} + +void QPAGenerator::writeTaggedUInt8(QFontEngineQPA::HeaderTag tag, quint8 value) +{ + writeUInt16(tag); + writeUInt16(sizeof(value)); + writeUInt8(value); +} + +void QPAGenerator::writeTaggedQFixed(QFontEngineQPA::HeaderTag tag, QFixed value) +{ + writeUInt16(tag); + writeUInt16(sizeof(quint32)); + writeUInt32(value.value()); +} + + +/* + Creates a new multi QPA engine. + + This function takes ownership of the QFontEngine, increasing it's refcount. +*/ +QFontEngineMultiQPA::QFontEngineMultiQPA(QFontEngine *fe, int _script, const QStringList &fallbacks) + : QFontEngineMulti(fallbacks.size() + 1), + fallbackFamilies(fallbacks), script(_script) +{ + engines[0] = fe; + fe->ref.ref(); + fontDef = engines[0]->fontDef; +} + +void QFontEngineMultiQPA::loadEngine(int at) +{ + Q_ASSERT(at < engines.size()); + Q_ASSERT(engines.at(at) == 0); + + QFontDef request = fontDef; + request.styleStrategy |= QFont::NoFontMerging; + request.family = fallbackFamilies.at(at-1); + engines[at] = QFontDatabase::findFont(script, + /*fontprivate*/0, + request); + Q_ASSERT(engines[at]); + engines[at]->ref.ref(); + engines[at]->fontDef = request; +} + +QT_END_NAMESPACE diff --git a/src/gui/text/qfontengine_qpa_p.h b/src/gui/text/qfontengine_qpa_p.h new file mode 100644 index 0000000..a88c1bc --- /dev/null +++ b/src/gui/text/qfontengine_qpa_p.h @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFONTENGINE_QPA_P_H +#define QFONTENGINE_QPA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// +#include <QtCore/qconfig.h> +#include <QtCore/qglobal.h> +#include <QtCore/qendian.h> +#include <QtCore/QBuffer> + +#include "qfontengine_p.h" + +#include <QtCore/QFile> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QFontEngine; +class QFreetypeFace; +class QBuffer; + +class Q_GUI_EXPORT QFontEngineQPA : public QFontEngine +{ +public: + // if you add new tags please make sure to update the tables in + // qpfutil.cpp and tools/makeqpf/qpf2.cpp + enum HeaderTag { + Tag_FontName, // 0 string + Tag_FileName, // 1 string + Tag_FileIndex, // 2 quint32 + Tag_FontRevision, // 3 quint32 + Tag_FreeText, // 4 string + Tag_Ascent, // 5 QFixed + Tag_Descent, // 6 QFixed + Tag_Leading, // 7 QFixed + Tag_XHeight, // 8 QFixed + Tag_AverageCharWidth, // 9 QFixed + Tag_MaxCharWidth, // 10 QFixed + Tag_LineThickness, // 11 QFixed + Tag_MinLeftBearing, // 12 QFixed + Tag_MinRightBearing, // 13 QFixed + Tag_UnderlinePosition, // 14 QFixed + Tag_GlyphFormat, // 15 quint8 + Tag_PixelSize, // 16 quint8 + Tag_Weight, // 17 quint8 + Tag_Style, // 18 quint8 + Tag_EndOfHeader, // 19 string + Tag_WritingSystems, // 20 bitfield + + NumTags + }; + + enum TagType { + StringType, + FixedType, + UInt8Type, + UInt32Type, + BitFieldType + }; + + struct Tag + { + quint16 tag; + quint16 size; + }; + + enum GlyphFormat { + BitmapGlyphs = 1, + AlphamapGlyphs = 8 + }; + + enum { + CurrentMajorVersion = 2, + CurrentMinorVersion = 0 + }; + + // The CMap is identical to the TrueType CMap table format + // The GMap table is a normal array with the total number of + // covered glyphs in the TrueType font + enum BlockTag { + CMapBlock, + GMapBlock, + GlyphBlock + }; + + struct Q_PACKED Header + { + char magic[4]; // 'QPF2' + quint32 lock; // values: 0 = unlocked, 0xffffffff = read-only, otherwise qws client id of locking process + quint8 majorVersion; + quint8 minorVersion; + quint16 dataSize; + }; + + struct Q_PACKED Block + { + quint16 tag; + quint16 pad; + quint32 dataSize; + }; + + struct Q_PACKED Glyph + { + quint8 width; + quint8 height; + quint8 bytesPerLine; + qint8 x; + qint8 y; + qint8 advance; + }; + + QFontEngineQPA(const QFontDef &def, const QByteArray &data); + ~QFontEngineQPA(); + + FaceId faceId() const { return face_id; } + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; + + bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const; + void recalcAdvances(QGlyphLayout *, QTextEngine::ShaperFlags) const; + + void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags); + QImage alphaMapForGlyph(glyph_t t); + + glyph_metrics_t boundingBox(const QGlyphLayout &glyphs); + glyph_metrics_t boundingBox(glyph_t glyph); + + QFixed ascent() const; + QFixed descent() const; + QFixed leading() const; + qreal maxCharWidth() const; + qreal minLeftBearing() const; + qreal minRightBearing() const; + QFixed underlinePosition() const; + QFixed lineThickness() const; + + Type type() const; + + bool canRender(const QChar *string, int len); + inline const char *name() const { return "QPF2"; } + + virtual int glyphCount() const { return glyphMapEntries; } + + bool isValid() const; + + const Glyph *findGlyph(glyph_t g) const; + + static bool verifyHeader(const uchar *data, int size); + static QVariant extractHeaderField(const uchar *data, HeaderTag tag); + +private: + + const uchar *fontData; + int dataSize; + const uchar *externalCMap; + quint32 cmapOffset; + int cmapSize; + quint32 glyphMapOffset; + quint32 glyphMapEntries; + quint32 glyphDataOffset; + quint32 glyphDataSize; + QString internalFileName; + QString encodedFileName; + bool readOnly; + + FaceId face_id; + QByteArray freetypeCMapTable; + mutable bool kerning_pairs_loaded; +}; + +struct QPAGenerator +{ + QPAGenerator(QBuffer *device, QFontEngine *engine) + : dev(device), fe(engine) {} + + void generate(); + void writeHeader(); + void writeGMap(); + void writeBlock(QFontEngineQPA::BlockTag tag, const QByteArray &data); + + void writeTaggedString(QFontEngineQPA::HeaderTag tag, const QByteArray &string); + void writeTaggedUInt32(QFontEngineQPA::HeaderTag tag, quint32 value); + void writeTaggedUInt8(QFontEngineQPA::HeaderTag tag, quint8 value); + void writeTaggedQFixed(QFontEngineQPA::HeaderTag tag, QFixed value); + + void writeUInt16(quint16 value) { value = qToBigEndian(value); dev->write((const char *)&value, sizeof(value)); } + void writeUInt32(quint32 value) { value = qToBigEndian(value); dev->write((const char *)&value, sizeof(value)); } + void writeUInt8(quint8 value) { dev->write((const char *)&value, sizeof(value)); } + void writeInt8(qint8 value) { dev->write((const char *)&value, sizeof(value)); } + + void align4() { while (dev->pos() & 3) { dev->putChar('\0'); } } + + QBuffer *dev; + QFontEngine *fe; +}; + +class QFontEngineMultiQPA : public QFontEngineMulti +{ +public: + QFontEngineMultiQPA(QFontEngine *fe, int script, const QStringList &fallbacks); + + void loadEngine(int at); + +private: + QStringList fallbackFamilies; + int script; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QFONTENGINE_QPA_P_H diff --git a/src/gui/text/qfontengine_qpf.cpp b/src/gui/text/qfontengine_qpf.cpp index c7a3151..3db5ce1 100644 --- a/src/gui/text/qfontengine_qpf.cpp +++ b/src/gui/text/qfontengine_qpf.cpp @@ -251,8 +251,8 @@ QList<QByteArray> QFontEngineQPF::cleanUpAfterClientCrash(const QList<int> &cras { QList<QByteArray> removedFonts; QDir dir(qws_fontCacheDir(), QLatin1String("*.qsf")); - for (int i = 0; i < int(dir.count()); ++i) { - const QByteArray fileName = QFile::encodeName(dir.absoluteFilePath(dir[i])); + foreach (const QFileInfo &fi, dir.entryInfoList()) { + const QByteArray fileName = QFile::encodeName(fi.absoluteFilePath()); int fd = QT_OPEN(fileName.constData(), O_RDONLY, 0); if (fd >= 0) { @@ -278,15 +278,12 @@ QList<QByteArray> QFontEngineQPF::cleanUpAfterClientCrash(const QList<int> &cras static inline unsigned int getChar(const QChar *str, int &i, const int len) { - unsigned int uc = str[i].unicode(); - if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) { - uint low = str[i+1].unicode(); - if (low >= 0xdc00 && low < 0xe000) { - uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000; - ++i; - } + uint ucs4 = str[i].unicode(); + if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) { + ++i; + ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode()); } - return uc; + return ucs4; } #ifdef QT_FONTS_ARE_RESOURCES QFontEngineQPF::QFontEngineQPF(const QFontDef &def, const uchar *bytes, int size) @@ -306,6 +303,8 @@ QFontEngineQPF::QFontEngineQPF(const QFontDef &def, int fileDescriptor, QFontEng glyphMapEntries = 0; glyphDataOffset = 0; glyphDataSize = 0; + if (renderingFontEngine) + glyphFormat = renderingFontEngine->glyphFormat; kerning_pairs_loaded = false; readOnly = true; @@ -340,14 +339,14 @@ QFontEngineQPF::QFontEngineQPF(const QFontDef &def, int fileDescriptor, QFontEng fd = QT_OPEN(encodedFileName, O_RDONLY); if (fd == -1) { #if defined(DEBUG_FONTENGINE) - qErrnoWarning("QFontEngineQPF: unable to open %s", encodedName.constData()); + qErrnoWarning("QFontEngineQPF: unable to open %s", encodedFileName.constData()); #endif return; } } if (fd == -1) { #if defined(DEBUG_FONTENGINE) - qWarning("QFontEngineQPF: insufficient access rights to %s", encodedName.constData()); + qWarning("QFontEngineQPF: insufficient access rights to %s", encodedFileName.constData()); #endif return; } @@ -359,7 +358,7 @@ QFontEngineQPF::QFontEngineQPF(const QFontDef &def, int fileDescriptor, QFontEng fd = QT_OPEN(encodedFileName, O_RDWR | O_EXCL | O_CREAT, 0644); if (fd == -1) { #if defined(DEBUG_FONTENGINE) - qErrnoWarning(errno, "QFontEngineQPF: open() failed for %s", encodedName.constData()); + qErrnoWarning(errno, "QFontEngineQPF: open() failed for %s", encodedFileName.constData()); #endif return; } @@ -372,7 +371,7 @@ QFontEngineQPF::QFontEngineQPF(const QFontDef &def, int fileDescriptor, QFontEng const QByteArray &data = buffer.data(); if (QT_WRITE(fd, data.constData(), data.size()) == -1) { #if defined(DEBUG_FONTENGINE) - qErrnoWarning(errno, "QFontEngineQPF: write() failed for %s", encodedName.constData()); + qErrnoWarning(errno, "QFontEngineQPF: write() failed for %s", encodedFileName.constData()); #endif return; } diff --git a/src/gui/text/qfontengine_qws.cpp b/src/gui/text/qfontengine_qws.cpp index 5943609..ade283f 100644 --- a/src/gui/text/qfontengine_qws.cpp +++ b/src/gui/text/qfontengine_qws.cpp @@ -40,7 +40,6 @@ ****************************************************************************/ #include "qfontengine_p.h" -#include <private/qunicodetables_p.h> #include <qwsdisplay_qws.h> #include <qvarlengtharray.h> #include <private/qpainter_p.h> @@ -51,15 +50,17 @@ #include <qdebug.h> - #ifndef QT_NO_QWS_QPF +#include "qplatformdefs.h" #include "qfile.h" -#include "qdir.h" -#define QT_USE_MMAP #include <stdlib.h> +#if !defined(Q_OS_INTEGRITY) +#define QT_USE_MMAP +#endif + #ifdef QT_USE_MMAP // for mmap #include <unistd.h> @@ -69,33 +70,25 @@ #include <fcntl.h> #include <errno.h> -# if defined(QT_LINUXBASE) && !defined(MAP_FILE) - // LSB 3.2 does not define MAP_FILE -# define MAP_FILE 0 -# endif - +#ifndef MAP_FILE +# define MAP_FILE 0 +#endif +#ifndef MAP_FAILED +# define MAP_FAILED (void *)-1 #endif -#endif // QT_NO_QWS_QPF +#endif // QT_USE_MMAP QT_BEGIN_NAMESPACE -#ifndef QT_NO_QWS_QPF -QT_BEGIN_INCLUDE_NAMESPACE -#include "qplatformdefs.h" -QT_END_INCLUDE_NAMESPACE - static inline unsigned int getChar(const QChar *str, int &i, const int len) { - unsigned int uc = str[i].unicode(); - if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) { - uint low = str[i+1].unicode(); - if (low >= 0xdc00 && low < 0xe000) { - uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000; - ++i; - } + uint ucs4 = str[i].unicode(); + if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) { + ++i; + ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode()); } - return uc; + return ucs4; } #define FM_SMOOTH 1 @@ -161,17 +154,10 @@ public: QPFGlyphTree* more; QPFGlyph* glyph; public: -#ifdef QT_USE_MMAP QPFGlyphTree(uchar*& data) { read(data); } -#else - QPFGlyphTree(QIODevice& f) - { - read(f); - } -#endif ~QPFGlyphTree() { @@ -237,7 +223,6 @@ private: { } -#ifdef QT_USE_MMAP void read(uchar*& data) { // All node data first @@ -247,19 +232,7 @@ private: // Then all video data readData(data); } -#else - void read(QIODevice& f) - { - // All node data first - readNode(f); - // Then all non-video data - readMetrics(f); - // Then all video data - readData(f); - } -#endif -#ifdef QT_USE_MMAP void readNode(uchar*& data) { uchar rw = *data++; @@ -285,35 +258,7 @@ private: if ( more ) more->readNode(data); } -#else - void readNode(QIODevice& f) - { - uchar rw = f.getch(); - uchar cl = f.getch(); - min = (rw << 8) | cl; - rw = f.getch(); - cl = f.getch(); - max = (rw << 8) | cl; - int flags = f.getch(); - if ( flags & 1 ) - less = new QPFGlyphTree; - else - less = 0; - if ( flags & 2 ) - more = new QPFGlyphTree; - else - more = 0; - int n = max-min+1; - glyph = new QPFGlyph[n]; - - if ( less ) - less->readNode(f); - if ( more ) - more->readNode(f); - } -#endif -#ifdef QT_USE_MMAP void readMetrics(uchar*& data) { int n = max-min+1; @@ -326,22 +271,7 @@ private: if ( more ) more->readMetrics(data); } -#else - void readMetrics(QIODevice& f) - { - int n = max-min+1; - for (int i=0; i<n; i++) { - glyph[i].metrics = new QPFGlyphMetrics; - f.readBlock((char*)glyph[i].metrics, sizeof(QPFGlyphMetrics)); - } - if ( less ) - less->readMetrics(f); - if ( more ) - more->readMetrics(f); - } -#endif -#ifdef QT_USE_MMAP void readData(uchar*& data) { int n = max-min+1; @@ -356,24 +286,6 @@ private: if ( more ) more->readData(data); } -#else - void readData(QIODevice& f) - { - int n = max-min+1; - for (int i=0; i<n; i++) { - QSize s( glyph[i].metrics->width, glyph[i].metrics->height ); - //############### s = qt_screen->mapToDevice( s ); - uint datasize = glyph[i].metrics->linestep * s.height(); - glyph[i].data = new uchar[datasize]; // ### deleted? - f.readBlock((char*)glyph[i].data, datasize); - } - if ( less ) - less->readData(f); - if ( more ) - more->readData(f); - } -#endif - }; class QFontEngineQPF1Data @@ -381,45 +293,58 @@ class QFontEngineQPF1Data public: QPFFontMetrics fm; QPFGlyphTree *tree; - void *mmapStart; + uchar *mmapStart; size_t mmapLength; +#ifdef QT_USE_MMAP + bool used_mmap; +#endif }; - QFontEngineQPF1::QFontEngineQPF1(const QFontDef&, const QString &fn) { cache_cost = 1; - int f = QT_OPEN( QFile::encodeName(fn), O_RDONLY, 0); - Q_ASSERT(f>=0); + int fd = QT_OPEN(QFile::encodeName(fn).constData(), O_RDONLY, 0); + if (fd == -1) + qFatal("Failed to open '%s'", QFile::encodeName(fn).constData()); + QT_STATBUF st; - if ( QT_FSTAT( f, &st ) ) - qFatal("Failed to stat %s",QFile::encodeName(fn).data()); - uchar* data = (uchar*)mmap( 0, // any address - st.st_size, // whole file - PROT_READ, // read-only memory -#if !defined(Q_OS_SOLARIS) && !defined(Q_OS_QNX4) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS) - MAP_FILE | MAP_PRIVATE, // swap-backed map from file -#else - MAP_PRIVATE, -#endif - f, 0 ); // from offset 0 of f -#if defined(Q_OS_QNX4) && !defined(MAP_FAILED) -#define MAP_FAILED ((void *)-1) -#endif - if ( !data || data == (uchar*)MAP_FAILED ) - qFatal("Failed to mmap %s",QFile::encodeName(fn).data()); - QT_CLOSE(f); + if (QT_FSTAT(fd, &st) != 0) + qFatal("Failed to stat '%s'", QFile::encodeName(fn).constData()); d = new QFontEngineQPF1Data; - d->mmapStart = data; + d->mmapStart = 0; d->mmapLength = st.st_size; - memcpy(reinterpret_cast<char*>(&d->fm),data,sizeof(d->fm)); - data += sizeof(d->fm); - d->tree = new QPFGlyphTree(data); - glyphFormat = (d->fm.flags & FM_SMOOTH) ? QFontEngineGlyphCache::Raster_A8 - : QFontEngineGlyphCache::Raster_Mono; +#ifdef QT_USE_MMAP + d->used_mmap = true; + d->mmapStart = (uchar *)::mmap(0, st.st_size, // any address, whole file + PROT_READ, // read-only memory + MAP_FILE | MAP_PRIVATE, // swap-backed map from file + fd, 0); // from offset 0 of fd + if (d->mmapStart == (uchar *)MAP_FAILED) + d->mmapStart = 0; +#endif + + if (!d->mmapStart) { +#ifdef QT_USE_MMAP + d->used_mmap = false; +#endif + d->mmapStart = new uchar[d->mmapLength]; + if (QT_READ(fd, d->mmapStart, d->mmapLength) != d->mmapLength) + qFatal("Failed to read '%s'", QFile::encodeName(fn).constData()); + } + QT_CLOSE(fd); + + if (d->mmapStart) { + uchar* data = d->mmapStart; + + memcpy(reinterpret_cast<char*>(&d->fm), data, sizeof(d->fm)); + data += sizeof(d->fm); + + d->tree = new QPFGlyphTree(data); + glyphFormat = (d->fm.flags & FM_SMOOTH) ? QFontEngineGlyphCache::Raster_A8 + : QFontEngineGlyphCache::Raster_Mono; #if 0 qDebug() << "font file" << fn << "ascent" << d->fm.ascent << "descent" << d->fm.descent @@ -431,12 +356,19 @@ QFontEngineQPF1::QFontEngineQPF1(const QFontDef&, const QString &fn) << "underlinepos" << d->fm.underlinepos << "underlinewidth" << d->fm.underlinewidth; #endif + } } QFontEngineQPF1::~QFontEngineQPF1() { - if (d->mmapStart) - munmap(d->mmapStart, d->mmapLength); + if (d->mmapStart) { +#if defined(QT_USE_MMAP) + if (d->used_mmap) + ::munmap(d->mmapStart, d->mmapLength); + else +#endif + delete [] d->mmapStart; + } delete d->tree; delete d; } diff --git a/src/gui/text/qfontengine_s60.cpp b/src/gui/text/qfontengine_s60.cpp index 36eb7c0..203b6e1 100644 --- a/src/gui/text/qfontengine_s60.cpp +++ b/src/gui/text/qfontengine_s60.cpp @@ -108,7 +108,8 @@ QByteArray QSymbianTypeFaceExtras::getSfntTable(uint tag) const Q_ASSERT(m_trueTypeExtension->HasTrueTypeTable(tag)); TInt error = KErrNone; TInt tableByteLength = 0; - TAny *table = q_check_ptr(m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength)); + TAny *table = m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength); + Q_CHECK_PTR(table); const QByteArray result(static_cast<const char*>(table), tableByteLength); m_trueTypeExtension->ReleaseTrueTypeTable(table); return result; @@ -138,8 +139,8 @@ bool QSymbianTypeFaceExtras::getSfntTableData(uint tag, uchar *buffer, uint *len TInt error = KErrNone; TInt tableByteLength; - TAny *table = - q_check_ptr(m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength)); + TAny *table = m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength); + Q_CHECK_PTR(table); if (error != KErrNone) { return false; @@ -232,15 +233,12 @@ bool QSymbianTypeFaceExtras::symbianFontTableApiAvailable() // duplicated from qfontengine_xyz.cpp static inline unsigned int getChar(const QChar *str, int &i, const int len) { - unsigned int uc = str[i].unicode(); - if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) { - uint low = str[i+1].unicode(); - if (low >= 0xdc00 && low < 0xe000) { - uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000; - ++i; - } + uint ucs4 = str[i].unicode(); + if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) { + ++i; + ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode()); } - return uc; + return ucs4; } extern QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName); // qfontdatabase_s60.cpp diff --git a/src/gui/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp index 2b2ac98..fc11387 100644 --- a/src/gui/text/qfontengine_win.cpp +++ b/src/gui/text/qfontengine_win.cpp @@ -224,15 +224,12 @@ void QFontEngineWin::getCMap() inline unsigned int getChar(const QChar *str, int &i, const int len) { - unsigned int uc = str[i].unicode(); - if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) { - uint low = str[i+1].unicode(); - if (low >= 0xdc00 && low < 0xe000) { - uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000; - ++i; - } + uint ucs4 = str[i].unicode(); + if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) { + ++i; + ucs4 = QChar::surrogateToUcs4(ucs4, str[i].unicode()); } - return uc; + return ucs4; } int QFontEngineWin::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs, bool mirrored) const @@ -1251,7 +1248,7 @@ QImage QFontEngineWin::alphaMapForGlyph(glyph_t glyph, const QTransform &xform) #define SPI_GETFONTSMOOTHINGCONTRAST 0x200C #define SPI_SETFONTSMOOTHINGCONTRAST 0x200D -QImage QFontEngineWin::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTransform &t) +QImage QFontEngineWin::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, const QTransform &t) { HFONT font = hfont; @@ -1284,9 +1281,26 @@ QImage QFontEngineWin::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTra return rgbMask; } +// From qfontdatabase_win.cpp +extern QFontEngine *qt_load_font_engine_win(const QFontDef &request); +QFontEngine *QFontEngineWin::cloneWithSize(qreal pixelSize) const +{ + QFontDef request = fontDef; + QString actualFontName = request.family; + if (!uniqueFamilyName.isEmpty()) + request.family = uniqueFamilyName; + request.pixelSize = pixelSize; + + QFontEngine *fontEngine = qt_load_font_engine_win(request); + if (fontEngine != NULL) + fontEngine->fontDef.family = actualFontName; + + return fontEngine; +} + // -------------------------------------- Multi font engine -QFontEngineMultiWin::QFontEngineMultiWin(QFontEngineWin *first, const QStringList &fallbacks) +QFontEngineMultiWin::QFontEngineMultiWin(QFontEngine *first, const QStringList &fallbacks) : QFontEngineMulti(fallbacks.size()+1), fallbacks(fallbacks) { diff --git a/src/gui/text/qfontengine_win_p.h b/src/gui/text/qfontengine_win_p.h index 3636383..ebcafff 100644 --- a/src/gui/text/qfontengine_win_p.h +++ b/src/gui/text/qfontengine_win_p.h @@ -104,7 +104,9 @@ public: virtual QImage alphaMapForGlyph(glyph_t t) { return alphaMapForGlyph(t, QTransform()); } virtual QImage alphaMapForGlyph(glyph_t, const QTransform &xform); - virtual QImage alphaRGBMapForGlyph(glyph_t t, int margin, const QTransform &xform); + virtual QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, int margin, const QTransform &xform); + + virtual QFontEngine *cloneWithSize(qreal pixelSize) const; #ifndef Q_CC_MINGW virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0); @@ -118,6 +120,7 @@ public: #endif QString _name; + QString uniqueFamilyName; HFONT hfont; LOGFONT logfont; uint stockFont : 1; @@ -150,7 +153,7 @@ private: class QFontEngineMultiWin : public QFontEngineMulti { public: - QFontEngineMultiWin(QFontEngineWin *first, const QStringList &fallbacks); + QFontEngineMultiWin(QFontEngine *first, const QStringList &fallbacks); void loadEngine(int at); QStringList fallbacks; diff --git a/src/gui/text/qfontengine_x11.cpp b/src/gui/text/qfontengine_x11.cpp index 5ee9829..6e87f4c 100644 --- a/src/gui/text/qfontengine_x11.cpp +++ b/src/gui/text/qfontengine_x11.cpp @@ -358,9 +358,7 @@ bool QFontEngineXLFD::stringToCMap(const QChar *s, int len, QGlyphLayout *glyphs QVarLengthArray<ushort> _s(len); QChar *str = (QChar *)_s.data(); for (int i = 0; i < len; ++i) { - if (i < len - 1 - && s[i].unicode() >= 0xd800 && s[i].unicode() < 0xdc00 - && s[i+1].unicode() >= 0xdc00 && s[i].unicode() < 0xe000) { + if (s[i].isHighSurrogate() && i < len-1 && s[i+1].isLowSurrogate()) { *str = QChar(); ++i; } else { @@ -863,11 +861,8 @@ glyph_t QFontEngineXLFD::glyphIndexToFreetypeGlyphIndex(glyph_t g) const // Multi FT engine // ------------------------------------------------------------------ -static QFontEngine *engineForPattern(FcPattern *pattern, const QFontDef &request, - int screen) +static QFontEngine *engineForPattern(FcPattern *match, const QFontDef &request, int screen) { - FcResult res; - FcPattern *match = FcFontMatch(0, pattern, &res); QFontEngineX11FT *engine = new QFontEngineX11FT(match, request, screen); if (!engine->invalid()) return engine; @@ -879,9 +874,9 @@ static QFontEngine *engineForPattern(FcPattern *pattern, const QFontDef &request } QFontEngineMultiFT::QFontEngineMultiFT(QFontEngine *fe, FcPattern *matchedPattern, FcPattern *p, int s, const QFontDef &req) - : QFontEngineMulti(2), request(req), pattern(p), firstEnginePattern(matchedPattern), fontSet(0), screen(s) + : QFontEngineMulti(2), request(req), pattern(p), fontSet(0), screen(s) { - + firstEnginePattern = FcPatternDuplicate(matchedPattern); engines[0] = fe; engines.at(0)->ref.ref(); fontDef = engines[0]->fontDef; @@ -907,8 +902,6 @@ void QFontEngineMultiFT::loadEngine(int at) extern QMutex *qt_fontdatabase_mutex(); QMutexLocker locker(qt_fontdatabase_mutex()); - extern void qt_addPatternProps(FcPattern *pattern, int screen, int script, - const QFontDef &request); extern QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &); extern FcFontSet *qt_fontSetForPattern(FcPattern *pattern, const QFontDef &request); @@ -940,22 +933,18 @@ void QFontEngineMultiFT::loadEngine(int at) Q_ASSERT(at < engines.size()); Q_ASSERT(engines.at(at) == 0); - FcPattern *pattern = FcPatternDuplicate(fontSet->fonts[at + firstFontIndex - 1]); - qt_addPatternProps(pattern, screen, QUnicodeTables::Common, request); - - QFontDef fontDef = qt_FcPatternToQFontDef(pattern, this->request); + FcPattern *match = FcFontRenderPrepare(NULL, pattern, fontSet->fonts[at + firstFontIndex - 1]); + QFontDef fontDef = qt_FcPatternToQFontDef(match, this->request); // note: we use -1 for the script to make sure that we keep real // FT engines separate from Multi engines in the font cache QFontCache::Key key(fontDef, -1, screen); QFontEngine *fontEngine = QFontCache::instance()->findEngine(key); if (!fontEngine) { - FcConfigSubstitute(0, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - fontEngine = engineForPattern(pattern, request, screen); + fontEngine = engineForPattern(match, request, screen); QFontCache::instance()->insertEngine(key, fontEngine); } - FcPatternDestroy(pattern); + FcPatternDestroy(match); fontEngine->ref.ref(); engines[at] = fontEngine; } @@ -992,7 +981,7 @@ QFontEngineX11FT::QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int s face_id.filename = file_name; face_id.index = face_index; - canUploadGlyphsToServer = qApp->thread() == QThread::currentThread(); + canUploadGlyphsToServer = QApplication::testAttribute(Qt::AA_X11InitThreads) || (qApp->thread() == QThread::currentThread()); subpixelType = Subpixel_None; if (antialias) { @@ -1012,10 +1001,29 @@ QFontEngineX11FT::QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int s } } + if (fd.hintingPreference != QFont::PreferDefaultHinting) { + switch (fd.hintingPreference) { + case QFont::PreferNoHinting: + default_hint_style = HintNone; + break; + case QFont::PreferVerticalHinting: + default_hint_style = HintLight; + break; + case QFont::PreferFullHinting: + default: + default_hint_style = HintFull; + break; + } + } #ifdef FC_HINT_STYLE - { + else { int hint_style = 0; - if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch) + // Try to use Xft.hintstyle from XDefaults first if running in GNOME, to match + // the behavior of cairo + if (X11->fc_hint_style > -1 && X11->desktopEnvironment == DE_GNOME) + hint_style = X11->fc_hint_style; + else if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch + && X11->fc_hint_style > -1) hint_style = X11->fc_hint_style; switch (hint_style) { @@ -1104,17 +1112,14 @@ QFontEngineX11FT::QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int s } #endif - if (!init(face_id, antialias, defaultFormat)) { - FcPatternDestroy(pattern); + if (!init(face_id, antialias, defaultFormat)) return; - } if (!freetype->charset) { FcCharSet *cs; FcPatternGetCharSet (pattern, FC_CHARSET, 0, &cs); freetype->charset = FcCharSetCopy(cs); } - FcPatternDestroy(pattern); } QFontEngineX11FT::~QFontEngineX11FT() @@ -1177,6 +1182,22 @@ bool QFontEngineX11FT::uploadGlyphToServer(QGlyphSet *set, uint glyphid, Glyph * #endif } +QFontEngine *QFontEngineX11FT::cloneWithSize(qreal pixelSize) const +{ + QFontDef fontDef; + fontDef.pixelSize = pixelSize; + QFontEngineX11FT *fe = new QFontEngineX11FT(fontDef); + if (!fe->initFromFontEngine(this)) { + delete fe; + return 0; + } else { +#ifndef QT_NO_XRENDER + fe->xglyph_format = xglyph_format; +#endif + return fe; + } +} + #endif // QT_NO_FONTCONFIG QT_END_NAMESPACE diff --git a/src/gui/text/qfontengine_x11_p.h b/src/gui/text/qfontengine_x11_p.h index 504b37f..1c0bcad 100644 --- a/src/gui/text/qfontengine_x11_p.h +++ b/src/gui/text/qfontengine_x11_p.h @@ -157,9 +157,12 @@ private: class Q_GUI_EXPORT QFontEngineX11FT : public QFontEngineFT { public: + explicit QFontEngineX11FT(const QFontDef &fontDef) : QFontEngineFT(fontDef) {} explicit QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int screen); ~QFontEngineX11FT(); + QFontEngine *cloneWithSize(qreal pixelSize) const; + #ifndef QT_NO_XRENDER int xglyph_format; #endif diff --git a/src/gui/text/qfontenginedirectwrite.cpp b/src/gui/text/qfontenginedirectwrite.cpp new file mode 100644 index 0000000..5bac117 --- /dev/null +++ b/src/gui/text/qfontenginedirectwrite.cpp @@ -0,0 +1,697 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_DIRECTWRITE + +#include "qfontenginedirectwrite_p.h" + +#include <qendian.h> +#include <dwrite.h> +#include <private/qnativeimage_p.h> + +#include <d2d1.h> + +QT_BEGIN_NAMESPACE + +// Convert from design units to logical pixels +#define DESIGN_TO_LOGICAL(DESIGN_UNIT_VALUE) \ + QFixed::fromReal((qreal(DESIGN_UNIT_VALUE) / qreal(m_unitsPerEm)) * fontDef.pixelSize) + +namespace { + + class GeometrySink: public IDWriteGeometrySink + { + public: + GeometrySink(QPainterPath *path) : m_path(path), m_refCount(0) + { + Q_ASSERT(m_path != 0); + } + + IFACEMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *beziers, UINT bezierCount); + IFACEMETHOD_(void, AddLines)(const D2D1_POINT_2F *points, UINT pointCount); + IFACEMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin); + IFACEMETHOD(Close)(); + IFACEMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd); + IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode); + IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags); + + IFACEMETHOD_(unsigned long, AddRef)(); + IFACEMETHOD_(unsigned long, Release)(); + IFACEMETHOD(QueryInterface)(IID const &riid, void **ppvObject); + + private: + inline static QPointF fromD2D1_POINT_2F(const D2D1_POINT_2F &inp) + { + return QPointF(inp.x, inp.y); + } + + unsigned long m_refCount; + QPointF m_startPoint; + QPainterPath *m_path; + }; + + void GeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, + UINT bezierCount) + { + for (uint i=0; i<bezierCount; ++i) { + QPointF c1 = fromD2D1_POINT_2F(beziers[i].point1); + QPointF c2 = fromD2D1_POINT_2F(beziers[i].point2); + QPointF p2 = fromD2D1_POINT_2F(beziers[i].point3); + + m_path->cubicTo(c1, c2, p2); + } + } + + void GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) + { + for (uint i=0; i<pointsCount; ++i) + m_path->lineTo(fromD2D1_POINT_2F(points[i])); + } + + void GeometrySink::BeginFigure(D2D1_POINT_2F startPoint, + D2D1_FIGURE_BEGIN /*figureBegin*/) + { + m_startPoint = fromD2D1_POINT_2F(startPoint); + m_path->moveTo(m_startPoint); + } + + IFACEMETHODIMP GeometrySink::Close() + { + return E_NOTIMPL; + } + + void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) + { + if (figureEnd == D2D1_FIGURE_END_CLOSED) + m_path->closeSubpath(); + } + + void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) + { + m_path->setFillRule(fillMode == D2D1_FILL_MODE_ALTERNATE + ? Qt::OddEvenFill + : Qt::WindingFill); + } + + void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT /*vertexFlags*/) + { + /* Not implemented */ + } + + IFACEMETHODIMP_(unsigned long) GeometrySink::AddRef() + { + return InterlockedIncrement(&m_refCount); + } + + IFACEMETHODIMP_(unsigned long) GeometrySink::Release() + { + unsigned long newCount = InterlockedDecrement(&m_refCount); + if (newCount == 0) + { + delete this; + return 0; + } + + return newCount; + } + + IFACEMETHODIMP GeometrySink::QueryInterface(IID const &riid, void **ppvObject) + { + if (__uuidof(IDWriteGeometrySink) == riid) { + *ppvObject = this; + } else if (__uuidof(IUnknown) == riid) { + *ppvObject = this; + } else { + *ppvObject = NULL; + return E_FAIL; + } + + AddRef(); + return S_OK; + } + +} + +QFontEngineDirectWrite::QFontEngineDirectWrite(IDWriteFactory *directWriteFactory, + IDWriteFontFace *directWriteFontFace, + qreal pixelSize) + : m_directWriteFontFace(directWriteFontFace) + , m_directWriteFactory(directWriteFactory) + , m_directWriteBitmapRenderTarget(0) + , m_lineThickness(-1) + , m_unitsPerEm(-1) + , m_ascent(-1) + , m_descent(-1) + , m_xHeight(-1) + , m_lineGap(-1) +{ + m_directWriteFactory->AddRef(); + m_directWriteFontFace->AddRef(); + + fontDef.pixelSize = pixelSize; + collectMetrics(); +} + +QFontEngineDirectWrite::~QFontEngineDirectWrite() +{ + m_directWriteFactory->Release(); + m_directWriteFontFace->Release(); + + if (m_directWriteBitmapRenderTarget != 0) + m_directWriteBitmapRenderTarget->Release(); +} + +void QFontEngineDirectWrite::collectMetrics() +{ + if (m_directWriteFontFace != 0) { + DWRITE_FONT_METRICS metrics; + + m_directWriteFontFace->GetMetrics(&metrics); + m_unitsPerEm = metrics.designUnitsPerEm; + + m_lineThickness = DESIGN_TO_LOGICAL(metrics.underlineThickness); + m_ascent = DESIGN_TO_LOGICAL(metrics.ascent); + m_descent = DESIGN_TO_LOGICAL(metrics.descent); + m_xHeight = DESIGN_TO_LOGICAL(metrics.xHeight); + m_lineGap = DESIGN_TO_LOGICAL(metrics.lineGap); + } +} + +QFixed QFontEngineDirectWrite::lineThickness() const +{ + if (m_lineThickness > 0) + return m_lineThickness; + else + return QFontEngine::lineThickness(); +} + +bool QFontEngineDirectWrite::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + if (m_directWriteFontFace) { + DWORD t = qbswap<quint32>(tag); + + const void *tableData = 0; + void *tableContext = 0; + UINT32 tableSize; + BOOL exists; + HRESULT hr = m_directWriteFontFace->TryGetFontTable( + t, &tableData, &tableSize, &tableContext, &exists + ); + + if (SUCCEEDED(hr)) { + if (!exists) + return false; + + if (buffer == 0) { + *length = tableSize; + return true; + } else if (*length < tableSize) { + return false; + } + + qMemCopy(buffer, tableData, tableSize); + m_directWriteFontFace->ReleaseFontTable(tableContext); + + return true; + } else { + qErrnoWarning("QFontEngineDirectWrite::getSfntTableData: TryGetFontTable failed"); + } + } + + return false; +} + +QFixed QFontEngineDirectWrite::emSquareSize() const +{ + if (m_unitsPerEm > 0) + return m_unitsPerEm; + else + return QFontEngine::emSquareSize(); +} + +inline unsigned int getChar(const QChar *str, int &i, const int len) +{ + uint ucs4 = str[i].unicode(); + if (str[i].isHighSurrogate() && i < len-1 && str[i+1].isLowSurrogate()) { + ++i; + ucs4 = QChar::surrogateToUcs4( ucs4, str[i].unicode()); + } + return ucs4; +} + +bool QFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, + int *nglyphs, QTextEngine::ShaperFlags flags) const +{ + if (m_directWriteFontFace != 0) { + QVarLengthArray<UINT32> codePoints(len); + for (int i=0; i<len; ++i) { + codePoints[i] = getChar(str, i, len); + if (flags & QTextEngine::RightToLeft) + codePoints[i] = QChar::mirroredChar(codePoints[i]); + } + + QVarLengthArray<UINT16> glyphIndices(len); + HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(codePoints.data(), + len, + glyphIndices.data()); + + if (SUCCEEDED(hr)) { + for (int i=0; i<len; ++i) + glyphs->glyphs[i] = glyphIndices[i]; + + *nglyphs = len; + + if (!(flags & QTextEngine::GlyphIndicesOnly)) + recalcAdvances(glyphs, 0); + + return true; + } else { + qErrnoWarning("QFontEngineDirectWrite::stringToCMap: GetGlyphIndicesW failed"); + } + } + + return false; +} + +void QFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const +{ + if (m_directWriteFontFace == 0) + return; + + QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs); + + // ### Caching? + for(int i=0; i<glyphs->numGlyphs; i++) + glyphIndices[i] = UINT16(glyphs->glyphs[i]); + + QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size()); + HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(), + glyphIndices.size(), + glyphMetrics.data()); + if (SUCCEEDED(hr)) { + for (int i=0; i<glyphs->numGlyphs; ++i) { + glyphs->advances_x[i] = DESIGN_TO_LOGICAL(glyphMetrics[i].advanceWidth); + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + glyphs->advances_y[i] = 0; + } + } else { + qErrnoWarning("QFontEngineDirectWrite::recalcAdvances: GetDesignGlyphMetrics failed"); + } +} + +void QFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags) +{ + if (m_directWriteFontFace == 0) + return; + + QVarLengthArray<UINT16> glyphIndices(nglyphs); + QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(nglyphs); + QVarLengthArray<FLOAT> glyphAdvances(nglyphs); + + for (int i=0; i<nglyphs; ++i) { + glyphIndices[i] = glyphs[i]; + glyphOffsets[i].advanceOffset = positions[i].x.toReal(); + glyphOffsets[i].ascenderOffset = -positions[i].y.toReal(); + glyphAdvances[i] = 0.0; + } + + GeometrySink geometrySink(path); + HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline( + fontDef.pixelSize, + glyphIndices.data(), + glyphAdvances.data(), + glyphOffsets.data(), + nglyphs, + false, + flags & QTextItem::RightToLeft, + &geometrySink + ); + + if (FAILED(hr)) + qErrnoWarning("QFontEngineDirectWrite::addGlyphsToPath: GetGlyphRunOutline failed"); +} + +glyph_metrics_t QFontEngineDirectWrite::boundingBox(const QGlyphLayout &glyphs) +{ + if (glyphs.numGlyphs == 0) + return glyph_metrics_t(); + + bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics; + + QFixed w = 0; + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += round ? glyphs.effectiveAdvance(i).round() : glyphs.effectiveAdvance(i); + + } + + return glyph_metrics_t(0, -m_ascent, w - lastRightBearing(glyphs), m_ascent + m_descent, w, 0); +} + +glyph_metrics_t QFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, + const QTransform &matrix, + GlyphFormat /*format*/) +{ + glyph_metrics_t bbox = QFontEngine::boundingBox(glyph, matrix); // To get transformed advance + + UINT16 glyphIndex = glyph; + FLOAT glyphAdvance = 0; + + DWRITE_GLYPH_OFFSET glyphOffset; + glyphOffset.advanceOffset = 0; + glyphOffset.ascenderOffset = 0; + + DWRITE_GLYPH_RUN glyphRun; + glyphRun.fontFace = m_directWriteFontFace; + glyphRun.fontEmSize = fontDef.pixelSize; + glyphRun.glyphCount = 1; + glyphRun.glyphIndices = &glyphIndex; + glyphRun.glyphAdvances = &glyphAdvance; + glyphRun.isSideways = false; + glyphRun.bidiLevel = 0; + glyphRun.glyphOffsets = &glyphOffset; + + DWRITE_MATRIX transform; + transform.dx = subPixelPosition.toReal(); + transform.dy = 0; + transform.m11 = matrix.m11(); + transform.m12 = matrix.m12(); + transform.m21 = matrix.m21(); + transform.m22 = matrix.m22(); + + IDWriteGlyphRunAnalysis *glyphAnalysis = NULL; + HRESULT hr = m_directWriteFactory->CreateGlyphRunAnalysis( + &glyphRun, + 1.0f, + &transform, + DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, + DWRITE_MEASURING_MODE_NATURAL, + 0.0, 0.0, + &glyphAnalysis + ); + + if (SUCCEEDED(hr)) { + RECT rect; + glyphAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &rect); + glyphAnalysis->Release(); + + return glyph_metrics_t(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + bbox.xoff, bbox.yoff); + } else { + return glyph_metrics_t(); + } +} + +glyph_metrics_t QFontEngineDirectWrite::boundingBox(glyph_t g) +{ + if (m_directWriteFontFace == 0) + return glyph_metrics_t(); + + UINT16 glyphIndex = g; + + DWRITE_GLYPH_METRICS glyphMetrics; + HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics); + if (SUCCEEDED(hr)) { + QFixed advanceWidth = DESIGN_TO_LOGICAL(glyphMetrics.advanceWidth); + QFixed leftSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.leftSideBearing); + QFixed rightSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.rightSideBearing); + QFixed advanceHeight = DESIGN_TO_LOGICAL(glyphMetrics.advanceHeight); + QFixed verticalOriginY = DESIGN_TO_LOGICAL(glyphMetrics.verticalOriginY); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + advanceWidth = advanceWidth.round(); + advanceHeight = advanceHeight.round(); + } + + QFixed width = advanceWidth - leftSideBearing - rightSideBearing; + + return glyph_metrics_t(-leftSideBearing, -verticalOriginY, + width, m_ascent + m_descent, + advanceWidth, advanceHeight); + } else { + qErrnoWarning("QFontEngineDirectWrite::boundingBox: GetDesignGlyphMetrics failed"); + } + + return glyph_metrics_t(); +} + +QFixed QFontEngineDirectWrite::ascent() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_ascent.round() + : m_ascent; +} + +QFixed QFontEngineDirectWrite::descent() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? (m_descent - 1).round() + : (m_descent - 1); +} + +QFixed QFontEngineDirectWrite::leading() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_lineGap.round() + : m_lineGap; +} + +QFixed QFontEngineDirectWrite::xHeight() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_xHeight.round() + : m_xHeight; +} + +qreal QFontEngineDirectWrite::maxCharWidth() const +{ + // ### + return 0; +} + +extern uint qt_pow_gamma[256]; + +QImage QFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, + const QTransform &xform) +{ + QImage im = imageForGlyph(glyph, subPixelPosition, 0, xform); + + QImage indexed(im.width(), im.height(), QImage::Format_Indexed8); + QVector<QRgb> colors(256); + for (int i=0; i<256; ++i) + colors[i] = qRgba(0, 0, 0, i); + indexed.setColorTable(colors); + + for (int y=0; y<im.height(); ++y) { + uint *src = (uint*) im.scanLine(y); + uchar *dst = indexed.scanLine(y); + for (int x=0; x<im.width(); ++x) { + *dst = 255 - (qt_pow_gamma[qGray(0xffffffff - *src)] * 255. / 2047.); + ++dst; + ++src; + } + } + + return indexed; +} + +bool QFontEngineDirectWrite::supportsSubPixelPositions() const +{ + return true; +} + +QImage QFontEngineDirectWrite::imageForGlyph(glyph_t t, + QFixed subPixelPosition, + int margin, + const QTransform &xform) +{ + UINT16 glyphIndex = t; + FLOAT glyphAdvance = 0; + + DWRITE_GLYPH_OFFSET glyphOffset; + glyphOffset.advanceOffset = 0; + glyphOffset.ascenderOffset = 0; + + DWRITE_GLYPH_RUN glyphRun; + glyphRun.fontFace = m_directWriteFontFace; + glyphRun.fontEmSize = fontDef.pixelSize; + glyphRun.glyphCount = 1; + glyphRun.glyphIndices = &glyphIndex; + glyphRun.glyphAdvances = &glyphAdvance; + glyphRun.isSideways = false; + glyphRun.bidiLevel = 0; + glyphRun.glyphOffsets = &glyphOffset; + + DWRITE_MATRIX transform; + transform.dx = subPixelPosition.toReal(); + transform.dy = 0; + transform.m11 = xform.m11(); + transform.m12 = xform.m12(); + transform.m21 = xform.m21(); + transform.m22 = xform.m22(); + + IDWriteGlyphRunAnalysis *glyphAnalysis = NULL; + HRESULT hr = m_directWriteFactory->CreateGlyphRunAnalysis( + &glyphRun, + 1.0f, + &transform, + DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, + DWRITE_MEASURING_MODE_NATURAL, + 0.0, 0.0, + &glyphAnalysis + ); + + if (SUCCEEDED(hr)) { + RECT rect; + glyphAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &rect); + + rect.left -= margin; + rect.top -= margin; + rect.right += margin; + rect.bottom += margin; + + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + + int size = width * height * 3; + if (size > 0) { + BYTE *alphaValues = new BYTE[size]; + qMemSet(alphaValues, size, 0); + + hr = glyphAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, + &rect, + alphaValues, + size); + + if (SUCCEEDED(hr)) { + QImage img(width, height, QImage::Format_RGB32); + img.fill(0xffffffff); + + for (int y=0; y<height; ++y) { + uint *dest = reinterpret_cast<uint *>(img.scanLine(y)); + BYTE *src = alphaValues + width * 3 * y; + + for (int x=0; x<width; ++x) { + dest[x] = *(src) << 16 + | *(src + 1) << 8 + | *(src + 2); + + src += 3; + } + } + + delete[] alphaValues; + glyphAnalysis->Release(); + + return img; + } else { + delete[] alphaValues; + glyphAnalysis->Release(); + + qErrnoWarning("QFontEngineDirectWrite::imageForGlyph: CreateAlphaTexture failed"); + } + } + } else { + qErrnoWarning("QFontEngineDirectWrite::imageForGlyph: CreateGlyphRunAnalysis failed"); + } + + return QImage(); +} + +QImage QFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t, + QFixed subPixelPosition, + int margin, + const QTransform &xform) +{ + QImage mask = imageForGlyph(t, subPixelPosition, margin, xform); + return mask.depth() == 32 + ? mask + : mask.convertToFormat(QImage::Format_RGB32); +} + +const char *QFontEngineDirectWrite::name() const +{ + return 0; +} + +bool QFontEngineDirectWrite::canRender(const QChar *string, int len) +{ + QVarLengthArray<UINT32> codePoints(len); + int actualLength = 0; + for (int i=0; i<len; ++i, actualLength++) + codePoints[actualLength] = getChar(string, i, len); + + QVarLengthArray<UINT16> glyphIndices(actualLength); + HRESULT hr = m_directWriteFontFace->GetGlyphIndices(codePoints.data(), actualLength, + glyphIndices.data()); + if (FAILED(hr)) { + qErrnoWarning(hr, "QFontEngineDirectWrite::canRender: GetGlyphIndices failed"); + return false; + } else { + for (int i=0; i<glyphIndices.size(); ++i) { + if (glyphIndices.at(i) == 0) + return false; + } + + return true; + } +} + +QFontEngine::Type QFontEngineDirectWrite::type() const +{ + return QFontEngine::DirectWrite; +} + +QFontEngine *QFontEngineDirectWrite::cloneWithSize(qreal pixelSize) const +{ + QFontEngine *fontEngine = new QFontEngineDirectWrite(m_directWriteFactory, m_directWriteFontFace, + pixelSize); + + fontEngine->fontDef = fontDef; + fontEngine->fontDef.pixelSize = pixelSize; + + return fontEngine; +} + +QT_END_NAMESPACE + +#endif // QT_NO_DIRECTWRITE diff --git a/src/gui/text/qfontenginedirectwrite_p.h b/src/gui/text/qfontenginedirectwrite_p.h new file mode 100644 index 0000000..edf1e6a --- /dev/null +++ b/src/gui/text/qfontenginedirectwrite_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFONTENGINEDIRECTWRITE_H +#define QFONTENGINEDIRECTWRITE_H + +#ifndef QT_NO_DIRECTWRITE + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qfontengine_p.h" + +struct IDWriteFont ; +struct IDWriteFontFace ; +struct IDWriteFactory ; +struct IDWriteBitmapRenderTarget ; +struct IDWriteGdiInterop ; + +QT_BEGIN_NAMESPACE + +class QFontEngineDirectWrite : public QFontEngine +{ + Q_OBJECT +public: + explicit QFontEngineDirectWrite(IDWriteFactory *directWriteFactory, + IDWriteFontFace *directWriteFontFace, + qreal pixelSize); + ~QFontEngineDirectWrite(); + + QFixed lineThickness() const; + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; + QFixed emSquareSize() const; + + bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const; + void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const; + + void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags); + + glyph_metrics_t boundingBox(const QGlyphLayout &glyphs); + glyph_metrics_t boundingBox(glyph_t g); + glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, + QFixed subPixelPosition, + const QTransform &matrix, + GlyphFormat format); + + QFixed ascent() const; + QFixed descent() const; + QFixed leading() const; + QFixed xHeight() const; + qreal maxCharWidth() const; + + const char *name() const; + + bool supportsSubPixelPositions() const; + + QImage alphaMapForGlyph(glyph_t, QFixed subPixelPosition, const QTransform &t); + QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, int margin, + const QTransform &xform); + + QFontEngine *cloneWithSize(qreal pixelSize) const; + + bool canRender(const QChar *string, int len); + Type type() const; + +private: + friend class QRawFontPrivate; + + QImage imageForGlyph(glyph_t t, QFixed subPixelPosition, int margin, const QTransform &xform); + void collectMetrics(); + + IDWriteFontFace *m_directWriteFontFace; + IDWriteFactory *m_directWriteFactory; + IDWriteBitmapRenderTarget *m_directWriteBitmapRenderTarget; + + QFixed m_lineThickness; + int m_unitsPerEm; + QFixed m_ascent; + QFixed m_descent; + QFixed m_xHeight; + QFixed m_lineGap; + FaceId m_faceId; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_DIRECTWRITE + +#endif // QFONTENGINEDIRECTWRITE_H diff --git a/src/gui/text/qfontengineglyphcache_p.h b/src/gui/text/qfontengineglyphcache_p.h index d612692..3ac5392 100644 --- a/src/gui/text/qfontengineglyphcache_p.h +++ b/src/gui/text/qfontengineglyphcache_p.h @@ -72,7 +72,7 @@ QT_BEGIN_NAMESPACE -class QFontEngineGlyphCache +class QFontEngineGlyphCache: public QSharedData { public: enum Type { diff --git a/src/gui/text/qfontinfo.h b/src/gui/text/qfontinfo.h index 1238cba..37a724e 100644 --- a/src/gui/text/qfontinfo.h +++ b/src/gui/text/qfontinfo.h @@ -61,6 +61,7 @@ public: QFontInfo &operator=(const QFontInfo &); QString family() const; + QString styleName() const; int pixelSize() const; int pointSize() const; qreal pointSizeF() const; diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp index f23ab88..1d93d54 100644 --- a/src/gui/text/qfontmetrics.cpp +++ b/src/gui/text/qfontmetrics.cpp @@ -63,7 +63,6 @@ extern void qt_format_text(const QFont& font, const QRectF &_r, int tf, const QString &text, QRectF *brect, int tabStops, int *tabArray, int tabArrayLen, QPainter *painter); -Q_GUI_EXPORT extern int qt_defaultDpi(); /***************************************************************************** QFontMetrics member functions @@ -443,6 +442,24 @@ bool QFontMetrics::inFont(QChar ch) const } /*! + \fn bool QFontMetrics::inFontUcs4(uint character) const + \since 4.8 + + Returns true if the given \a character encoded in UCS-4/UTF-32 is a valid + character in the font; otherwise returns false. +*/ +bool QFontMetrics::inFontUcs4(uint ucs4) const +{ + const int script = QUnicodeTables::script(ucs4); + QFontEngine *engine = d->engineForScript(script); + Q_ASSERT(engine != 0); + if (engine->type() == QFontEngine::Box) + return false; + QString utf16 = QString::fromUcs4(&ucs4, 1); + return engine->canRender(utf16.data(), utf16.length()); +} + +/*! Returns the left bearing of character \a ch in the font. The left bearing is the right-ward distance of the left-most pixel @@ -1315,6 +1332,24 @@ bool QFontMetricsF::inFont(QChar ch) const } /*! + \fn bool QFontMetricsF::inFontUcs4(uint ch) const + \since 4.8 + + Returns true if the character given by \a ch, encoded in UCS-4/UTF-32, + is a valid character in the font; otherwise returns false. +*/ +bool QFontMetricsF::inFontUcs4(uint ucs4) const +{ + const int script = QUnicodeTables::script(ucs4); + QFontEngine *engine = d->engineForScript(script); + Q_ASSERT(engine != 0); + if (engine->type() == QFontEngine::Box) + return false; + QString utf16 = QString::fromUcs4(&ucs4, 1); + return engine->canRender(utf16.data(), utf16.length()); +} + +/*! Returns the left bearing of character \a ch in the font. The left bearing is the right-ward distance of the left-most pixel @@ -1779,7 +1814,7 @@ qreal QFontMetricsF::lineWidth() const Use the boundingRect() function in combination with QString::left() instead. - + \oldcode QRect rect = boundingRect(text, len); \newcode diff --git a/src/gui/text/qfontmetrics.h b/src/gui/text/qfontmetrics.h index 73958cc..6a2db60 100644 --- a/src/gui/text/qfontmetrics.h +++ b/src/gui/text/qfontmetrics.h @@ -71,6 +71,10 @@ public: ~QFontMetrics(); QFontMetrics &operator=(const QFontMetrics &); +#ifdef Q_COMPILER_RVALUE_REFS + inline QFontMetrics &operator=(QFontMetrics &&other) + { qSwap(d, other.d); return *this; } +#endif int ascent() const; int descent() const; @@ -85,6 +89,7 @@ public: int averageCharWidth() const; bool inFont(QChar) const; + bool inFontUcs4(uint ucs4) const; int leftBearing(QChar) const; int rightBearing(QChar) const; @@ -148,7 +153,10 @@ public: QFontMetricsF &operator=(const QFontMetricsF &); QFontMetricsF &operator=(const QFontMetrics &); - +#ifdef Q_COMPILER_RVALUE_REFS + inline QFontMetricsF &operator=(QFontMetricsF &&other) + { qSwap(d, other.d); return *this; } +#endif qreal ascent() const; qreal descent() const; qreal height() const; @@ -162,6 +170,7 @@ public: qreal averageCharWidth() const; bool inFont(QChar) const; + bool inFontUcs4(uint ucs4) const; qreal leftBearing(QChar) const; qreal rightBearing(QChar) const; diff --git a/src/gui/text/qglyphrun.cpp b/src/gui/text/qglyphrun.cpp new file mode 100644 index 0000000..7f43378 --- /dev/null +++ b/src/gui/text/qglyphrun.cpp @@ -0,0 +1,358 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglobal.h" + +#if !defined(QT_NO_RAWFONT) + +#include "qglyphrun.h" +#include "qglyphrun_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGlyphRun + \brief The QGlyphRun class provides direct access to the internal glyphs in a font. + \since 4.8 + + \ingroup text + \mainclass + + When Qt displays a string of text encoded in Unicode, it will first convert the Unicode points + into a list of glyph indexes and a list of positions based on one or more fonts. The Unicode + representation of the text and the QFont object will in this case serve as a convenient + abstraction that hides the details of what actually takes place when displaying the text + on-screen. For instance, by the time the text actually reaches the screen, it may be represented + by a set of fonts in addition to the one specified by the user, e.g. in case the originally + selected font did not support all the writing systems contained in the text. + + Under certain circumstances, it can be useful as an application developer to have more low-level + control over which glyphs in a specific font are drawn to the screen. This could for instance + be the case in applications that use an external font engine and text shaper together with Qt. + QGlyphRun provides an interface to the raw data needed to get text on the screen. It + contains a list of glyph indexes, a position for each glyph and a font. + + It is the user's responsibility to ensure that the selected font actually contains the + provided glyph indexes. + + QTextLayout::glyphRuns() or QTextFragment::glyphRuns() can be used to convert unicode encoded + text into a list of QGlyphRun objects, and QPainter::drawGlyphRun() can be used to draw the + glyphs. + + \note Please note that QRawFont is considered local to the thread in which it is constructed. + This in turn means that a new QRawFont will have to be created and set on the QGlyphRun if it is + moved to a different thread. If the QGlyphRun contains a reference to a QRawFont from a different + thread than the current, it will not be possible to draw the glyphs using a QPainter, as the + QRawFont is considered invalid and inaccessible in this case. +*/ + + +/*! + Constructs an empty QGlyphRun object. +*/ +QGlyphRun::QGlyphRun() : d(new QGlyphRunPrivate) +{ +} + +/*! + Constructs a QGlyphRun object which is a copy of \a other. +*/ +QGlyphRun::QGlyphRun(const QGlyphRun &other) +{ + d = other.d; +} + +/*! + Destroys the QGlyphRun. +*/ +QGlyphRun::~QGlyphRun() +{ + // Required for QExplicitlySharedDataPointer +} + +/*! + \internal +*/ +void QGlyphRun::detach() +{ + if (d->ref != 1) + d.detach(); +} + +/*! + Assigns \a other to this QGlyphRun object. +*/ +QGlyphRun &QGlyphRun::operator=(const QGlyphRun &other) +{ + d = other.d; + return *this; +} + +/*! + Compares \a other to this QGlyphRun object. Returns true if the list of glyph indexes, + the list of positions and the font are all equal, otherwise returns false. +*/ +bool QGlyphRun::operator==(const QGlyphRun &other) const +{ + if (d == other.d) + return true; + + if ((d->glyphIndexDataSize != other.d->glyphIndexDataSize) + || (d->glyphPositionDataSize != other.d->glyphPositionDataSize)) { + return false; + } + + if (d->glyphIndexData != other.d->glyphIndexData) { + for (int i = 0; i < d->glyphIndexDataSize; ++i) { + if (d->glyphIndexData[i] != other.d->glyphIndexData[i]) + return false; + } + } + if (d->glyphPositionData != other.d->glyphPositionData) { + for (int i = 0; i < d->glyphPositionDataSize; ++i) { + if (d->glyphPositionData[i] != other.d->glyphPositionData[i]) + return false; + } + } + + return (d->overline == other.d->overline + && d->underline == other.d->underline + && d->strikeOut == other.d->strikeOut + && d->rawFont == other.d->rawFont); +} + +/*! + \fn bool QGlyphRun::operator!=(const QGlyphRun &other) const + + Compares \a other to this QGlyphRun object. Returns true if any of the list of glyph + indexes, the list of positions or the font are different, otherwise returns false. +*/ + +/*! + Returns the font selected for this QGlyphRun object. + + \sa setRawFont() +*/ +QRawFont QGlyphRun::rawFont() const +{ + return d->rawFont; +} + +/*! + Sets the font specified by \a rawFont to be the font used to look up the + glyph indexes. + + \sa rawFont(), setGlyphIndexes() +*/ +void QGlyphRun::setRawFont(const QRawFont &rawFont) +{ + detach(); + d->rawFont = rawFont; +} + +/*! + Returns the glyph indexes for this QGlyphRun object. + + \sa setGlyphIndexes(), setPositions() +*/ +QVector<quint32> QGlyphRun::glyphIndexes() const +{ + if (d->glyphIndexes.constData() == d->glyphIndexData) { + return d->glyphIndexes; + } else { + QVector<quint32> indexes(d->glyphIndexDataSize); + qMemCopy(indexes.data(), d->glyphIndexData, d->glyphIndexDataSize * sizeof(quint32)); + return indexes; + } +} + +/*! + Set the glyph indexes for this QGlyphRun object to \a glyphIndexes. The glyph indexes must + be valid for the selected font. +*/ +void QGlyphRun::setGlyphIndexes(const QVector<quint32> &glyphIndexes) +{ + detach(); + d->glyphIndexes = glyphIndexes; // Keep a reference to the QVector to avoid copying + d->glyphIndexData = glyphIndexes.constData(); + d->glyphIndexDataSize = glyphIndexes.size(); +} + +/*! + Returns the position of the edge of the baseline for each glyph in this set of glyph indexes. +*/ +QVector<QPointF> QGlyphRun::positions() const +{ + if (d->glyphPositions.constData() == d->glyphPositionData) { + return d->glyphPositions; + } else { + QVector<QPointF> glyphPositions(d->glyphPositionDataSize); + qMemCopy(glyphPositions.data(), d->glyphPositionData, + d->glyphPositionDataSize * sizeof(QPointF)); + return glyphPositions; + } +} + +/*! + Sets the positions of the edge of the baseline for each glyph in this set of glyph indexes to + \a positions. +*/ +void QGlyphRun::setPositions(const QVector<QPointF> &positions) +{ + detach(); + d->glyphPositions = positions; // Keep a reference to the vector to avoid copying + d->glyphPositionData = positions.constData(); + d->glyphPositionDataSize = positions.size(); +} + +/*! + Clears all data in the QGlyphRun object. +*/ +void QGlyphRun::clear() +{ + detach(); + d->rawFont = QRawFont(); + d->strikeOut = false; + d->overline = false; + d->underline = false; + + setPositions(QVector<QPointF>()); + setGlyphIndexes(QVector<quint32>()); +} + +/*! + Sets the glyph indexes and positions of this QGlyphRun to use the first \a size + elements in the arrays \a glyphIndexArray and \a glyphPositionArray. The data is + \e not copied. The caller must guarantee that the arrays are not deleted as long + as this QGlyphRun and any copies of it exists. + + \sa setGlyphIndexes(), setPositions() +*/ +void QGlyphRun::setRawData(const quint32 *glyphIndexArray, const QPointF *glyphPositionArray, + int size) +{ + detach(); + d->glyphIndexes.clear(); + d->glyphPositions.clear(); + + d->glyphIndexData = glyphIndexArray; + d->glyphPositionData = glyphPositionArray; + d->glyphIndexDataSize = d->glyphPositionDataSize = size; +} + +/*! + Returns true if this QGlyphRun should be painted with an overline decoration. + + \sa setOverline() +*/ +bool QGlyphRun::overline() const +{ + return d->overline; +} + +/*! + Indicates that this QGlyphRun should be painted with an overline decoration if \a overline is true. + Otherwise the QGlyphRun should be painted with no overline decoration. + + \sa overline() +*/ +void QGlyphRun::setOverline(bool overline) +{ + if (d->overline == overline) + return; + + detach(); + d->overline = overline; +} + +/*! + Returns true if this QGlyphRun should be painted with an underline decoration. + + \sa setUnderline() +*/ +bool QGlyphRun::underline() const +{ + return d->underline; +} + +/*! + Indicates that this QGlyphRun should be painted with an underline decoration if \a underline is + true. Otherwise the QGlyphRun should be painted with no underline decoration. + + \sa underline() +*/ +void QGlyphRun::setUnderline(bool underline) +{ + if (d->underline == underline) + return; + + detach(); + d->underline = underline; +} + +/*! + Returns true if this QGlyphRun should be painted with a strike out decoration. + + \sa setStrikeOut() +*/ +bool QGlyphRun::strikeOut() const +{ + return d->strikeOut; +} + +/*! + Indicates that this QGlyphRun should be painted with an strike out decoration if \a strikeOut is + true. Otherwise the QGlyphRun should be painted with no strike out decoration. + + \sa strikeOut() +*/ +void QGlyphRun::setStrikeOut(bool strikeOut) +{ + if (d->strikeOut == strikeOut) + return; + + detach(); + d->strikeOut = strikeOut; +} + +QT_END_NAMESPACE + +#endif // QT_NO_RAWFONT diff --git a/src/gui/text/qglyphrun.h b/src/gui/text/qglyphrun.h new file mode 100644 index 0000000..bc7f4ff --- /dev/null +++ b/src/gui/text/qglyphrun.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLYPHRUN_H +#define QGLYPHRUN_H + +#include <QtCore/qsharedpointer.h> +#include <QtCore/qvector.h> +#include <QtCore/qpoint.h> +#include <QtGui/qrawfont.h> + +#if !defined(QT_NO_RAWFONT) + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QGlyphRunPrivate; +class Q_GUI_EXPORT QGlyphRun +{ +public: + QGlyphRun(); + QGlyphRun(const QGlyphRun &other); + ~QGlyphRun(); + + QRawFont rawFont() const; + void setRawFont(const QRawFont &rawFont); + + void setRawData(const quint32 *glyphIndexArray, + const QPointF *glyphPositionArray, + int size); + + QVector<quint32> glyphIndexes() const; + void setGlyphIndexes(const QVector<quint32> &glyphIndexes); + + QVector<QPointF> positions() const; + void setPositions(const QVector<QPointF> &positions); + + void clear(); + + QGlyphRun &operator=(const QGlyphRun &other); + + bool operator==(const QGlyphRun &other) const; + inline bool operator!=(const QGlyphRun &other) const + { return !operator==(other); } + + void setOverline(bool overline); + bool overline() const; + + void setUnderline(bool underline); + bool underline() const; + + void setStrikeOut(bool strikeOut); + bool strikeOut() const; + +private: + friend class QGlyphRunPrivate; + friend class QTextLine; + + QGlyphRun operator+(const QGlyphRun &other) const; + QGlyphRun &operator+=(const QGlyphRun &other); + + void detach(); + QExplicitlySharedDataPointer<QGlyphRunPrivate> d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_RAWFONT + +#endif // QGLYPHS_H diff --git a/src/gui/text/qglyphrun_p.h b/src/gui/text/qglyphrun_p.h new file mode 100644 index 0000000..a7745e6 --- /dev/null +++ b/src/gui/text/qglyphrun_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGLYPHRUN_P_H +#define QGLYPHRUN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of internal files. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include "qglyphrun.h" +#include "qrawfont.h" + +#include <qfont.h> + +#if !defined(QT_NO_RAWFONT) + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGlyphRunPrivate: public QSharedData +{ +public: + QGlyphRunPrivate() + : overline(false) + , underline(false) + , strikeOut(false) + , glyphIndexData(glyphIndexes.constData()) + , glyphIndexDataSize(0) + , glyphPositionData(glyphPositions.constData()) + , glyphPositionDataSize(0) + { + } + + QGlyphRunPrivate(const QGlyphRunPrivate &other) + : QSharedData(other) + , glyphIndexes(other.glyphIndexes) + , glyphPositions(other.glyphPositions) + , rawFont(other.rawFont) + , overline(other.overline) + , underline(other.underline) + , strikeOut(other.strikeOut) + , glyphIndexData(other.glyphIndexData) + , glyphIndexDataSize(other.glyphIndexDataSize) + , glyphPositionData(other.glyphPositionData) + , glyphPositionDataSize(other.glyphPositionDataSize) + { + } + + QVector<quint32> glyphIndexes; + QVector<QPointF> glyphPositions; + QRawFont rawFont; + + uint overline : 1; + uint underline : 1; + uint strikeOut : 1; + + const quint32 *glyphIndexData; + int glyphIndexDataSize; + + const QPointF *glyphPositionData; + int glyphPositionDataSize; + + static QGlyphRunPrivate *get(const QGlyphRun &glyphRun) + { + return glyphRun.d.data(); + } +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGLYPHS_P_H + +#endif // QT_NO_RAWFONT diff --git a/src/gui/text/qpfutil.cpp b/src/gui/text/qpfutil.cpp index f02571b..8e69a4b 100644 --- a/src/gui/text/qpfutil.cpp +++ b/src/gui/text/qpfutil.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -static QFontEngineQPF::TagType tagTypes[QFontEngineQPF::NumTags] = { +static const QFontEngineQPF::TagType tagTypes[QFontEngineQPF::NumTags] = { QFontEngineQPF::StringType, // FontName QFontEngineQPF::StringType, // FileName QFontEngineQPF::UInt32Type, // FileIndex diff --git a/src/gui/text/qplatformfontdatabase_qpa.cpp b/src/gui/text/qplatformfontdatabase_qpa.cpp new file mode 100644 index 0000000..e3eeca5 --- /dev/null +++ b/src/gui/text/qplatformfontdatabase_qpa.cpp @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformfontdatabase_qpa.h" +#include <QtGui/private/qfontengine_p.h> +#include <QtGui/private/qfontengine_qpa_p.h> +#include <QtCore/QLibraryInfo> +#include <QtCore/QDir> + +QT_BEGIN_NAMESPACE + +extern void qt_registerFont(const QString &familyname, const QString &foundryname, int weight, + QFont::Style style, int stretch, bool antialiased,bool scalable, int pixelSize, + const QSupportedWritingSystems &writingSystems, void *hanlde); + +/*! + \fn void QPlatformFontDatabase::registerQPF2Font(const QByteArray &dataArray, void *) + + Registers the pre-rendered QPF2 font contained in the given \a dataArray. + + \sa registerFont() +*/ +void QPlatformFontDatabase::registerQPF2Font(const QByteArray &dataArray, void *handle) +{ + if (dataArray.size() == 0) + return; + + const uchar *data = reinterpret_cast<const uchar *>(dataArray.constData()); + if (QFontEngineQPA::verifyHeader(data, dataArray.size())) { + QString fontName = QFontEngineQPA::extractHeaderField(data, QFontEngineQPA::Tag_FontName).toString(); + int pixelSize = QFontEngineQPA::extractHeaderField(data, QFontEngineQPA::Tag_PixelSize).toInt(); + QVariant weight = QFontEngineQPA::extractHeaderField(data, QFontEngineQPA::Tag_Weight); + QVariant style = QFontEngineQPA::extractHeaderField(data, QFontEngineQPA::Tag_Style); + QByteArray writingSystemBits = QFontEngineQPA::extractHeaderField(data, QFontEngineQPA::Tag_WritingSystems).toByteArray(); + + if (!fontName.isEmpty() && pixelSize) { + QFont::Weight fontWeight = QFont::Normal; + if (weight.type() == QVariant::Int || weight.type() == QVariant::UInt) + fontWeight = QFont::Weight(weight.toInt()); + + QFont::Style fontStyle = static_cast<QFont::Style>(style.toInt()); + + QSupportedWritingSystems writingSystems; + for (int i = 0; i < writingSystemBits.count(); ++i) { + uchar currentByte = writingSystemBits.at(i); + for (int j = 0; j < 8; ++j) { + if (currentByte & 1) + writingSystems.setSupported(QFontDatabase::WritingSystem(i * 8 + j)); + currentByte >>= 1; + } + } + QFont::Stretch stretch = QFont::Unstretched; + registerFont(fontName,QString(),fontWeight,fontStyle,stretch,true,false,pixelSize,writingSystems,handle); + } + } else { + qDebug() << "header verification of QPF2 font failed. maybe it is corrupt?"; + } +} + +/*! + \fn void QPlatformFontDatabase::registerFont(const QString &familyName, + const QString &foundryName, QFont::Weight weight, QFont::Style style, + QFont::Stretch stretch, bool antialiased, bool scalable, int pixelSize, + const QSupportedWritingSystems &writingSystems, void *usrPtr) + + Registers a font with the given set of attributes describing the font's + foundry, family name, style and stretch information, pixel size, and + supported writing systems. Additional information about whether the font + can be scaled and antialiased can also be provided. + + The foundry name and font family are described by \a foundryName and + \a familyName. The font weight (light, normal, bold, etc.), style (normal, + oblique, italic) and stretch information (condensed, expanded, unstretched, + etc.) are specified by \a weight, \a style and \a stretch. + + Some fonts can be antialiased and scaled; \a scalable and \a antialiased + can be set to true for fonts with these attributes. The intended pixel + size of non-scalable fonts is specified by \a pixelSize; this value will be + ignored for scalable fonts. + + The writing systems supported by the font are specified by the + \a writingSystems argument. + + \sa registerQPF2Font() +*/ +void QPlatformFontDatabase::registerFont(const QString &familyname, const QString &foundryname, QFont::Weight weight, + QFont::Style style, QFont::Stretch stretch, bool antialiased, bool scalable, int pixelSize, + const QSupportedWritingSystems &writingSystems, void *usrPtr) +{ + if (scalable) + pixelSize = 0; + qt_registerFont(familyname,foundryname,weight,style,stretch,antialiased,scalable,pixelSize,writingSystems,usrPtr); +} + +class QWritingSystemsPrivate +{ +public: + QWritingSystemsPrivate() + : ref(1) + , vector(QFontDatabase::WritingSystemsCount,false) + { + } + + QWritingSystemsPrivate(const QWritingSystemsPrivate *other) + : ref(1) + , vector(other->vector) + { + } + + QAtomicInt ref; + QVector<bool> vector; +}; + +QSupportedWritingSystems::QSupportedWritingSystems() +{ + d = new QWritingSystemsPrivate; +} + +QSupportedWritingSystems::QSupportedWritingSystems(const QSupportedWritingSystems &other) +{ + d = other.d; + d->ref.ref(); +} + +/*! + Assigns the \a other supported writing systems object to this object. +*/ +QSupportedWritingSystems &QSupportedWritingSystems::operator=(const QSupportedWritingSystems &other) +{ + if (d != other.d) { + other.d->ref.ref(); + if (!d->ref.deref()) + delete d; + d = other.d; + } + return *this; +} + +/*! + Destroys the object. +*/ +QSupportedWritingSystems::~QSupportedWritingSystems() +{ + if (!d->ref.deref()) + delete d; +} + +void QSupportedWritingSystems::detach() +{ + if (d->ref != 1) { + QWritingSystemsPrivate *newd = new QWritingSystemsPrivate(d); + if (!d->ref.deref()) + delete d; + d = newd; + } +} + +/*! + Sets the supported state of the writing system given by \a writingSystem to + the value specified by \a support. A value of true indicates that the + writing system is supported; a value of false indicates that it is + unsupported. + + \sa supported() +*/ +void QSupportedWritingSystems::setSupported(QFontDatabase::WritingSystem writingSystem, bool support) +{ + detach(); + d->vector[writingSystem] = support; +} + +/*! + Returns true if the writing system given by \a writingSystem is supported; + otherwise returns false. + + \sa setSupported() +*/ +bool QSupportedWritingSystems::supported(QFontDatabase::WritingSystem writingSystem) const +{ + return d->vector.at(writingSystem); +} + +/*! + \class QSupportedWritingSystems + \brief The QSupportedWritingSystems class is used when registering fonts with the internal Qt + fontdatabase + \ingroup painting + \since 4.8 + + Its to provide an easy to use interface for indicating what writing systems a specific font + supports. + +*/ + +/*! + This function is called once at startup by Qts internal fontdatabase. Reimplement this function + in a subclass for a convenient place to initialise the internal fontdatabase. + + The default implementation looks in the fontDir() location and registers all qpf2 fonts. +*/ +void QPlatformFontDatabase::populateFontDatabase() +{ + QString fontpath = fontDir(); + + if(!QFile::exists(fontpath)) { + qFatal("QFontDatabase: Cannot find font directory %s - is Qt installed correctly?", + qPrintable(fontpath)); + } + + QDir dir(fontpath); + dir.setNameFilters(QStringList() << QLatin1String("*.qpf2")); + dir.refresh(); + for (int i = 0; i < int(dir.count()); ++i) { + const QByteArray fileName = QFile::encodeName(dir.absoluteFilePath(dir[i])); + QFile file(QString::fromLocal8Bit(fileName)); + if (file.open(QFile::ReadOnly)) { + const QByteArray fileData = file.readAll(); + QByteArray *fileDataPtr = new QByteArray(fileData); + registerQPF2Font(fileData, fileDataPtr); + } + } +} + +/*! + Returns the font engine that can be used to render the font described by + the font definition, \a fontDef, in the specified \a script. +*/ +QFontEngine *QPlatformFontDatabase::fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle) +{ + Q_UNUSED(script); + Q_UNUSED(handle); + QByteArray *fileDataPtr = static_cast<QByteArray *>(handle); + QFontEngineQPA *engine = new QFontEngineQPA(fontDef,*fileDataPtr); + //qDebug() << fontDef.pixelSize << fontDef.weight << fontDef.style << fontDef.stretch << fontDef.styleHint << fontDef.styleStrategy << fontDef.family << script; + return engine; +} + +QFontEngine *QPlatformFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, + QFont::HintingPreference hintingPreference) +{ + Q_UNUSED(fontData); + Q_UNUSED(pixelSize); + Q_UNUSED(hintingPreference); + qWarning("This plugin does not support font engines created directly from font data"); + return 0; +} + +/*! + Returns a list of alternative fonts for the specified \a family and + \a style and \a script using the \a styleHint given. +*/ +QStringList QPlatformFontDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const +{ + Q_UNUSED(family); + Q_UNUSED(style); + Q_UNUSED(styleHint); + Q_UNUSED(script); + return QStringList(); +} + +/*! + Adds an application font described by the font contained supplied \a fontData + or using the font contained in the file referenced by \a fileName. Returns + a list of family names, or an empty list if the font could not be added. + + \note The default implementation of this function does not add an application + font. Subclasses should reimplement this function to perform the necessary + loading and registration of fonts. +*/ +QStringList QPlatformFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) +{ + Q_UNUSED(fontData); + Q_UNUSED(fileName); + + qWarning("This plugin does not support application fonts"); + return QStringList(); +} + +/*! + Releases the font handle and deletes any associated data loaded from a file. +*/ +void QPlatformFontDatabase::releaseHandle(void *handle) +{ + QByteArray *fileDataPtr = static_cast<QByteArray *>(handle); + delete fileDataPtr; +} + +/*! + Returns the path to the font directory. + + The font directory is stored in the general Qt settings unless it has been + overridden by the \c QT_QPA_FONTDIR environment variable. + + When using builds of Qt that do not support settings, the \c QT_QPA_FONTDIR + environment variable is the only way to specify the font directory. +*/ +QString QPlatformFontDatabase::fontDir() const +{ + QString fontpath = QString::fromLocal8Bit(qgetenv("QT_QPA_FONTDIR")); + if (fontpath.isEmpty()) { +#ifndef QT_NO_SETTINGS + fontpath = QLibraryInfo::location(QLibraryInfo::LibrariesPath); + fontpath += QLatin1String("/fonts"); +#endif + } + + return fontpath; +} + +/*! + \class QPlatformFontDatabase + \brief The QPlatformFontDatabase class makes it possible to customize how fonts + are discovered and how they are rendered + \since 4.8 + + \ingroup painting + + QPlatformFontDatabase is the superclass which is intended to let platform implementations use + native font handling. + + Qt has its internal font database which it uses to discover available fonts on the + user's system. To be able to populate this database subclass this class, and + reimplement populateFontDatabase(). + + Use the function registerFont() to populate the internal font database. + + Sometimes a specified font does not have the required glyphs; in such a case, the + fallbackForFamily() function is called automatically to find alternative font + families that can supply alternatives to the missing glyphs. + + \sa QSupportedWritingSystems +*/ +QT_END_NAMESPACE diff --git a/src/gui/text/qplatformfontdatabase_qpa.h b/src/gui/text/qplatformfontdatabase_qpa.h new file mode 100644 index 0000000..1fb3c32 --- /dev/null +++ b/src/gui/text/qplatformfontdatabase_qpa.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLATFORMFONTDATABASE_QPA_H +#define QPLATFORMFONTDATABASE_QPA_H + +#include <QtCore/qconfig.h> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QList> +#include <QtGui/QFontDatabase> +#include <QtGui/private/qfont_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QWritingSystemsPrivate; + +class Q_GUI_EXPORT QSupportedWritingSystems +{ +public: + + QSupportedWritingSystems(); + QSupportedWritingSystems(const QSupportedWritingSystems &other); + QSupportedWritingSystems &operator=(const QSupportedWritingSystems &other); + ~QSupportedWritingSystems(); + + void setSupported(QFontDatabase::WritingSystem, bool supported = true); + bool supported(QFontDatabase::WritingSystem) const; + +private: + void detach(); + + QWritingSystemsPrivate *d; + + friend Q_GUI_EXPORT bool operator==(const QSupportedWritingSystems &, const QSupportedWritingSystems &); + friend Q_GUI_EXPORT bool operator!=(const QSupportedWritingSystems &, const QSupportedWritingSystems &); +}; + +Q_GUI_EXPORT bool operator==(const QSupportedWritingSystems &, const QSupportedWritingSystems &); +Q_GUI_EXPORT bool operator!=(const QSupportedWritingSystems &, const QSupportedWritingSystems &); + +class QFontRequestPrivate; + +class Q_GUI_EXPORT QPlatformFontDatabase +{ +public: + virtual void populateFontDatabase(); + virtual QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle); + virtual QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const; + virtual QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName); + virtual void releaseHandle(void *handle); + + virtual QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference); + + virtual QString fontDir() const; + + //callback + static void registerQPF2Font(const QByteArray &dataArray, void *handle); + static void registerFont(const QString &familyname, const QString &foundryname, QFont::Weight weight, + QFont::Style style, QFont::Stretch stretch, bool antialiased, bool scalable, int pixelSize, + const QSupportedWritingSystems &writingSystems, void *handle); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPLATFORMFONTDATABASE_QPA_H diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp new file mode 100644 index 0000000..61bc63e --- /dev/null +++ b/src/gui/text/qrawfont.cpp @@ -0,0 +1,706 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglobal.h" + +#if !defined(QT_NO_RAWFONT) + +#include "qrawfont.h" +#include "qrawfont_p.h" + +#include <QtCore/qendian.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QRawFont + \brief The QRawFont class provides access to a single physical instance of a font. + \since 4.8 + + \ingroup text + \mainclass + + \note QRawFont is a low level class. For most purposes QFont is a more appropriate class. + + Most commonly, when presenting text in a user interface, the exact fonts used + to render the characters is to some extent unknown. This can be the case for several + reasons: For instance, the actual, physical fonts present on the target system could be + unexpected to the developers, or the text could contain user selected styles, sizes or + writing systems that are not supported by font chosen in the code. + + Therefore, Qt's QFont class really represents a query for fonts. When text is interpreted, + Qt will do its best to match the text to the query, but depending on the support, different + fonts can be used behind the scenes. + + For most use cases, this is both expected and necessary, as it minimizes the possibility of + text in the user interface being undisplayable. In some cases, however, more direct control + over the process might be useful. It is for these use cases the QRawFont class exists. + + A QRawFont object represents a single, physical instance of a given font in a given pixel size. + I.e. in the typical case it represents a set of TrueType or OpenType font tables and uses a + user specified pixel size to convert metrics into logical pixel units. It can be used in + combination with the QGlyphRun class to draw specific glyph indexes at specific positions, and + also have accessors to some relevant data in the physical font. + + QRawFont only provides support for the main font technologies: GDI and DirectWrite on Windows + platforms, FreeType on Symbian and Linux platforms and CoreText on Mac OS X. For other + font back-ends, the APIs will be disabled. + + QRawFont can be constructed in a number of ways: + \list + \o It can be constructed by calling QTextLayout::glyphs() or QTextFragment::glyphs(). The + returned QGlyphs objects will contain QRawFont objects which represent the actual fonts + used to render each portion of the text. + \o It can be constructed by passing a QFont object to QRawFont::fromFont(). The function + will return a QRawFont object representing the font that will be selected as response to + the QFont query and the selected writing system. + \o It can be constructed by passing a file name or QByteArray directly to the QRawFont + constructor, or by calling loadFromFile() or loadFromData(). In this case, the + font will not be registered in QFontDatabase, and it will not be available as part of + regular font selection. + \endlist + + QRawFont is considered local to the thread in which it is constructed (either using a + constructor, or by calling loadFromData() or loadFromFile()). The QRawFont cannot be moved to a + different thread, but will have to be recreated in the thread in question. + + \note For the requirement of caching glyph indexes and font selections for static text to avoid + reshaping and relayouting in the inner loop of an application, a better choice is the QStaticText + class, since it optimizes the memory cost of the cache and also provides the possibility of paint + engine specific caches for an additional speed-up. +*/ + +/*! + \enum QRawFont::AntialiasingType + + This enum represents the different ways a glyph can be rasterized in the function + alphaMapForGlyph(). + + \value PixelAntialiasing Will rasterize by measuring the coverage of the shape on whole pixels. + The returned image contains the alpha values of each pixel based on the coverage of + the glyph shape. + \value SubPixelAntialiasing Will rasterize by measuring the coverage of each subpixel, + returning a separate alpha value for each of the red, green and blue components of + each pixel. +*/ + +/*! + Constructs an invalid QRawFont. +*/ +QRawFont::QRawFont() + : d(new QRawFontPrivate) +{ +} + +/*! + Constructs a QRawFont representing the font contained in the file referenced + by \a fileName for the size (in pixels) given by \a pixelSize, and using the + hinting preference specified by \a hintingPreference. + + \note The referenced file must contain a TrueType or OpenType font. +*/ +QRawFont::QRawFont(const QString &fileName, + qreal pixelSize, + QFont::HintingPreference hintingPreference) + : d(new QRawFontPrivate) +{ + loadFromFile(fileName, pixelSize, hintingPreference); +} + +/*! + Constructs a QRawFont representing the font contained in the supplied + \a fontData for the size (in pixels) given by \a pixelSize, and using the + hinting preference specified by \a hintingPreference. + + \note The data must contain a TrueType or OpenType font. +*/ +QRawFont::QRawFont(const QByteArray &fontData, + qreal pixelSize, + QFont::HintingPreference hintingPreference) + : d(new QRawFontPrivate) +{ + loadFromData(fontData, pixelSize, hintingPreference); +} + +/*! + Creates a QRawFont which is a copy of \a other. +*/ +QRawFont::QRawFont(const QRawFont &other) +{ + d = other.d; +} + +/*! + Destroys the QRawFont +*/ +QRawFont::~QRawFont() +{ +} + +/*! + Assigns \a other to this QRawFont. +*/ +QRawFont &QRawFont::operator=(const QRawFont &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if the QRawFont is valid and false otherwise. +*/ +bool QRawFont::isValid() const +{ + return d->isValid(); +} + +/*! + Replaces the current QRawFont with the contents of the file referenced + by \a fileName for the size (in pixels) given by \a pixelSize, and using the + hinting preference specified by \a hintingPreference. + + The file must reference a TrueType or OpenType font. + + \sa loadFromData() +*/ +void QRawFont::loadFromFile(const QString &fileName, + qreal pixelSize, + QFont::HintingPreference hintingPreference) +{ + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) + loadFromData(file.readAll(), pixelSize, hintingPreference); +} + +/*! + Replaces the current QRawFont with the font contained in the supplied + \a fontData for the size (in pixels) given by \a pixelSize, and using the + hinting preference specified by \a hintingPreference. + + The \a fontData must contain a TrueType or OpenType font. + + \sa loadFromFile() +*/ +void QRawFont::loadFromData(const QByteArray &fontData, + qreal pixelSize, + QFont::HintingPreference hintingPreference) +{ + d.detach(); + d->cleanUp(); + d->hintingPreference = hintingPreference; + d->thread = QThread::currentThread(); + d->platformLoadFromData(fontData, pixelSize, hintingPreference); +} + +/*! + This function returns a rasterized image of the glyph at the given + \a glyphIndex in the underlying font, using the \a transform specified. + If the QRawFont is not valid, this function will return an invalid QImage. + + If \a antialiasingType is set to QRawFont::SubPixelAntialiasing, then the resulting image will be + in QImage::Format_RGB32 and the RGB values of each pixel will represent the subpixel opacities of + the pixel in the rasterization of the glyph. Otherwise, the image will be in the format of + QImage::Format_A8 and each pixel will contain the opacity of the pixel in the rasterization. + + \sa pathForGlyph(), QPainter::drawGlyphRun() +*/ +QImage QRawFont::alphaMapForGlyph(quint32 glyphIndex, AntialiasingType antialiasingType, + const QTransform &transform) const +{ + if (!d->isValid()) + return QImage(); + + if (antialiasingType == SubPixelAntialiasing) + return d->fontEngine->alphaRGBMapForGlyph(glyphIndex, QFixed(), 0, transform); + + return d->fontEngine->alphaMapForGlyph(glyphIndex, QFixed(), transform); +} + +/*! + This function returns the shape of the glyph at a given \a glyphIndex in the underlying font + if the QRawFont is valid. Otherwise, it returns an empty QPainterPath. + + The returned glyph will always be unhinted. + + \sa alphaMapForGlyph(), QPainterPath::addText() +*/ +QPainterPath QRawFont::pathForGlyph(quint32 glyphIndex) const +{ + if (!d->isValid()) + return QPainterPath(); + + QFixedPoint position; + QPainterPath path; + d->fontEngine->addGlyphsToPath(&glyphIndex, &position, 1, &path, 0); + return path; +} + +/*! + Returns true if this QRawFont is equal to \a other. Otherwise, returns false. +*/ +bool QRawFont::operator==(const QRawFont &other) const +{ + return d->fontEngine == other.d->fontEngine; +} + +/*! + \fn bool QRawFont::operator!=(const QRawFont &other) const + + Returns true if this QRawFont is not equal to \a other. Otherwise, returns false. +*/ + +/*! + Returns the ascent of this QRawFont in pixel units. + + \sa QFontMetricsF::ascent() +*/ +qreal QRawFont::ascent() const +{ + return d->isValid() ? d->fontEngine->ascent().toReal() : 0.0; +} + +/*! + Returns the descent of this QRawFont in pixel units. + + \sa QFontMetricsF::descent() +*/ +qreal QRawFont::descent() const +{ + return d->isValid() ? d->fontEngine->descent().toReal() : 0.0; +} + +/*! + Returns the xHeight of this QRawFont in pixel units. + + \sa QFontMetricsF::xHeight() +*/ +qreal QRawFont::xHeight() const +{ + return d->isValid() ? d->fontEngine->xHeight().toReal() : 0.0; +} + +/*! + Returns the leading of this QRawFont in pixel units. + + \sa QFontMetricsF::leading() +*/ +qreal QRawFont::leading() const +{ + return d->isValid() ? d->fontEngine->leading().toReal() : 0.0; +} + +/*! + Returns the average character width of this QRawFont in pixel units. + + \sa QFontMetricsF::averageCharWidth() +*/ +qreal QRawFont::averageCharWidth() const +{ + return d->isValid() ? d->fontEngine->averageCharWidth().toReal() : 0.0; +} + +/*! + Returns the width of the widest character in the font. + + \sa QFontMetricsF::maxWidth() +*/ +qreal QRawFont::maxCharWidth() const +{ + return d->isValid() ? d->fontEngine->maxCharWidth() : 0.0; +} + +/*! + Returns the pixel size set for this QRawFont. The pixel size affects how glyphs are + rasterized, the size of glyphs returned by pathForGlyph(), and is used to convert + internal metrics from design units to logical pixel units. + + \sa setPixelSize() +*/ +qreal QRawFont::pixelSize() const +{ + return d->isValid() ? d->fontEngine->fontDef.pixelSize : 0.0; +} + +/*! + Returns the number of design units define the width and height of the em square + for this QRawFont. This value is used together with the pixel size when converting design metrics + to pixel units, as the internal metrics are specified in design units and the pixel size gives + the size of 1 em in pixels. + + \sa pixelSize(), setPixelSize() +*/ +qreal QRawFont::unitsPerEm() const +{ + return d->isValid() ? d->fontEngine->emSquareSize().toReal() : 0.0; +} + +/*! + Returns the family name of this QRawFont. +*/ +QString QRawFont::familyName() const +{ + return d->isValid() ? d->fontEngine->fontDef.family : QString(); +} + +/*! + Returns the style name of this QRawFont. + + \sa QFont::styleName() +*/ +QString QRawFont::styleName() const +{ + return d->isValid() ? d->fontEngine->fontDef.styleName : QString(); +} + +/*! + Returns the style of this QRawFont. + + \sa QFont::style() +*/ +QFont::Style QRawFont::style() const +{ + return d->isValid() ? QFont::Style(d->fontEngine->fontDef.style) : QFont::StyleNormal; +} + +/*! + Returns the weight of this QRawFont. + + \sa QFont::weight() +*/ +int QRawFont::weight() const +{ + return d->isValid() ? int(d->fontEngine->fontDef.weight) : -1; +} + +/*! + Converts the string of unicode points given by \a text to glyph indexes + using the CMAP table in the underlying font, and returns a vector containing + the result. + + Note that, in cases where there are other tables in the font that affect the + shaping of the text, the returned glyph indexes will not correctly represent + the rendering of the text. To get the correctly shaped text, you can use + QTextLayout to lay out and shape the text, then call QTextLayout::glyphs() + to get the set of glyph index list and QRawFont pairs. + + \sa advancesForGlyphIndexes(), glyphIndexesForChars(), QGlyphRun, QTextLayout::glyphRuns(), QTextFragment::glyphRuns() +*/ +QVector<quint32> QRawFont::glyphIndexesForString(const QString &text) const +{ + if (!d->isValid()) + return QVector<quint32>(); + + int nglyphs = text.size(); + QVarLengthGlyphLayoutArray glyphs(nglyphs); + if (!glyphIndexesForChars(text.data(), text.size(), glyphs.glyphs, &nglyphs)) { + glyphs.resize(nglyphs); + if (!glyphIndexesForChars(text.data(), text.size(), glyphs.glyphs, &nglyphs)) { + Q_ASSERT_X(false, Q_FUNC_INFO, "stringToCMap shouldn't fail twice"); + return QVector<quint32>(); + } + } + + QVector<quint32> glyphIndexes; + for (int i=0; i<nglyphs; ++i) + glyphIndexes.append(glyphs.glyphs[i]); + + return glyphIndexes; +} + +/*! + Converts a string of unicode points to glyph indexes using the CMAP table in the + underlying font. The function works like glyphIndexesForString() except it take + an array (\a chars), the results will be returned though \a glyphIndexes array + and number of glyphs will be set in \a numGlyphs. The size of \a glyphIndexes array + must be at least \a numChars, if that's still not enough, this function will return + false, then you can resize \a glyphIndexes from the size returned in \a numGlyphs. + + \sa glyphIndexesForString(), advancesForGlyphIndexes(), QGlyphRun, + QTextLayout::glyphRuns(), QTextFragment::glyphRuns() +*/ +bool QRawFont::glyphIndexesForChars(const QChar *chars, int numChars, quint32 *glyphIndexes, int *numGlyphs) const +{ + if (!d->isValid()) + return false; + + QGlyphLayout glyphs; + glyphs.glyphs = glyphIndexes; + return d->fontEngine->stringToCMap(chars, numChars, &glyphs, numGlyphs, QTextEngine::GlyphIndicesOnly); +} + +/*! + Returns the QRawFont's advances for each of the \a glyphIndexes in pixel units. The advances + give the distance from the position of a given glyph to where the next glyph should be drawn + to make it appear as if the two glyphs are unspaced. + + \sa QTextLine::horizontalAdvance(), QFontMetricsF::width() +*/ +QVector<QPointF> QRawFont::advancesForGlyphIndexes(const QVector<quint32> &glyphIndexes) const +{ + if (!d->isValid()) + return QVector<QPointF>(); + + int numGlyphs = glyphIndexes.size(); + QVarLengthGlyphLayoutArray glyphs(numGlyphs); + qMemCopy(glyphs.glyphs, glyphIndexes.data(), numGlyphs * sizeof(quint32)); + + d->fontEngine->recalcAdvances(&glyphs, 0); + + QVector<QPointF> advances; + for (int i=0; i<numGlyphs; ++i) + advances.append(QPointF(glyphs.advances_x[i].toReal(), glyphs.advances_y[i].toReal())); + + return advances; +} + +/*! + Returns the QRawFont's advances for each of the \a glyphIndexes in pixel units. The advances + give the distance from the position of a given glyph to where the next glyph should be drawn + to make it appear as if the two glyphs are unspaced. The glyph indexes are given with the + array \a glyphIndexes while the results are returned through \a advances, both of them must + have \a numGlyphs elements. + + \sa QTextLine::horizontalAdvance(), QFontMetricsF::width() +*/ +bool QRawFont::advancesForGlyphIndexes(const quint32 *glyphIndexes, QPointF *advances, int numGlyphs) const +{ + if (!d->isValid()) + return false; + + QGlyphLayout glyphs; + glyphs.glyphs = const_cast<HB_Glyph *>(glyphIndexes); + glyphs.numGlyphs = numGlyphs; + QVarLengthArray<QFixed> advances_x(numGlyphs); + QVarLengthArray<QFixed> advances_y(numGlyphs); + glyphs.advances_x = advances_x.data(); + glyphs.advances_y = advances_y.data(); + + d->fontEngine->recalcAdvances(&glyphs, 0); + + for (int i=0; i<numGlyphs; ++i) + advances[i] = QPointF(glyphs.advances_x[i].toReal(), glyphs.advances_y[i].toReal()); + + return true; +} + +/*! + Returns the hinting preference used to construct this QRawFont. + + \sa QFont::hintingPreference() +*/ +QFont::HintingPreference QRawFont::hintingPreference() const +{ + return d->isValid() ? d->hintingPreference : QFont::PreferDefaultHinting; +} + +/*! + Retrieves the sfnt table named \a tagName from the underlying physical font, or an empty + byte array if no such table was found. The returned font table's byte order is Big Endian, like + the sfnt format specifies. The \a tagName must be four characters long and should be formatted + in the default endianness of the current platform. +*/ +QByteArray QRawFont::fontTable(const char *tagName) const +{ + if (!d->isValid()) + return QByteArray(); + + const quint32 *tagId = reinterpret_cast<const quint32 *>(tagName); + return d->fontEngine->getSfntTable(qToBigEndian(*tagId)); +} + +// From qfontdatabase.cpp +extern QList<QFontDatabase::WritingSystem> qt_determine_writing_systems_from_truetype_bits(quint32 unicodeRange[4], quint32 codePageRange[2]); + +/*! + Returns a list of writing systems supported by the font according to designer supplied + information in the font file. Please note that this does not guarantee support for a + specific unicode point in the font. You can use the supportsCharacter() to check support + for a single, specific character. + + \note The list is determined based on the unicode ranges and codepage ranges set in the font's + OS/2 table and requires such a table to be present in the underlying font file. + + \sa supportsCharacter() +*/ +QList<QFontDatabase::WritingSystem> QRawFont::supportedWritingSystems() const +{ + if (d->isValid()) { + QByteArray os2Table = fontTable("OS/2"); + if (os2Table.size() > 86) { + char *data = os2Table.data(); + quint32 *bigEndianUnicodeRanges = reinterpret_cast<quint32 *>(data + 42); + quint32 *bigEndianCodepageRanges = reinterpret_cast<quint32 *>(data + 78); + + quint32 unicodeRanges[4]; + quint32 codepageRanges[2]; + + for (int i=0; i<4; ++i) { + if (i < 2) + codepageRanges[i] = qFromBigEndian(bigEndianCodepageRanges[i]); + unicodeRanges[i] = qFromBigEndian(bigEndianUnicodeRanges[i]); + } + + return qt_determine_writing_systems_from_truetype_bits(unicodeRanges, codepageRanges); + } + } + + return QList<QFontDatabase::WritingSystem>(); +} + +/*! + Returns true if the font has a glyph that corresponds to the given \a character. + + \sa supportedWritingSystems() +*/ +bool QRawFont::supportsCharacter(QChar character) const +{ + return d->isValid() && d->fontEngine->canRender(&character, 1); +} + +/*! + \overload + Returns true if the font has a glyph that corresponds to the UCS-4 encoded character \a ucs4. + + \sa supportedWritingSystems() +*/ +bool QRawFont::supportsCharacter(quint32 ucs4) const +{ + QChar str[2]; + int len; + if (!QChar::requiresSurrogates(ucs4)) { + str[0] = QChar(ucs4); + len = 1; + } else { + str[0] = QChar(QChar::highSurrogate(ucs4)); + str[1] = QChar(QChar::lowSurrogate(ucs4)); + len = 2; + } + + return d->isValid() && d->fontEngine->canRender(str, len); +} + +// qfontdatabase.cpp +extern int qt_script_for_writing_system(QFontDatabase::WritingSystem writingSystem); + +/*! + Fetches the physical representation based on a \a font query. The physical font returned is + the font that will be preferred by Qt in order to display text in the selected \a writingSystem. +*/ +QRawFont QRawFont::fromFont(const QFont &font, QFontDatabase::WritingSystem writingSystem) +{ + QRawFont rawFont; +#if defined(Q_WS_MAC) + QTextLayout layout(QFontDatabase::writingSystemSample(writingSystem), font); + layout.beginLayout(); + QTextLine line = layout.createLine(); + layout.endLayout(); + QList<QGlyphRun> list = layout.glyphRuns(); + if (list.size()) { + // Pick the one matches the family name we originally requested, + // if none of them match, just pick the first one + for (int i = 0; i < list.size(); i++) { + rawFont = list.at(i).rawFont(); + if (rawFont.familyName() == font.family()) + return rawFont; + } + return list.at(0).rawFont(); + } +#else + QFontPrivate *font_d = QFontPrivate::get(font); + int script = qt_script_for_writing_system(writingSystem); + QFontEngine *fe = font_d->engineForScript(script); + + if (fe != 0 && fe->type() == QFontEngine::Multi) { + QFontEngineMulti *multiEngine = static_cast<QFontEngineMulti *>(fe); + fe = multiEngine->engine(0); + if (fe == 0) { + multiEngine->loadEngine(0); + fe = multiEngine->engine(0); + } + } + + if (fe != 0) { + rawFont.d.data()->fontEngine = fe; + rawFont.d.data()->fontEngine->ref.ref(); + rawFont.d.data()->hintingPreference = font.hintingPreference(); + } +#endif + return rawFont; +} + +/*! + Sets the pixel size with which this font should be rendered to \a pixelSize. +*/ +void QRawFont::setPixelSize(qreal pixelSize) +{ + if (d->fontEngine == 0) + return; + + d.detach(); + QFontEngine *oldFontEngine = d->fontEngine; + + d->fontEngine = d->fontEngine->cloneWithSize(pixelSize); + if (d->fontEngine != 0) + d->fontEngine->ref.ref(); + + oldFontEngine->ref.deref(); + if (oldFontEngine->cache_count == 0 && oldFontEngine->ref == 0) + delete oldFontEngine; +} + +/*! + \internal +*/ +void QRawFontPrivate::cleanUp() +{ + platformCleanUp(); + if (fontEngine != 0) { + fontEngine->ref.deref(); + if (fontEngine->cache_count == 0 && fontEngine->ref == 0) + delete fontEngine; + fontEngine = 0; + } + hintingPreference = QFont::PreferDefaultHinting; +} + +#endif // QT_NO_RAWFONT + +QT_END_NAMESPACE diff --git a/src/gui/text/qrawfont.h b/src/gui/text/qrawfont.h new file mode 100644 index 0000000..cf77996 --- /dev/null +++ b/src/gui/text/qrawfont.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRAWFONT_H +#define QRAWFONT_H + +#include <QtCore/qstring.h> +#include <QtCore/qiodevice.h> +#include <QtCore/qglobal.h> +#include <QtCore/qobject.h> +#include <QtCore/qpoint.h> +#include <QtGui/qfont.h> +#include <QtGui/qtransform.h> +#include <QtGui/qfontdatabase.h> + +#if !defined(QT_NO_RAWFONT) + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QRawFontPrivate; +class Q_GUI_EXPORT QRawFont +{ +public: + enum AntialiasingType { + PixelAntialiasing, + SubPixelAntialiasing + }; + + QRawFont(); + QRawFont(const QString &fileName, + qreal pixelSize, + QFont::HintingPreference hintingPreference = QFont::PreferDefaultHinting); + QRawFont(const QByteArray &fontData, + qreal pixelSize, + QFont::HintingPreference hintingPreference = QFont::PreferDefaultHinting); + QRawFont(const QRawFont &other); + ~QRawFont(); + + bool isValid() const; + + QRawFont &operator=(const QRawFont &other); + + bool operator==(const QRawFont &other) const; + inline bool operator!=(const QRawFont &other) const + { return !operator==(other); } + + QString familyName() const; + QString styleName() const; + + QFont::Style style() const; + int weight() const; + + QVector<quint32> glyphIndexesForString(const QString &text) const; + QVector<QPointF> advancesForGlyphIndexes(const QVector<quint32> &glyphIndexes) const; + bool glyphIndexesForChars(const QChar *chars, int numChars, quint32 *glyphIndexes, int *numGlyphs) const; + bool advancesForGlyphIndexes(const quint32 *glyphIndexes, QPointF *advances, int numGlyphs) const; + + QImage alphaMapForGlyph(quint32 glyphIndex, + AntialiasingType antialiasingType = SubPixelAntialiasing, + const QTransform &transform = QTransform()) const; + QPainterPath pathForGlyph(quint32 glyphIndex) const; + + void setPixelSize(qreal pixelSize); + qreal pixelSize() const; + + QFont::HintingPreference hintingPreference() const; + + qreal ascent() const; + qreal descent() const; + qreal leading() const; + qreal xHeight() const; + qreal averageCharWidth() const; + qreal maxCharWidth() const; + + qreal unitsPerEm() const; + + void loadFromFile(const QString &fileName, + qreal pixelSize, + QFont::HintingPreference hintingPreference); + + void loadFromData(const QByteArray &fontData, + qreal pixelSize, + QFont::HintingPreference hintingPreference); + + bool supportsCharacter(quint32 ucs4) const; + bool supportsCharacter(QChar character) const; + QList<QFontDatabase::WritingSystem> supportedWritingSystems() const; + + QByteArray fontTable(const char *tagName) const; + + static QRawFont fromFont(const QFont &font, + QFontDatabase::WritingSystem writingSystem = QFontDatabase::Any); + +private: + friend class QRawFontPrivate; + QExplicitlySharedDataPointer<QRawFontPrivate> d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_RAWFONT + +#endif // QRAWFONT_H diff --git a/src/gui/text/qrawfont_ft.cpp b/src/gui/text/qrawfont_ft.cpp new file mode 100644 index 0000000..1666df3 --- /dev/null +++ b/src/gui/text/qrawfont_ft.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qglobal.h> + +#if !defined(QT_NO_RAWFONT) + +#include "qrawfont_p.h" +#include "qfontengine_ft_p.h" +#include "quuid.h" + +#if defined(Q_WS_X11) && !defined(QT_NO_FONTCONFIG) +# include "qfontengine_x11_p.h" +#endif + +QT_BEGIN_NAMESPACE + +class QFontEngineFTRawFont + +#if defined(Q_WS_X11) && !defined(QT_NO_FONTCONFIG) + : public QFontEngineX11FT +#else + : public QFontEngineFT +#endif + +{ +public: + QFontEngineFTRawFont(const QFontDef &fontDef) +#if defined(Q_WS_X11) && !defined(QT_NO_FONTCONFIG) + : QFontEngineX11FT(fontDef) +#else + : QFontEngineFT(fontDef) +#endif + { + } + + void updateFamilyNameAndStyle() + { + fontDef.family = QString::fromAscii(freetype->face->family_name); + + if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC) + fontDef.style = QFont::StyleItalic; + + if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD) + fontDef.weight = QFont::Bold; + } + + bool initFromData(const QByteArray &fontData) + { + FaceId faceId; + faceId.filename = ""; + faceId.index = 0; + faceId.uuid = QUuid::createUuid().toByteArray(); + + return init(faceId, true, Format_None, fontData); + } +}; + + +void QRawFontPrivate::platformCleanUp() +{ + // Font engine handles all resources +} + +void QRawFontPrivate::platformLoadFromData(const QByteArray &fontData, qreal pixelSize, + QFont::HintingPreference hintingPreference) +{ + Q_ASSERT(fontEngine == 0); + + QFontDef fontDef; + fontDef.pixelSize = pixelSize; + + QFontEngineFTRawFont *fe = new QFontEngineFTRawFont(fontDef); + if (!fe->initFromData(fontData)) { + delete fe; + return; + } + + fe->updateFamilyNameAndStyle(); + + switch (hintingPreference) { + case QFont::PreferNoHinting: + fe->setDefaultHintStyle(QFontEngineFT::HintNone); + break; + case QFont::PreferFullHinting: + fe->setDefaultHintStyle(QFontEngineFT::HintFull); + break; + case QFont::PreferVerticalHinting: + fe->setDefaultHintStyle(QFontEngineFT::HintLight); + break; + default: + // Leave it as it is + break; + } + + fontEngine = fe; + fontEngine->ref.ref(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_RAWFONT diff --git a/src/gui/text/qrawfont_mac.cpp b/src/gui/text/qrawfont_mac.cpp new file mode 100644 index 0000000..40c719a --- /dev/null +++ b/src/gui/text/qrawfont_mac.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qglobal.h> + +#if !defined(QT_NO_RAWFONT) + +#include "qrawfont_p.h" +#include "qfontengine_coretext_p.h" + +QT_BEGIN_NAMESPACE + +void QRawFontPrivate::platformCleanUp() +{ +} + +extern int qt_defaultDpi(); + +void QRawFontPrivate::platformLoadFromData(const QByteArray &fontData, + qreal pixelSize, + QFont::HintingPreference hintingPreference) +{ + // Mac OS X ignores it + Q_UNUSED(hintingPreference); + + QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(NULL, + fontData.constData(), fontData.size(), NULL); + + CGFontRef cgFont = CGFontCreateWithDataProvider(dataProvider); + + if (cgFont == NULL) { + qWarning("QRawFont::platformLoadFromData: CGFontCreateWithDataProvider failed"); + } else { + QFontDef def; + def.pixelSize = pixelSize; + def.pointSize = pixelSize * 72.0 / qt_defaultDpi(); + fontEngine = new QCoreTextFontEngine(cgFont, def); + CFRelease(cgFont); + fontEngine->ref.ref(); + } +} + +QT_END_NAMESPACE + +#endif // QT_NO_RAWFONT diff --git a/src/gui/text/qrawfont_p.h b/src/gui/text/qrawfont_p.h new file mode 100644 index 0000000..3557751 --- /dev/null +++ b/src/gui/text/qrawfont_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRAWFONTPRIVATE_P_H +#define QRAWFONTPRIVATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qrawfont.h" + +#include "qfontengine_p.h" +#include <QtCore/qthread.h> +#include <QtCore/qthreadstorage.h> + +#if !defined(QT_NO_RAWFONT) + +QT_BEGIN_NAMESPACE + +namespace { class CustomFontFileLoader; } +class Q_AUTOTEST_EXPORT QRawFontPrivate +{ +public: + QRawFontPrivate() + : fontEngine(0) + , hintingPreference(QFont::PreferDefaultHinting) + , thread(0) +#if defined(Q_WS_WIN) + , fontHandle(NULL) +#endif + {} + + QRawFontPrivate(const QRawFontPrivate &other) + : fontEngine(other.fontEngine) + , hintingPreference(other.hintingPreference) + , thread(other.thread) +#if defined(Q_WS_WIN) + , fontHandle(NULL) +#endif + { + if (fontEngine != 0) + fontEngine->ref.ref(); + } + + ~QRawFontPrivate() + { + Q_ASSERT(ref == 0); + cleanUp(); + } + + inline bool isValid() const + { + Q_ASSERT(thread == 0 || thread == QThread::currentThread()); + return fontEngine != 0; + } + + void cleanUp(); + void platformCleanUp(); + void platformLoadFromData(const QByteArray &fontData, + qreal pixelSize, + QFont::HintingPreference hintingPreference); + + static QRawFontPrivate *get(const QRawFont &font) { return font.d.data(); } + + QFontEngine *fontEngine; + QFont::HintingPreference hintingPreference; + QThread *thread; + QAtomicInt ref; + +#if defined(Q_WS_WIN) + HANDLE fontHandle; +#endif +}; + +QT_END_NAMESPACE + +#endif // QT_NO_RAWFONT + +#endif // QRAWFONTPRIVATE_P_H diff --git a/src/gui/text/qrawfont_qpa.cpp b/src/gui/text/qrawfont_qpa.cpp new file mode 100644 index 0000000..6a69804 --- /dev/null +++ b/src/gui/text/qrawfont_qpa.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qglobal.h> + +#if !defined(QT_NO_RAWFONT) + +#include "qrawfont_p.h" +#include <QtGui/qplatformfontdatabase_qpa.h> +#include <private/qapplication_p.h> + +QT_BEGIN_NAMESPACE + +void QRawFontPrivate::platformCleanUp() +{ +} + +void QRawFontPrivate::platformLoadFromData(const QByteArray &fontData, qreal pixelSize, + QFont::HintingPreference hintingPreference) +{ + Q_ASSERT(fontEngine == 0); + + QPlatformFontDatabase *pfdb = QApplicationPrivate::platformIntegration()->fontDatabase(); + fontEngine = pfdb->fontEngine(fontData, pixelSize, hintingPreference); + if (fontEngine != 0) + fontEngine->ref.ref(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_RAWFONT diff --git a/src/gui/text/qrawfont_win.cpp b/src/gui/text/qrawfont_win.cpp new file mode 100644 index 0000000..a729e31 --- /dev/null +++ b/src/gui/text/qrawfont_win.cpp @@ -0,0 +1,701 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrawfont_p.h" + +#if !defined(QT_NO_RAWFONT) + +#include <private/qsystemlibrary_p.h> + +#if !defined(QT_NO_DIRECTWRITE) +# include "qfontenginedirectwrite_p.h" +# include <dwrite.h> +#endif + +QT_BEGIN_NAMESPACE + +namespace { + + template<typename T> + struct BigEndian + { + quint8 data[sizeof(T)]; + + operator T() const + { + T littleEndian = 0; + for (int i = 0; i < int(sizeof(T)); ++i) + littleEndian |= data[i] << ((sizeof(T) - i - 1) * 8); + + return littleEndian; + } + + BigEndian<T> &operator=(const T &t) + { + for (int i = 0; i < int(sizeof(T)); ++i) + data[i] = ((t >> (sizeof(T) - i - 1) * 8) & 0xff); + + return *this; + } + }; + +# pragma pack(1) + + // Common structure for all formats of the "name" table + struct NameTable + { + BigEndian<quint16> format; + BigEndian<quint16> count; + BigEndian<quint16> stringOffset; + }; + + struct NameRecord + { + BigEndian<quint16> platformID; + BigEndian<quint16> encodingID; + BigEndian<quint16> languageID; + BigEndian<quint16> nameID; + BigEndian<quint16> length; + BigEndian<quint16> offset; + }; + + struct OffsetSubTable + { + BigEndian<quint32> scalerType; + BigEndian<quint16> numTables; + BigEndian<quint16> searchRange; + BigEndian<quint16> entrySelector; + BigEndian<quint16> rangeShift; + }; + + struct TableDirectory + { + BigEndian<quint32> identifier; + BigEndian<quint32> checkSum; + BigEndian<quint32> offset; + BigEndian<quint32> length; + }; + + struct OS2Table + { + BigEndian<quint16> version; + BigEndian<qint16> avgCharWidth; + BigEndian<quint16> weightClass; + BigEndian<quint16> widthClass; + BigEndian<quint16> type; + BigEndian<qint16> subscriptXSize; + BigEndian<qint16> subscriptYSize; + BigEndian<qint16> subscriptXOffset; + BigEndian<qint16> subscriptYOffset; + BigEndian<qint16> superscriptXSize; + BigEndian<qint16> superscriptYSize; + BigEndian<qint16> superscriptXOffset; + BigEndian<qint16> superscriptYOffset; + BigEndian<qint16> strikeOutSize; + BigEndian<qint16> strikeOutPosition; + BigEndian<qint16> familyClass; + quint8 panose[10]; + BigEndian<quint32> unicodeRanges[4]; + quint8 vendorID[4]; + BigEndian<quint16> selection; + BigEndian<quint16> firstCharIndex; + BigEndian<quint16> lastCharIndex; + BigEndian<qint16> typoAscender; + BigEndian<qint16> typoDescender; + BigEndian<qint16> typoLineGap; + BigEndian<quint16> winAscent; + BigEndian<quint16> winDescent; + BigEndian<quint32> codepageRanges[2]; + BigEndian<qint16> height; + BigEndian<qint16> capHeight; + BigEndian<quint16> defaultChar; + BigEndian<quint16> breakChar; + BigEndian<quint16> maxContext; + }; + +# pragma pack() + + class EmbeddedFont + { + public: + EmbeddedFont(const QByteArray &fontData) : m_fontData(fontData) {} + + QString changeFamilyName(const QString &newFamilyName); + QByteArray data() const { return m_fontData; } + TableDirectory *tableDirectoryEntry(const QByteArray &tagName); + QString familyName(TableDirectory *nameTableDirectory = 0); + + private: + QByteArray m_fontData; + }; + + TableDirectory *EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName) + { + Q_ASSERT(tagName.size() == 4); + + const BigEndian<quint32> *tagIdPtr = + reinterpret_cast<const BigEndian<quint32> *>(tagName.constData()); + quint32 tagId = *tagIdPtr; + + OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data()); + TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1); + + TableDirectory *nameTableDirectoryEntry = 0; + for (int i=0; i<offsetSubTable->numTables; ++i, ++tableDirectory) { + if (tableDirectory->identifier == tagId) { + nameTableDirectoryEntry = tableDirectory; + break; + } + } + + return nameTableDirectoryEntry; + } + + QString EmbeddedFont::familyName(TableDirectory *nameTableDirectoryEntry) + { + QString name; + + if (nameTableDirectoryEntry == 0) + nameTableDirectoryEntry = tableDirectoryEntry("name"); + + if (nameTableDirectoryEntry != 0) { + NameTable *nameTable = reinterpret_cast<NameTable *>(m_fontData.data() + + nameTableDirectoryEntry->offset); + NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); + for (int i=0; i<nameTable->count; ++i, ++nameRecord) { + if (nameRecord->nameID == 1 + && nameRecord->platformID == 3 // Windows + && nameRecord->languageID == 0x0409) { // US English + const void *ptr = reinterpret_cast<const quint8 *>(nameTable) + + nameTable->stringOffset + + nameRecord->offset; + + const BigEndian<quint16> *s = reinterpret_cast<const BigEndian<quint16> *>(ptr); + const BigEndian<quint16> *e = s + nameRecord->length / sizeof(quint16); + while (s != e) + name += QChar(*s++); + break; + } + } + } + + return name; + } + + QString EmbeddedFont::changeFamilyName(const QString &newFamilyName) + { + TableDirectory *nameTableDirectoryEntry = tableDirectoryEntry("name"); + if (nameTableDirectoryEntry == 0) + return QString(); + + QString oldFamilyName = familyName(nameTableDirectoryEntry); + + // Reserve size for name table header, five required name records and string + const int requiredRecordCount = 5; + quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 }; + + int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount; + int newFamilyNameSize = newFamilyName.size() * sizeof(quint16); + + const QString regularString = QString::fromLatin1("Regular"); + int regularStringSize = regularString.size() * sizeof(quint16); + + // Align table size of table to 32 bits (pad with 0) + int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4; + + QByteArray newNameTable(fullSize, char(0)); + + { + NameTable *nameTable = reinterpret_cast<NameTable *>(newNameTable.data()); + nameTable->count = requiredRecordCount; + nameTable->stringOffset = sizeOfHeader; + + NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); + for (int i=0; i<requiredRecordCount; ++i, nameRecord++) { + nameRecord->nameID = nameIds[i]; + nameRecord->encodingID = 1; + nameRecord->languageID = 0x0409; + nameRecord->platformID = 3; + nameRecord->length = newFamilyNameSize; + + // Special case for sub-family + if (nameIds[i] == 4) { + nameRecord->offset = newFamilyNameSize; + nameRecord->length = regularStringSize; + } + } + + // nameRecord now points to string data + BigEndian<quint16> *stringStorage = reinterpret_cast<BigEndian<quint16> *>(nameRecord); + const quint16 *sourceString = newFamilyName.utf16(); + for (int i=0; i<newFamilyName.size(); ++i) + stringStorage[i] = sourceString[i]; + stringStorage += newFamilyName.size(); + + sourceString = regularString.utf16(); + for (int i=0; i<regularString.size(); ++i) + stringStorage[i] = sourceString[i]; + } + + quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data()); + quint32 *tableEnd = reinterpret_cast<quint32 *>(newNameTable.data() + fullSize); + + quint32 checkSum = 0; + while (p < tableEnd) + checkSum += *(p++); + + nameTableDirectoryEntry->checkSum = checkSum; + nameTableDirectoryEntry->offset = m_fontData.size(); + nameTableDirectoryEntry->length = fullSize; + + m_fontData.append(newNameTable); + + return oldFamilyName; + } + +#if !defined(QT_NO_DIRECTWRITE) + + class DirectWriteFontFileStream: public IDWriteFontFileStream + { + public: + DirectWriteFontFileStream(const QByteArray &fontData) + : m_fontData(fontData) + , m_referenceCount(0) + { + } + + ~DirectWriteFontFileStream() + { + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset, + UINT64 fragmentSize, OUT void **fragmentContext); + void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext); + HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize); + HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime); + + private: + QByteArray m_fontData; + ULONG m_referenceCount; + }; + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) { + *object = this; + AddRef(); + return S_OK; + } else { + *object = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef() + { + return InterlockedIncrement(&m_referenceCount); + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release() + { + ULONG newCount = InterlockedDecrement(&m_referenceCount); + if (newCount == 0) + delete this; + return newCount; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment( + const void **fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + OUT void **fragmentContext) + { + *fragmentContext = NULL; + if (fragmentSize + fileOffset <= m_fontData.size()) { + *fragmentStart = m_fontData.data() + fileOffset; + return S_OK; + } else { + *fragmentStart = NULL; + return E_FAIL; + } + } + + void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *) + { + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize) + { + *fileSize = m_fontData.size(); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime) + { + *lastWriteTime = 0; + return E_NOTIMPL; + } + + class DirectWriteFontFileLoader: public IDWriteFontFileLoader + { + public: + DirectWriteFontFileLoader() : m_referenceCount(0) {} + + ~DirectWriteFontFileLoader() + { + } + + inline void addKey(const void *key, const QByteArray &fontData) + { + Q_ASSERT(!m_fontDatas.contains(key)); + m_fontDatas.insert(key, fontData); + } + + inline void removeKey(const void *key) + { + m_fontDatas.remove(key); + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + OUT IDWriteFontFileStream **fontFileStream); + + private: + ULONG m_referenceCount; + QHash<const void *, QByteArray> m_fontDatas; + }; + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid, + void **object) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) { + *object = this; + AddRef(); + return S_OK; + } else { + *object = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef() + { + return InterlockedIncrement(&m_referenceCount); + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release() + { + ULONG newCount = InterlockedDecrement(&m_referenceCount); + if (newCount == 0) + delete this; + return newCount; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey( + void const *fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + IDWriteFontFileStream **fontFileStream) + { + Q_UNUSED(fontFileReferenceKeySize); + + if (fontFileReferenceKeySize != sizeof(const void *)) { + qWarning("DirectWriteFontFileLoader::CreateStreamFromKey: Wrong key size"); + return E_FAIL; + } + + const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey); + *fontFileStream = NULL; + if (!m_fontDatas.contains(key)) + return E_FAIL; + + QByteArray fontData = m_fontDatas.value(key); + DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData); + stream->AddRef(); + *fontFileStream = stream; + + return S_OK; + } + + class CustomFontFileLoader + { + public: + CustomFontFileLoader() : m_directWriteFactory(0), m_directWriteFontFileLoader(0) + { + HRESULT hres = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown **>(&m_directWriteFactory)); + if (FAILED(hres)) { + qErrnoWarning(hres, "CustomFontFileLoader::CustomFontFileLoader: " + "DWriteCreateFactory failed."); + } else { + m_directWriteFontFileLoader = new DirectWriteFontFileLoader(); + m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader); + } + } + + ~CustomFontFileLoader() + { + if (m_directWriteFactory != 0 && m_directWriteFontFileLoader != 0) + m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader); + + if (m_directWriteFactory != 0) + m_directWriteFactory->Release(); + } + + void addKey(const void *key, const QByteArray &fontData) + { + if (m_directWriteFontFileLoader != 0) + m_directWriteFontFileLoader->addKey(key, fontData); + } + + void removeKey(const void *key) + { + if (m_directWriteFontFileLoader != 0) + m_directWriteFontFileLoader->removeKey(key); + } + + IDWriteFontFileLoader *loader() const + { + return m_directWriteFontFileLoader; + } + + private: + IDWriteFactory *m_directWriteFactory; + DirectWriteFontFileLoader *m_directWriteFontFileLoader; + }; + +#endif + +} // Anonymous namespace + + +// From qfontdatabase_win.cpp +extern QFontEngine *qt_load_font_engine_win(const QFontDef &request); +// From qfontdatabase.cpp +extern QFont::Weight weightFromInteger(int weight); + +typedef HANDLE (WINAPI *PtrAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *); +static PtrAddFontMemResourceEx ptrAddFontMemResourceEx = 0; +typedef BOOL (WINAPI *PtrRemoveFontMemResourceEx)(HANDLE); +static PtrRemoveFontMemResourceEx ptrRemoveFontMemResourceEx = 0; + +static void resolveGdi32() +{ + static bool triedResolve = false; + if (!triedResolve) { + QSystemLibrary gdi32(QLatin1String("gdi32")); + if (gdi32.load()) { + ptrAddFontMemResourceEx = (PtrAddFontMemResourceEx)gdi32.resolve("AddFontMemResourceEx"); + ptrRemoveFontMemResourceEx = (PtrRemoveFontMemResourceEx)gdi32.resolve("RemoveFontMemResourceEx"); + } + + triedResolve = true; + } +} + +void QRawFontPrivate::platformCleanUp() +{ + if (fontHandle != NULL) { + if (ptrRemoveFontMemResourceEx) + ptrRemoveFontMemResourceEx(fontHandle); + fontHandle = NULL; + } +} + +void QRawFontPrivate::platformLoadFromData(const QByteArray &fontData, + qreal pixelSize, + QFont::HintingPreference hintingPreference) +{ + EmbeddedFont font(fontData); + +#if !defined(QT_NO_DIRECTWRITE) + if (hintingPreference == QFont::PreferDefaultHinting + || hintingPreference == QFont::PreferFullHinting) +#endif + { + GUID guid; + CoCreateGuid(&guid); + + QString uniqueFamilyName = QLatin1Char('f') + + QString::number(guid.Data1, 36) + QLatin1Char('-') + + QString::number(guid.Data2, 36) + QLatin1Char('-') + + QString::number(guid.Data3, 36) + QLatin1Char('-') + + QString::number(*reinterpret_cast<quint64 *>(guid.Data4), 36); + + QString actualFontName = font.changeFamilyName(uniqueFamilyName); + if (actualFontName.isEmpty()) { + qWarning("QRawFont::platformLoadFromData: Can't change family name of font"); + return; + } + + Q_ASSERT(fontHandle == NULL); + resolveGdi32(); + if (ptrAddFontMemResourceEx && ptrRemoveFontMemResourceEx) { + DWORD count = 0; + QByteArray newFontData = font.data(); + fontHandle = ptrAddFontMemResourceEx((void *)newFontData.constData(), newFontData.size(), + 0, &count); + if (count == 0 && fontHandle != NULL) { + ptrRemoveFontMemResourceEx(fontHandle); + fontHandle = NULL; + } + } + + if (fontHandle == NULL) { + qWarning("QRawFont::platformLoadFromData: AddFontMemResourceEx failed"); + } else { + QFontDef request; + request.family = uniqueFamilyName; + request.pixelSize = pixelSize; + request.styleStrategy = QFont::NoFontMerging | QFont::PreferMatch; + request.hintingPreference = hintingPreference; + + fontEngine = qt_load_font_engine_win(request); + if (request.family != fontEngine->fontDef.family) { + qWarning("QRawFont::platformLoadFromData: Failed to load font. " + "Got fallback instead: %s", qPrintable(fontEngine->fontDef.family)); + if (fontEngine->cache_count == 0 && fontEngine->ref == 0) + delete fontEngine; + fontEngine = 0; + } else { + Q_ASSERT(fontEngine->cache_count == 0 && fontEngine->ref == 0); + + // Override the generated font name + static_cast<QFontEngineWin *>(fontEngine)->uniqueFamilyName = uniqueFamilyName; + fontEngine->fontDef.family = actualFontName; + fontEngine->ref.ref(); + } + } + } +#if !defined(QT_NO_DIRECTWRITE) + else { + CustomFontFileLoader fontFileLoader; + fontFileLoader.addKey(this, fontData); + + IDWriteFactory *factory = NULL; + HRESULT hres = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown **>(&factory)); + if (FAILED(hres)) { + qErrnoWarning(hres, "QRawFont::platformLoadFromData: DWriteCreateFactory failed"); + return; + } + + IDWriteFontFile *fontFile = NULL; + void *key = this; + + hres = factory->CreateCustomFontFileReference(&key, sizeof(void *), + fontFileLoader.loader(), &fontFile); + if (FAILED(hres)) { + qErrnoWarning(hres, "QRawFont::platformLoadFromData: " + "CreateCustomFontFileReference failed"); + factory->Release(); + return; + } + + BOOL isSupportedFontType; + DWRITE_FONT_FILE_TYPE fontFileType; + DWRITE_FONT_FACE_TYPE fontFaceType; + UINT32 numberOfFaces; + fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces); + if (!isSupportedFontType) { + fontFile->Release(); + factory->Release(); + return; + } + + IDWriteFontFace *directWriteFontFace = NULL; + hres = factory->CreateFontFace(fontFaceType, 1, &fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE, + &directWriteFontFace); + if (FAILED(hres)) { + qErrnoWarning(hres, "QRawFont::platformLoadFromData: CreateFontFace failed"); + fontFile->Release(); + factory->Release(); + return; + } + + fontFile->Release(); + + fontEngine = new QFontEngineDirectWrite(factory, directWriteFontFace, pixelSize); + + // Get font family from font data + fontEngine->fontDef.family = font.familyName(); + fontEngine->ref.ref(); + + directWriteFontFace->Release(); + factory->Release(); + } +#endif + + // Get style and weight info + if (fontEngine != 0) { + TableDirectory *os2TableEntry = font.tableDirectoryEntry("OS/2"); + if (os2TableEntry != 0) { + const OS2Table *os2Table = + reinterpret_cast<const OS2Table *>(fontData.constData() + + os2TableEntry->offset); + + bool italic = os2Table->selection & 1; + bool oblique = os2Table->selection & 128; + + if (italic) + fontEngine->fontDef.style = QFont::StyleItalic; + else if (oblique) + fontEngine->fontDef.style = QFont::StyleOblique; + else + fontEngine->fontDef.style = QFont::StyleNormal; + + fontEngine->fontDef.weight = weightFromInteger(os2Table->weightClass); + } + } +} + +QT_END_NAMESPACE + +#endif // QT_NO_RAWFONT diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp index 73a871b..c985427 100644 --- a/src/gui/text/qstatictext.cpp +++ b/src/gui/text/qstatictext.cpp @@ -420,9 +420,6 @@ QStaticTextPrivate *QStaticTextPrivate::get(const QStaticText *q) return q->data.data(); } -Q_GUI_EXPORT extern int qt_defaultDpiX(); -Q_GUI_EXPORT extern int qt_defaultDpiY(); - namespace { class DrawTextItemRecorder: public QPaintEngine diff --git a/src/gui/text/qtextcontrol.cpp b/src/gui/text/qtextcontrol.cpp index f3d42f8..aeeef85 100644 --- a/src/gui/text/qtextcontrol.cpp +++ b/src/gui/text/qtextcontrol.cpp @@ -64,6 +64,7 @@ #include "qgraphicssceneevent.h" #include "qprinter.h" #include "qtextdocumentwriter.h" +#include "private/qtextcursor_p.h" #include <qtextformat.h> #include <qdatetime.h> @@ -1637,16 +1638,13 @@ void QTextControlPrivate::mouseMoveEvent(QEvent *e, Qt::MouseButton button, cons return; } - if (!mousePressed) - return; - const qreal mouseX = qreal(mousePos.x()); int newCursorPos = q->hitTest(mousePos, Qt::FuzzyHit); if (newCursorPos == -1) return; - if (wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) { + if (mousePressed && wordSelectionEnabled && !selectedWordOnDoubleClick.hasSelection()) { selectedWordOnDoubleClick = cursor; selectedWordOnDoubleClick.select(QTextCursor::WordUnderCursor); } @@ -1655,7 +1653,7 @@ void QTextControlPrivate::mouseMoveEvent(QEvent *e, Qt::MouseButton button, cons extendBlockwiseSelection(newCursorPos); else if (selectedWordOnDoubleClick.hasSelection()) extendWordwiseSelection(newCursorPos, mouseX); - else + else if (mousePressed) setCursorPosition(newCursorPos, QTextCursor::KeepAnchor); if (interactionFlags & Qt::TextEditable) { @@ -1983,6 +1981,8 @@ void QTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) } layout->setAdditionalFormats(overrides); cursor.endEditBlock(); + if (cursor.d) + cursor.d->setX(); if (oldPreeditCursor != preeditCursor) emit q->microFocusChanged(); } @@ -3000,7 +3000,7 @@ QAbstractTextDocumentLayout::PaintContext QTextControl::getPaintContext(QWidget if (widget) style = widget->style(); style->styleHint(QStyle::SH_TextControl_FocusIndicatorTextCharFormat, &opt, widget, &ret); - selection.format = qVariantValue<QTextFormat>(ret.variant).toCharFormat(); + selection.format = qvariant_cast<QTextFormat>(ret.variant).toCharFormat(); } else { QPalette::ColorGroup cg = d->hasFocus ? QPalette::Active : QPalette::Inactive; selection.format.setBackground(ctx.palette.brush(cg, QPalette::Highlight)); diff --git a/src/gui/text/qtextcursor.cpp b/src/gui/text/qtextcursor.cpp index bf1941c..acef9fa 100644 --- a/src/gui/text/qtextcursor.cpp +++ b/src/gui/text/qtextcursor.cpp @@ -362,20 +362,23 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor currentCharFormat = -1; bool adjustX = true; QTextBlock blockIt = block(); + bool visualMovement = priv->defaultCursorMoveStyle == Qt::VisualMoveStyle; if (!blockIt.isValid()) return false; - if (op >= QTextCursor::Left && op <= QTextCursor::WordRight - && blockIt.textDirection() == Qt::RightToLeft) { - if (op == QTextCursor::Left) - op = QTextCursor::NextCharacter; - else if (op == QTextCursor::Right) - op = QTextCursor::PreviousCharacter; - else if (op == QTextCursor::WordLeft) + if (blockIt.textDirection() == Qt::RightToLeft) { + if (op == QTextCursor::WordLeft) op = QTextCursor::NextWord; else if (op == QTextCursor::WordRight) op = QTextCursor::PreviousWord; + + if (!visualMovement) { + if (op == QTextCursor::Left) + op = QTextCursor::NextCharacter; + else if (op == QTextCursor::Right) + op = QTextCursor::PreviousCharacter; + } } const QTextLayout *layout = blockLayout(blockIt); @@ -418,9 +421,12 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor break; } case QTextCursor::PreviousCharacter: - case QTextCursor::Left: newPosition = priv->previousCursorPosition(position, QTextLayout::SkipCharacters); break; + case QTextCursor::Left: + newPosition = visualMovement ? priv->leftCursorPosition(position) + : priv->previousCursorPosition(position, QTextLayout::SkipCharacters); + break; case QTextCursor::StartOfWord: { if (relativePos == 0) break; @@ -529,9 +535,12 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor break; } case QTextCursor::NextCharacter: - case QTextCursor::Right: newPosition = priv->nextCursorPosition(position, QTextLayout::SkipCharacters); break; + case QTextCursor::Right: + newPosition = visualMovement ? priv->rightCursorPosition(position) + : priv->nextCursorPosition(position, QTextLayout::SkipCharacters); + break; case QTextCursor::NextWord: case QTextCursor::WordRight: newPosition = priv->nextCursorPosition(position, QTextLayout::SkipWords); @@ -1502,11 +1511,11 @@ void QTextCursor::deletePreviousChar() const QTextFragmentData * const frag = fragIt.value(); int fpos = fragIt.position(); QChar uc = d->priv->buffer().at(d->anchor - fpos + frag->stringPosition); - if (d->anchor > fpos && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) { + if (d->anchor > fpos && uc.isLowSurrogate()) { // second half of a surrogate, check if we have the first half as well, // if yes delete both at once uc = d->priv->buffer().at(d->anchor - 1 - fpos + frag->stringPosition); - if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) + if (uc.isHighSurrogate()) --d->anchor; } @@ -2558,4 +2567,20 @@ QTextDocument *QTextCursor::document() const return 0; // document went away } +/*! + \enum Qt::CursorMoveStyle + \since 4.8 + + This enum describes the movement style available to text cursors. The options + are: + + \value LogicalMoveStyle Within a left-to-right text block, decrease cursor + position when pressing left arrow key, increase cursor position when pressing + the right arrow key. If the text block is right-to-left, the opposite behavior + applies. + \value VisualMoveStyle Pressing the left arrow key will always cause the cursor + to move left, regardless of the text's writing direction. Pressing the right + arrow key will always cause the cursor to move right. +*/ + QT_END_NAMESPACE diff --git a/src/gui/text/qtextcursor.h b/src/gui/text/qtextcursor.h index b93fc29..697899b 100644 --- a/src/gui/text/qtextcursor.h +++ b/src/gui/text/qtextcursor.h @@ -230,6 +230,7 @@ private: QSharedDataPointer<QTextCursorPrivate> d; friend class QTextDocumentFragmentPrivate; friend class QTextCopyHelper; + friend class QTextControlPrivate; }; QT_END_NAMESPACE diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index dcf3cbc..143dc1a 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -60,6 +60,7 @@ #include <qdir.h> #include <qapplication.h> #include "qtextcontrol_p.h" +#include "qfont_p.h" #include "private/qtextedit_p.h" #include "private/qdataurl_p.h" @@ -585,6 +586,29 @@ void QTextDocument::setDefaultTextOption(const QTextOption &option) } /*! + \since 4.8 + + The default cursor movement style is used by all QTextCursor objects + created from the document. The default is Qt::LogicalMoveStyle. +*/ +Qt::CursorMoveStyle QTextDocument::defaultCursorMoveStyle() const +{ + Q_D(const QTextDocument); + return d->defaultCursorMoveStyle; +} + +/*! + \since 4.8 + + Sets the default cursor movement style to the given \a style. +*/ +void QTextDocument::setDefaultCursorMoveStyle(Qt::CursorMoveStyle style) +{ + Q_D(QTextDocument); + d->defaultCursorMoveStyle = style; +} + +/*! \fn void QTextDocument::markContentsDirty(int position, int length) Marks the contents specified by the given \a position and \a length @@ -1671,8 +1695,6 @@ static void printPage(int index, QPainter *painter, const QTextDocument *doc, co painter->restore(); } -Q_GUI_EXPORT extern int qt_defaultDpi(); - /*! Prints the document to the given \a printer. The QPrinter must be set up before being used with this function. @@ -1985,6 +2007,8 @@ QVariant QTextDocument::loadResource(int type, const QUrl &name) if (fi.exists()) { resourceUrl = QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(name); + } else if (currentURL.isEmpty()) { + resourceUrl.setScheme(QLatin1String("file")); } } @@ -2075,6 +2099,10 @@ QString QTextHtmlExporter::toHtml(const QByteArray &encoding, ExportMode mode) html += QLatin1String(" font-size:"); html += QString::number(defaultCharFormat.fontPointSize()); html += QLatin1String("pt;"); + } else if (defaultCharFormat.hasProperty(QTextFormat::FontPixelSize)) { + html += QLatin1String(" font-size:"); + html += QString::number(defaultCharFormat.intProperty(QTextFormat::FontPixelSize)); + html += QLatin1String("px;"); } html += QLatin1String(" font-weight:"); @@ -2155,6 +2183,10 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format) html += QLatin1Char(';'); attributesEmitted = true; } + } else if (format.hasProperty(QTextFormat::FontPixelSize)) { + html += QLatin1String(" font-size:"); + html += QString::number(format.intProperty(QTextFormat::FontPixelSize)); + html += QLatin1String("px;"); } if (format.hasProperty(QTextFormat::FontWeight) @@ -2602,14 +2634,40 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block) default: html += QLatin1String("<ul"); // ### should not happen } - html += QLatin1String(" style=\"margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;"); + QString styleString = QString::fromLatin1("margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;"); if (format.hasProperty(QTextFormat::ListIndent)) { - html += QLatin1String(" -qt-list-indent: "); - html += QString::number(format.indent()); - html += QLatin1Char(';'); + styleString += QLatin1String(" -qt-list-indent: "); + styleString += QString::number(format.indent()); + styleString += QLatin1Char(';'); } + if (format.hasProperty(QTextFormat::ListNumberPrefix)) { + QString numberPrefix = format.numberPrefix(); + numberPrefix.replace(QLatin1Char('"'), QLatin1String("\\22")); + numberPrefix.replace(QLatin1Char('\''), QLatin1String("\\27")); // FIXME: There's a problem in the CSS parser the prevents this from being correctly restored + styleString += QLatin1String(" -qt-list-number-prefix: "); + styleString += QLatin1Char('\''); + styleString += numberPrefix; + styleString += QLatin1Char('\''); + styleString += QLatin1Char(';'); + } + + if (format.hasProperty(QTextFormat::ListNumberSuffix)) { + if (format.numberSuffix() != QLatin1String(".")) { // this is our default + QString numberSuffix = format.numberSuffix(); + numberSuffix.replace(QLatin1Char('"'), QLatin1String("\\22")); + numberSuffix.replace(QLatin1Char('\''), QLatin1String("\\27")); // see above + styleString += QLatin1String(" -qt-list-number-suffix: "); + styleString += QLatin1Char('\''); + styleString += numberSuffix; + styleString += QLatin1Char('\''); + styleString += QLatin1Char(';'); + } + } + + html += QLatin1String(" style=\""); + html += styleString; html += QLatin1String("\">"); } @@ -2651,6 +2709,8 @@ void QTextHtmlExporter::emitBlock(const QTextBlock &block) emitBlockAttributes(block); html += QLatin1Char('>'); + if (block.begin().atEnd()) + html += QLatin1String("<br />"); QTextBlock::Iterator it = block.begin(); if (fragmentMarkers && !it.atEnd() && block == doc->begin()) diff --git a/src/gui/text/qtextdocument.h b/src/gui/text/qtextdocument.h index 4647a66..268b72e 100644 --- a/src/gui/text/qtextdocument.h +++ b/src/gui/text/qtextdocument.h @@ -60,7 +60,6 @@ class QPainter; class QPrinter; class QAbstractTextDocumentLayout; class QPoint; -class QTextCursor; class QTextObject; class QTextFormat; class QTextFrame; @@ -70,6 +69,7 @@ class QUrl; class QVariant; class QRectF; class QTextOption; +class QTextCursor; template<typename T> class QVector; @@ -269,6 +269,9 @@ public: QTextOption defaultTextOption() const; void setDefaultTextOption(const QTextOption &option); + Qt::CursorMoveStyle defaultCursorMoveStyle() const; + void setDefaultCursorMoveStyle(Qt::CursorMoveStyle style); + Q_SIGNALS: void contentsChange(int from, int charsRemoves, int charsAdded); void contentsChanged(); diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index a31e9d3..39d0e6c 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -209,6 +209,7 @@ QTextDocumentPrivate::QTextDocumentPrivate() defaultTextOption.setTabStop(80); // same as in qtextengine.cpp defaultTextOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + defaultCursorMoveStyle = Qt::LogicalMoveStyle; indentWidth = 40; documentMargin = 4; @@ -1383,6 +1384,20 @@ int QTextDocumentPrivate::previousCursorPosition(int position, QTextLayout::Curs return it.layout()->previousCursorPosition(position-start, mode) + start; } +int QTextDocumentPrivate::leftCursorPosition(int position) const +{ + QTextBlock it = blocksFind(position); + int start = it.position(); + return it.layout()->leftCursorPosition(position-start) + start; +} + +int QTextDocumentPrivate::rightCursorPosition(int position) const +{ + QTextBlock it = blocksFind(position); + int start = it.position(); + return it.layout()->rightCursorPosition(position-start) + start; +} + void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format) { beginEditBlock(); @@ -1407,11 +1422,18 @@ void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format) static QTextFrame *findChildFrame(QTextFrame *f, int pos) { - // ##### use binary search - QList<QTextFrame *> children = f->childFrames(); - for (int i = 0; i < children.size(); ++i) { - QTextFrame *c = children.at(i); - if (pos >= c->firstPosition() && pos <= c->lastPosition()) + /* Binary search for frame at pos */ + const QList<QTextFrame *> children = f->childFrames(); + int first = 0; + int last = children.size() - 1; + while (first <= last) { + int mid = (first + last) / 2; + QTextFrame *c = children.at(mid); + if (pos > c->lastPosition()) + first = mid + 1; + else if (pos < c->firstPosition()) + last = mid - 1; + else return c; } return 0; diff --git a/src/gui/text/qtextdocument_p.h b/src/gui/text/qtextdocument_p.h index dcc60a7..fbf91bf 100644 --- a/src/gui/text/qtextdocument_p.h +++ b/src/gui/text/qtextdocument_p.h @@ -64,6 +64,7 @@ #include "private/qtextformat_p.h" #include "QtGui/qtextdocument.h" #include "QtGui/qtextobject.h" +#include "QtGui/qtextcursor.h" #include "QtCore/qmap.h" #include "QtCore/qvariant.h" #include "QtCore/qurl.h" @@ -244,6 +245,8 @@ public: int nextCursorPosition(int position, QTextLayout::CursorMode mode) const; int previousCursorPosition(int position, QTextLayout::CursorMode mode) const; + int leftCursorPosition(int position) const; + int rightCursorPosition(int position) const; void changeObjectFormat(QTextObject *group, int format); @@ -339,6 +342,7 @@ private: public: QTextOption defaultTextOption; + Qt::CursorMoveStyle defaultCursorMoveStyle; #ifndef QT_NO_CSSPARSER QCss::StyleSheet parsedDefaultStyleSheet; #endif diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp index e26e661..0c8860e 100644 --- a/src/gui/text/qtextdocumentfragment.cpp +++ b/src/gui/text/qtextdocumentfragment.cpp @@ -545,8 +545,13 @@ void QTextHtmlImporter::import() } if (currentNode->isBlock()) { - if (processBlockNode() == ContinueWithNextNode) + QTextHtmlImporter::ProcessNodeResult result = processBlockNode(); + if (result == ContinueWithNextNode) { continue; + } else if (result == ContinueWithNextSibling) { + currentNodeIdx += currentNode->children.size(); + continue; + } } if (currentNode->charFormat.isAnchor() && !currentNode->charFormat.anchorName().isEmpty()) { @@ -682,6 +687,10 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processSpecialNodes() QTextListFormat listFmt; listFmt.setStyle(style); + if (!currentNode->textListNumberPrefix.isNull()) + listFmt.setNumberPrefix(currentNode->textListNumberPrefix); + if (!currentNode->textListNumberSuffix.isNull()) + listFmt.setNumberSuffix(currentNode->textListNumberSuffix); ++indent; if (currentNode->hasCssListIndent) @@ -1153,7 +1162,7 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processBlockNode() if (currentNode->isEmptyParagraph) { hasBlock = false; - return ContinueWithNextNode; + return ContinueWithNextSibling; } hasBlock = true; diff --git a/src/gui/text/qtextdocumentfragment_p.h b/src/gui/text/qtextdocumentfragment_p.h index bfbec30..227123e 100644 --- a/src/gui/text/qtextdocumentfragment_p.h +++ b/src/gui/text/qtextdocumentfragment_p.h @@ -135,7 +135,7 @@ private: Table scanTable(int tableNodeIdx); - enum ProcessNodeResult { ContinueWithNextNode, ContinueWithCurrentNode }; + enum ProcessNodeResult { ContinueWithNextNode, ContinueWithCurrentNode, ContinueWithNextSibling }; void appendBlock(const QTextBlockFormat &format, QTextCharFormat charFmt = QTextCharFormat()); bool appendNodeText(); diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp index 589eca8..a507988 100644 --- a/src/gui/text/qtextdocumentlayout.cpp +++ b/src/gui/text/qtextdocumentlayout.cpp @@ -75,8 +75,6 @@ QT_BEGIN_NAMESPACE -Q_GUI_EXPORT extern int qt_defaultDpi(); - // ################ should probably add frameFormatChange notification! struct QTextLayoutStruct; @@ -513,9 +511,6 @@ public: qreal scaleToDevice(qreal value) const; QFixed scaleToDevice(QFixed value) const; - - qreal lineH; - QTextDocumentLayout::LineHeightMode lineHeightMode; }; QTextDocumentLayoutPrivate::QTextDocumentLayoutPrivate() @@ -523,9 +518,7 @@ QTextDocumentLayoutPrivate::QTextDocumentLayoutPrivate() cursorWidth(1), currentLazyLayoutPosition(-1), lazyLayoutStepSize(1000), - lastPageCount(-1), - lineH(1), - lineHeightMode(QTextDocumentLayout::ProportionalHeight) + lastPageCount(-1) { showLayoutProgress = true; insideDocumentChange = false; @@ -2511,6 +2504,24 @@ void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayout fd->currentLayoutStruct = 0; } +static inline void getLineHeightParams(const QTextBlockFormat &blockFormat, const QTextLine &line, qreal scaling, + QFixed *lineAdjustment, QFixed *lineBreakHeight, QFixed *lineHeight) +{ + *lineHeight = QFixed::fromReal(blockFormat.lineHeight(line.height(), scaling)); + + if (blockFormat.lineHeightType() == QTextBlockFormat::FixedHeight || blockFormat.lineHeightType() == QTextBlockFormat::MinimumHeight) { + *lineBreakHeight = *lineHeight; + if (blockFormat.lineHeightType() == QTextBlockFormat::FixedHeight) + *lineAdjustment = QFixed::fromReal(line.ascent() + qMax(line.leading(), qreal(0.0))) - ((*lineHeight * 4) / 5); + else + *lineAdjustment = QFixed::fromReal(line.height()) - *lineHeight; + } + else { + *lineBreakHeight = QFixed::fromReal(line.height()); + *lineAdjustment = 0; + } +} + void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosition, const QTextBlockFormat &blockFormat, QTextLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, const QTextBlockFormat *previousBlockFormat) { @@ -2644,11 +2655,12 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi } - // TODO: replace with proper line height support in 4.8 - QFixed lineHeight = (lineHeightMode == QTextDocumentLayout::FixedHeight) - ? QFixed::fromReal(lineH) : QFixed::fromReal(line.height() * lineH); + QFixed lineBreakHeight, lineHeight, lineAdjustment; + qreal scaling = (q->paintDevice() && q->paintDevice()->logicalDpiY() != qt_defaultDpi()) ? + qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi()) : 1; + getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight); - if (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineHeight > layoutStruct->pageBottom) { + if (layoutStruct->pageHeight > 0 && layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom) { layoutStruct->newPage(); floatMargins(layoutStruct->y, layoutStruct, &left, &right); @@ -2660,7 +2672,7 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi right -= text_indent; } - line.setPosition(QPointF((left - layoutStruct->x_left).toReal(), (layoutStruct->y - cy).toReal())); + line.setPosition(QPointF((left - layoutStruct->x_left).toReal(), (layoutStruct->y - cy - lineAdjustment).toReal())); layoutStruct->y += lineHeight; layoutStruct->contentsWidth = qMax<QFixed>(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + line.naturalTextWidth()) + totalRightMargin); @@ -2680,11 +2692,16 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi QTextLine line = tl->lineAt(i); layoutStruct->contentsWidth = qMax(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + tl->lineAt(i).naturalTextWidth()) + totalRightMargin); - const QFixed lineHeight = QFixed::fromReal(line.height()); + + QFixed lineBreakHeight, lineHeight, lineAdjustment; + qreal scaling = (q->paintDevice() && q->paintDevice()->logicalDpiY() != qt_defaultDpi()) ? + qreal(q->paintDevice()->logicalDpiY()) / qreal(qt_defaultDpi()) : 1; + getLineHeightParams(blockFormat, line, scaling, &lineAdjustment, &lineBreakHeight, &lineHeight); + if (layoutStruct->pageHeight != QFIXED_MAX) { - if (layoutStruct->absoluteY() + lineHeight > layoutStruct->pageBottom) + if (layoutStruct->absoluteY() + lineBreakHeight > layoutStruct->pageBottom) layoutStruct->newPage(); - line.setPosition(QPointF(line.position().x(), layoutStruct->y.toReal() - tl->position().y())); + line.setPosition(QPointF(line.position().x(), (layoutStruct->y - lineAdjustment).toReal() - tl->position().y())); } layoutStruct->y += lineHeight; } @@ -2722,13 +2739,6 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi } } -void QTextDocumentLayout::setLineHeight(qreal lineH, QTextDocumentLayout::LineHeightMode mode = QTextDocumentLayout::ProportionalHeight) -{ - Q_D(QTextDocumentLayout); - d->lineH = lineH; - d->lineHeightMode = mode; -} - void QTextDocumentLayoutPrivate::floatMargins(const QFixed &y, const QTextLayoutStruct *layoutStruct, QFixed *left, QFixed *right) const { @@ -2986,10 +2996,19 @@ void QTextDocumentLayout::resizeInlineObject(QTextInlineObject item, int posInDo QSizeF inlineSize = (pos == QTextFrameFormat::InFlow ? intrinsic : QSizeF(0, 0)); item.setWidth(inlineSize.width()); - if (f.verticalAlignment() == QTextCharFormat::AlignMiddle) { + + QFontMetrics m(f.font()); + switch (f.verticalAlignment()) + { + case QTextCharFormat::AlignMiddle: item.setDescent(inlineSize.height() / 2); item.setAscent(inlineSize.height() / 2 - 1); - } else { + break; + case QTextCharFormat::AlignBaseline: + item.setDescent(m.descent()); + item.setAscent(inlineSize.height() - m.descent() - 1); + break; + default: item.setDescent(0); item.setAscent(inlineSize.height() - 1); } @@ -3071,6 +3090,7 @@ void QTextDocumentLayoutPrivate::ensureLayouted(QFixed y) const if (currentLazyLayoutPosition == -1) return; const QSizeF oldSize = q->dynamicDocumentSize(); + Q_UNUSED(oldSize); if (checkPoints.isEmpty()) layoutStep(); diff --git a/src/gui/text/qtextdocumentlayout_p.h b/src/gui/text/qtextdocumentlayout_p.h index 1de13e6..620b9bd 100644 --- a/src/gui/text/qtextdocumentlayout_p.h +++ b/src/gui/text/qtextdocumentlayout_p.h @@ -63,7 +63,7 @@ class QTextListFormat; class QTextDocumentLayoutPrivate; -class Q_GUI_EXPORT QTextDocumentLayout : public QAbstractTextDocumentLayout +class Q_AUTOTEST_EXPORT QTextDocumentLayout : public QAbstractTextDocumentLayout { Q_DECLARE_PRIVATE(QTextDocumentLayout) Q_OBJECT @@ -109,11 +109,6 @@ protected: void drawInlineObject(QPainter *p, const QRectF &rect, QTextInlineObject item, int posInDocument, const QTextFormat &format); virtual void timerEvent(QTimerEvent *e); - - // TODO: remove when we support line height properly in 4.8 - enum LineHeightMode { ProportionalHeight, FixedHeight }; - void setLineHeight(qreal lineHeight, QTextDocumentLayout::LineHeightMode mode); - private: QRectF doLayout(int from, int oldLength, int length); void layoutFinished(); diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index ee2eef6..2fc1dbd 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -319,6 +319,26 @@ static void appendItems(QScriptAnalysis *analysis, int &start, int &stop, const start = stop; } +static QChar::Direction skipBoundryNeutrals(QScriptAnalysis *analysis, + const ushort *unicode, int length, + int &sor, int &eor, QBidiControl &control) +{ + QChar::Direction dir = control.basicDirection(); + int level = sor > 0 ? analysis[sor - 1].bidiLevel : control.level; + while (sor < length) { + dir = QChar::direction(unicode[sor]); + // Keep skipping DirBN as if it doesn't exist + if (dir != QChar::DirBN) + break; + analysis[sor++].bidiLevel = level; + } + + eor = sor; + if (eor == length) + dir = control.basicDirection(); + + return dir; +} // creates the next QScript items. static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiControl &control) @@ -430,8 +450,7 @@ static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiCon case QChar::DirAN: if (eor >= 0) { appendItems(analysis, sor, eor, control, dir); - dir = eor < length ? QChar::direction(unicode[eor]) : control.basicDirection(); - status.eor = dir; + status.eor = dir = skipBoundryNeutrals(analysis, unicode, length, sor, eor, control); } else { eor = current; status.eor = dir; } @@ -455,8 +474,7 @@ static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiCon } eor = current - 1; appendItems(analysis, sor, eor, control, dir); - dir = eor < length ? QChar::direction(unicode[eor]) : control.basicDirection(); - status.eor = dir; + status.eor = dir = skipBoundryNeutrals(analysis, unicode, length, sor, eor, control); } else { if(status.eor != QChar::DirL) { appendItems(analysis, sor, eor, control, dir); @@ -612,7 +630,7 @@ static bool bidiItemize(QTextEngine *engine, QScriptAnalysis *analysis, QBidiCon } else { eor = current; } - dir = QChar::DirON; status.eor = QChar::DirAN; + dir = QChar::DirAN; status.eor = QChar::DirAN; break; case QChar::DirCS: if(status.eor == QChar::DirAN) { @@ -856,7 +874,20 @@ void QTextEngine::shapeLine(const QScriptLine &line) } } -Q_GUI_EXPORT extern int qt_defaultDpiY(); // in qfont.cpp +#if !defined(QT_ENABLE_HARFBUZZ_FOR_MAC) && defined(Q_WS_MAC) +static bool enableHarfBuzz() +{ + static enum { Yes, No, Unknown } status = Unknown; + + if (status == Unknown) { + QByteArray v = qgetenv("QT_ENABLE_HARFBUZZ"); + bool value = !v.isEmpty() && v != "0" && v != "false"; + if (value) status = Yes; + else status = No; + } + return status == Yes; +} +#endif void QTextEngine::shapeText(int item) const { @@ -867,7 +898,24 @@ void QTextEngine::shapeText(int item) const return; #if defined(Q_WS_MAC) - shapeTextMac(item); +#if !defined(QT_ENABLE_HARFBUZZ_FOR_MAC) + if (enableHarfBuzz()) { +#endif + QFontEngine *actualFontEngine = fontEngine(si, &si.ascent, &si.descent, &si.leading); + if (actualFontEngine->type() == QFontEngine::Multi) + actualFontEngine = static_cast<QFontEngineMulti *>(actualFontEngine)->engine(0); + + HB_Face face = actualFontEngine->harfbuzzFace(); + HB_Script script = (HB_Script) si.analysis.script; + if (face->supported_scripts[script]) + shapeTextWithHarfbuzz(item); + else + shapeTextMac(item); +#if !defined(QT_ENABLE_HARFBUZZ_FOR_MAC) + } else { + shapeTextMac(item); + } +#endif #elif defined(Q_WS_WINCE) shapeTextWithCE(item); #else @@ -920,7 +968,7 @@ void QTextEngine::shapeText(int item) const } for (int i = 0; i < si.num_glyphs; ++i) - si.width += glyphs.advances_x[i]; + si.width += glyphs.advances_x[i] * !glyphs.attributes[i].dontPrint; } static inline bool hasCaseChange(const QScriptItem &si) @@ -945,8 +993,7 @@ static void heuristicSetGlyphAttributes(const QChar *uc, int length, QGlyphLayou int glyph_pos = 0; for (int i = 0; i < length; i++) { - if (uc[i].unicode() >= 0xd800 && uc[i].unicode() < 0xdc00 && i < length-1 - && uc[i+1].unicode() >= 0xdc00 && uc[i+1].unicode() < 0xe000) { + if (uc[i].isHighSurrogate() && i < length-1 && uc[i+1].isLowSurrogate()) { logClusters[i] = glyph_pos; logClusters[++i] = glyph_pos; } else { @@ -1244,6 +1291,10 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const actualFontEngine = static_cast<QFontEngineMulti *>(font)->engine(engineIdx); } + si.ascent = qMax(actualFontEngine->ascent(), si.ascent); + si.descent = qMax(actualFontEngine->descent(), si.descent); + si.leading = qMax(actualFontEngine->leading(), si.leading); + shaper_item.font = actualFontEngine->harfbuzzFont(); shaper_item.face = actualFontEngine->harfbuzzFace(); @@ -1306,6 +1357,7 @@ static void init(QTextEngine *e) e->ignoreBidi = false; e->cacheGlyphs = false; e->forceJustification = false; + e->visualMovement = false; e->layoutData = 0; @@ -1485,33 +1537,38 @@ void QTextEngine::itemize() const const ushort *e = uc + length; int lastScript = QUnicodeTables::Common; while (uc < e) { - int script = QUnicodeTables::script(*uc); - if (script == QUnicodeTables::Inherited) - script = lastScript; - analysis->flags = QScriptAnalysis::None; - if (*uc == QChar::ObjectReplacementCharacter) { - if (analysis->bidiLevel % 2) - --analysis->bidiLevel; + switch (*uc) { + case QChar::ObjectReplacementCharacter: analysis->script = QUnicodeTables::Common; analysis->flags = QScriptAnalysis::Object; - } else if (*uc == QChar::LineSeparator) { + break; + case QChar::LineSeparator: if (analysis->bidiLevel % 2) --analysis->bidiLevel; analysis->script = QUnicodeTables::Common; analysis->flags = QScriptAnalysis::LineOrParagraphSeparator; if (option.flags() & QTextOption::ShowLineAndParagraphSeparators) *const_cast<ushort*>(uc) = 0x21B5; // visual line separator - } else if (*uc == 9) { + break; + case 9: // Tab analysis->script = QUnicodeTables::Common; analysis->flags = QScriptAnalysis::Tab; analysis->bidiLevel = control.baseLevel(); - } else if ((*uc == 32 || *uc == QChar::Nbsp) - && (option.flags() & QTextOption::ShowTabsAndSpaces)) { - analysis->script = QUnicodeTables::Common; - analysis->flags = QScriptAnalysis::Space; - analysis->bidiLevel = control.baseLevel(); - } else { - analysis->script = script; + break; + case 32: // Space + case QChar::Nbsp: + if (option.flags() & QTextOption::ShowTabsAndSpaces) { + analysis->script = QUnicodeTables::Common; + analysis->flags = QScriptAnalysis::Space; + analysis->bidiLevel = control.baseLevel(); + break; + } + // fall through + default: + int script = QUnicodeTables::script(*uc); + analysis->script = script == QUnicodeTables::Inherited ? lastScript : script; + analysis->flags = QScriptAnalysis::None; + break; } lastScript = analysis->script; ++uc; @@ -1573,6 +1630,8 @@ bool QTextEngine::isRightToLeft() const default: break; } + if (!layoutData) + itemize(); // this places the cursor in the right position depending on the keyboard layout if (layoutData->string.isEmpty()) return QApplication::keyboardInputDirection() == Qt::RightToLeft; @@ -2033,7 +2092,8 @@ void QTextEngine::justify(const QScriptLine &line) } } - QFixed need = line.width - line.textWidth; + QFixed leading = leadingSpaceWidth(line); + QFixed need = line.width - line.textWidth - leading; if (need < 0) { // line overflows already! const_cast<QScriptLine &>(line).justified = true; @@ -2377,7 +2437,7 @@ void QTextEngine::indexAdditionalFormats() between the text that gets truncated and the ellipsis. This is important to get correctly shaped results for arabic text. */ -static bool nextCharJoins(const QString &string, int pos) +static inline bool nextCharJoins(const QString &string, int pos) { while (pos < string.length() && string.at(pos).category() == QChar::Mark_NonSpacing) ++pos; @@ -2386,13 +2446,14 @@ static bool nextCharJoins(const QString &string, int pos) return string.at(pos).joining() != QChar::OtherJoining; } -static bool prevCharJoins(const QString &string, int pos) +static inline bool prevCharJoins(const QString &string, int pos) { while (pos > 0 && string.at(pos - 1).category() == QChar::Mark_NonSpacing) --pos; if (pos == 0) return false; - return (string.at(pos - 1).joining() == QChar::Dual || string.at(pos - 1).joining() == QChar::Center); + QChar::Joining joining = string.at(pos - 1).joining(); + return (joining == QChar::Dual || joining == QChar::Center); } QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int flags) const @@ -2401,30 +2462,29 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int if (flags & Qt::TextShowMnemonic) { itemize(); + HB_CharAttributes *attributes = const_cast<HB_CharAttributes *>(this->attributes()); + if (!attributes) + return QString(); for (int i = 0; i < layoutData->items.size(); ++i) { QScriptItem &si = layoutData->items[i]; if (!si.num_glyphs) shape(i); - HB_CharAttributes *attributes = const_cast<HB_CharAttributes *>(this->attributes()); - if (!attributes) - return QString(); - unsigned short *logClusters = this->logClusters(&si); QGlyphLayout glyphs = shapedGlyphs(&si); const int end = si.position + length(&si); - for (int i = si.position; i < end - 1; ++i) + for (int i = si.position; i < end - 1; ++i) { if (layoutData->string.at(i) == QLatin1Char('&')) { const int gp = logClusters[i - si.position]; glyphs.attributes[gp].dontPrint = true; attributes[i + 1].charStop = false; attributes[i + 1].whiteSpace = false; attributes[i + 1].lineBreakType = HB_NoBreak; - if (i < end - 1 - && layoutData->string.at(i + 1) == QLatin1Char('&')) + if (layoutData->string.at(i + 1) == QLatin1Char('&')) ++i; } + } } } @@ -2486,7 +2546,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int if (mode == Qt::ElideRight) { QFixed currentWidth; - int pos = 0; + int pos; int nextBreak = 0; do { @@ -2506,7 +2566,7 @@ QString QTextEngine::elidedText(Qt::TextElideMode mode, const QFixed &width, int return layoutData->string.left(pos) + ellipsisText; } else if (mode == Qt::ElideLeft) { QFixed currentWidth; - int pos = layoutData->string.length(); + int pos; int nextBreak = layoutData->string.length(); do { @@ -2603,7 +2663,7 @@ void QTextEngine::splitItem(int item, int pos) const QFixed w = 0; const QGlyphLayout g = shapedGlyphs(&oldItem); for(int j = 0; j < breakGlyph; ++j) - w += g.advances_x[j]; + w += g.advances_x[j] * !g.attributes[j].dontPrint; newItem.width = oldItem.width - w; oldItem.width = w; @@ -2612,8 +2672,6 @@ void QTextEngine::splitItem(int item, int pos) const // qDebug("split at position %d itempos=%d", pos, item); } -Q_GUI_EXPORT extern int qt_defaultDpiY(); - QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const { const QScriptItem &si = layoutData->items[item]; @@ -2742,6 +2800,55 @@ QFixed QTextEngine::leadingSpaceWidth(const QScriptLine &line) return width(line.from + pos, line.length - pos); } +QFixed QTextEngine::alignLine(const QScriptLine &line) +{ + QFixed x = 0; + justify(line); + // if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned. + if (!line.justified && line.width != QFIXED_MAX) { + int align = option.alignment(); + if (align & Qt::AlignLeft) + x -= leadingSpaceWidth(line); + if (align & Qt::AlignJustify && isRightToLeft()) + align = Qt::AlignRight; + if (align & Qt::AlignRight) + x = line.width - (line.textAdvance + leadingSpaceWidth(line)); + else if (align & Qt::AlignHCenter) + x = (line.width - line.textAdvance)/2 - leadingSpaceWidth(line); + } + return x; +} + +QFixed QTextEngine::offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos) +{ + unsigned short *logClusters = this->logClusters(si); + const QGlyphLayout &glyphs = shapedGlyphs(si); + + int offsetInCluster = 0; + for (int i = pos - 1; i >= 0; i--) { + if (logClusters[i] == glyph_pos) + offsetInCluster++; + else + break; + } + + // in the case that the offset is inside a (multi-character) glyph, + // interpolate the position. + if (offsetInCluster > 0) { + int clusterLength = 0; + for (int i = pos - offsetInCluster; i < max; i++) { + if (logClusters[i] == glyph_pos) + clusterLength++; + else + break; + } + if (clusterLength) + return glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength; + } + + return 0; +} + // Scan in logClusters[from..to-1] for glyph_pos int QTextEngine::getClusterLength(unsigned short *logClusters, const HB_CharAttributes *attributes, @@ -2812,6 +2919,133 @@ int QTextEngine::positionInLigature(const QScriptItem *si, int end, return si->position + end; } +int QTextEngine::previousLogicalPosition(int oldPos) const +{ + const HB_CharAttributes *attrs = attributes(); + if (!attrs || oldPos < 0) + return oldPos; + + if (oldPos <= 0) + return 0; + oldPos--; + while (oldPos && !attrs[oldPos].charStop) + oldPos--; + return oldPos; +} + +int QTextEngine::nextLogicalPosition(int oldPos) const +{ + const HB_CharAttributes *attrs = attributes(); + int len = block.isValid() ? block.length() - 1 + : layoutData->string.length(); + Q_ASSERT(len <= layoutData->string.length()); + if (!attrs || oldPos < 0 || oldPos >= len) + return oldPos; + + oldPos++; + while (oldPos < len && !attrs[oldPos].charStop) + oldPos++; + return oldPos; +} + +int QTextEngine::lineNumberForTextPosition(int pos) +{ + if (!layoutData) + itemize(); + if (pos == layoutData->string.length() && lines.size()) + return lines.size() - 1; + for (int i = 0; i < lines.size(); ++i) { + const QScriptLine& line = lines[i]; + if (line.from + line.length + line.trailingSpaces > pos) + return i; + } + return -1; +} + +void QTextEngine::insertionPointsForLine(int lineNum, QVector<int> &insertionPoints) +{ + QTextLineItemIterator iterator(this, lineNum); + bool rtl = isRightToLeft(); + bool lastLine = lineNum >= lines.size() - 1; + + while (!iterator.atEnd()) { + iterator.next(); + const QScriptItem *si = &layoutData->items[iterator.item]; + if (si->analysis.bidiLevel % 2) { + int i = iterator.itemEnd - 1, min = iterator.itemStart; + if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd())) + i++; + for (; i >= min; i--) + insertionPoints.push_back(i); + } else { + int i = iterator.itemStart, max = iterator.itemEnd; + if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd())) + max++; + for (; i < max; i++) + insertionPoints.push_back(i); + } + } +} + +int QTextEngine::endOfLine(int lineNum) +{ + QVector<int> insertionPoints; + insertionPointsForLine(lineNum, insertionPoints); + + if (insertionPoints.size() > 0) + return insertionPoints.last(); + return 0; +} + +int QTextEngine::beginningOfLine(int lineNum) +{ + QVector<int> insertionPoints; + insertionPointsForLine(lineNum, insertionPoints); + + if (insertionPoints.size() > 0) + return insertionPoints.first(); + return 0; +} + +int QTextEngine::positionAfterVisualMovement(int pos, QTextCursor::MoveOperation op) +{ + if (!layoutData) + itemize(); + + bool moveRight = (op == QTextCursor::Right); + bool alignRight = isRightToLeft(); + if (!layoutData->hasBidi) + return moveRight ^ alignRight ? nextLogicalPosition(pos) : previousLogicalPosition(pos); + + int lineNum = lineNumberForTextPosition(pos); + Q_ASSERT(lineNum >= 0); + + QVector<int> insertionPoints; + insertionPointsForLine(lineNum, insertionPoints); + int i, max = insertionPoints.size(); + for (i = 0; i < max; i++) + if (pos == insertionPoints[i]) { + if (moveRight) { + if (i + 1 < max) + return insertionPoints[i + 1]; + } else { + if (i > 0) + return insertionPoints[i - 1]; + } + + if (moveRight ^ alignRight) { + if (lineNum + 1 < lines.size()) + return alignRight ? endOfLine(lineNum + 1) : beginningOfLine(lineNum + 1); + } + else { + if (lineNum > 0) + return alignRight ? beginningOfLine(lineNum - 1) : endOfLine(lineNum - 1); + } + } + + return pos; +} + QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f) : QTextEngine(string, f), _layoutData(string, _memory, MemSize) @@ -2923,5 +3157,127 @@ glyph_metrics_t glyph_metrics_t::transformed(const QTransform &matrix) const return m; } +QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, const QPointF &pos, + const QTextLayout::FormatRange *_selection) + : eng(_eng), + line(eng->lines[_lineNum]), + si(0), + lineNum(_lineNum), + lineEnd(line.from + line.length), + firstItem(eng->findItem(line.from)), + lastItem(eng->findItem(lineEnd - 1)), + nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0), + logicalItem(-1), + item(-1), + visualOrder(nItems), + levels(nItems), + selection(_selection) +{ + pos_x = x = QFixed::fromReal(pos.x()); + + x += line.x; + + x += eng->alignLine(line); + + for (int i = 0; i < nItems; ++i) + levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel; + QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data()); + + eng->shapeLine(line); +} + +QScriptItem &QTextLineItemIterator::next() +{ + x += itemWidth; + + ++logicalItem; + item = visualOrder[logicalItem] + firstItem; + itemLength = eng->length(item); + si = &eng->layoutData->items[item]; + if (!si->num_glyphs) + eng->shape(item); + + if (si->analysis.flags >= QScriptAnalysis::TabOrObject) { + itemWidth = si->width; + return *si; + } + + unsigned short *logClusters = eng->logClusters(si); + QGlyphLayout glyphs = eng->shapedGlyphs(si); + + itemStart = qMax(line.from, si->position); + glyphsStart = logClusters[itemStart - si->position]; + if (lineEnd < si->position + itemLength) { + itemEnd = lineEnd; + glyphsEnd = logClusters[itemEnd-si->position]; + } else { + itemEnd = si->position + itemLength; + glyphsEnd = si->num_glyphs; + } + // show soft-hyphen at line-break + if (si->position + itemLength >= lineEnd + && eng->layoutData->string.at(lineEnd - 1) == 0x00ad) + glyphs.attributes[glyphsEnd - 1].dontPrint = false; + + itemWidth = 0; + for (int g = glyphsStart; g < glyphsEnd; ++g) + itemWidth += glyphs.effectiveAdvance(g); + + return *si; +} + +bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const +{ + *selectionX = *selectionWidth = 0; + + if (!selection) + return false; + + if (si->analysis.flags >= QScriptAnalysis::TabOrObject) { + if (si->position >= selection->start + selection->length + || si->position + itemLength <= selection->start) + return false; + + *selectionX = x; + *selectionWidth = itemWidth; + } else { + unsigned short *logClusters = eng->logClusters(si); + QGlyphLayout glyphs = eng->shapedGlyphs(si); + + int from = qMax(itemStart, selection->start) - si->position; + int to = qMin(itemEnd, selection->start + selection->length) - si->position; + if (from >= to) + return false; + + int start_glyph = logClusters[from]; + int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to]; + QFixed soff; + QFixed swidth; + if (si->analysis.bidiLevel %2) { + for (int g = glyphsEnd - 1; g >= end_glyph; --g) + soff += glyphs.effectiveAdvance(g); + for (int g = end_glyph - 1; g >= start_glyph; --g) + swidth += glyphs.effectiveAdvance(g); + } else { + for (int g = glyphsStart; g < start_glyph; ++g) + soff += glyphs.effectiveAdvance(g); + for (int g = start_glyph; g < end_glyph; ++g) + swidth += glyphs.effectiveAdvance(g); + } + + // If the starting character is in the middle of a ligature, + // selection should only contain the right part of that ligature + // glyph, so we need to get the width of the left part here and + // add it to *selectionX + QFixed leftOffsetInLigature = eng->offsetInLigature(si, from, to, start_glyph); + *selectionX = x + soff + leftOffsetInLigature; + *selectionWidth = swidth - leftOffsetInLigature; + // If the ending character is also part of a ligature, swidth does + // not contain that part yet, we also need to find out the width of + // that left part + *selectionWidth += eng->offsetInLigature(si, to, eng->length(item), end_glyph); + } + return true; +} QT_END_NAMESPACE diff --git a/src/gui/text/qtextengine_mac.cpp b/src/gui/text/qtextengine_mac.cpp index 3690057..9da8f03 100644 --- a/src/gui/text/qtextengine_mac.cpp +++ b/src/gui/text/qtextengine_mac.cpp @@ -41,6 +41,9 @@ #include "qtextengine_p.h" +#include <private/qfontengine_coretext_p.h> +#include <private/qfontengine_mac_p.h> + QT_BEGIN_NAMESPACE // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs @@ -602,11 +605,17 @@ void QTextEngine::shapeTextMac(int item) const unsigned short *log_clusters = logClusters(&si); bool stringToCMapFailed = false; - if (!fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, attributes())) { + // Skip shaping of line or paragraph separators since we are not + // going to draw them anyway + if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator + && !(option.flags() & QTextOption::ShowLineAndParagraphSeparators)) + goto cleanUp; + + if (!fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, attributes(), &si)) { ensureSpace(num_glyphs); g = availableGlyphs(&si); stringToCMapFailed = !fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, - attributes()); + attributes(), &si); } if (!stringToCMapFailed) { @@ -642,6 +651,7 @@ void QTextEngine::shapeTextMac(int item) const } } +cleanUp: const ushort *uc = reinterpret_cast<const ushort *>(str); if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index 2b6db67..067e81d 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -64,6 +64,7 @@ #include "QtGui/qpaintengine.h" #include "QtGui/qtextobject.h" #include "QtGui/qtextoption.h" +#include "QtGui/qtextcursor.h" #include "QtCore/qset.h" #include "QtCore/qdebug.h" #ifndef QT_BUILD_COMPAT_LIB @@ -128,7 +129,7 @@ struct Q_AUTOTEST_EXPORT QScriptAnalysis TabOrObject = Tab, Object = 7 }; - unsigned short script : 8; + unsigned short script : 7; unsigned short bidiLevel : 6; // Unicode Bidi algorithm embedding level (0-61) unsigned short flags : 3; inline bool operator == (const QScriptAnalysis &other) const { @@ -375,7 +376,7 @@ struct Q_AUTOTEST_EXPORT QScriptLine { // created and filled in QTextLine::layout_helper QScriptLine() - : from(0), length(0), + : from(0), trailingSpaces(0), length(0), justified(0), gridfitted(0), hasTrailingSpaces(0), leadingIncluded(0) {} QFixed descent; @@ -387,7 +388,8 @@ struct Q_AUTOTEST_EXPORT QScriptLine QFixed textWidth; QFixed textAdvance; int from; - signed int length : 29; + unsigned short trailingSpaces; + signed int length : 28; mutable uint justified : 1; mutable uint gridfitted : 1; uint hasTrailingSpaces : 1; @@ -437,7 +439,7 @@ public: uint hasBidi : 1; uint layoutState : 2; uint memory_on_stack : 1; - bool haveCharAttributes; + uint haveCharAttributes : 1; QString string; bool reallocate(int totalGlyphs); }; @@ -473,6 +475,7 @@ public: void shape(int item) const; void justify(const QScriptLine &si); + QFixed alignLine(const QScriptLine &line); QFixed width(int charFrom, int numChars) const; glyph_metrics_t boundingBox(int from, int len) const; @@ -588,12 +591,18 @@ public: uint cacheGlyphs : 1; uint stackEngine : 1; uint forceJustification : 1; + uint visualMovement : 1; int *underlinePositions; mutable LayoutData *layoutData; inline bool hasFormats() const { return (block.docHandle() || specialData); } + inline bool visualCursorMovement() const + { + return (visualMovement || + (block.docHandle() ? block.docHandle()->defaultCursorMoveStyle == Qt::VisualMoveStyle : false)); + } struct SpecialData { int preeditPosition; @@ -613,7 +622,14 @@ public: void shapeLine(const QScriptLine &line); QFixed leadingSpaceWidth(const QScriptLine &line); + QFixed offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos); int positionInLigature(const QScriptItem *si, int end, QFixed x, QFixed edge, int glyph_pos, bool cursorOnCharacter); + int previousLogicalPosition(int oldPos) const; + int nextLogicalPosition(int oldPos) const; + int lineNumberForTextPosition(int pos); + int positionAfterVisualMovement(int oldPos, QTextCursor::MoveOperation op); + void insertionPointsForLine(int lineNum, QVector<int> &insertionPoints); + void resetFontEngineCache(); private: @@ -630,6 +646,8 @@ private: void splitItem(int item, int pos) const; void resolveAdditionalFormats() const; + int endOfLine(int lineNum); + int beginningOfLine(int lineNum); int getClusterLength(unsigned short *logClusters, const HB_CharAttributes *attributes, int from, int to, int glyph_pos, int *start); }; @@ -641,6 +659,49 @@ public: void *_memory[MemSize]; }; +struct QTextLineItemIterator +{ + QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(), + const QTextLayout::FormatRange *_selection = 0); + + inline bool atEnd() const { return logicalItem >= nItems - 1; } + inline bool atBeginning() const { return logicalItem <= 0; } + QScriptItem &next(); + + bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const; + inline bool isOutsideSelection() const { + QFixed tmp1, tmp2; + return !getSelectionBounds(&tmp1, &tmp2); + } + + QTextEngine *eng; + + QFixed x; + QFixed pos_x; + const QScriptLine &line; + QScriptItem *si; + + int lineNum; + int lineEnd; + int firstItem; + int lastItem; + int nItems; + int logicalItem; + int item; + int itemLength; + + int glyphsStart; + int glyphsEnd; + int itemStart; + int itemEnd; + + QFixed itemWidth; + + QVarLengthArray<int> visualOrder; + QVarLengthArray<uchar> levels; + + const QTextLayout::FormatRange *selection; +}; Q_DECLARE_OPERATORS_FOR_FLAGS(QTextEngine::ShaperFlags) diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index a41c22e..833414f 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -411,6 +411,9 @@ void QTextFormatPrivate::recalcFont() const case QTextFormat::FontStyleHint: f.setStyleHint(static_cast<QFont::StyleHint>(props.at(i).value.toInt()), f.styleStrategy()); break; + case QTextFormat::FontHintingPreference: + f.setHintingPreference(static_cast<QFont::HintingPreference>(props.at(i).value.toInt())); + break; case QTextFormat::FontStyleStrategy: f.setStyleStrategy(static_cast<QFont::StyleStrategy>(props.at(i).value.toInt())); break; @@ -541,6 +544,8 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt) \value TabPositions Specifies the tab positions. The tab positions are structs of QTextOption::Tab which are stored in a QList (internally, in a QList<QVariant>). \value BlockIndent + \value LineHeight + \value LineHeightType \value BlockNonBreakableLines \value BlockTrailingHorizontalRulerWidth The width of a horizontal ruler element. @@ -566,6 +571,8 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt) \value FontStyleHint Corresponds to the QFont::StyleHint property \value FontStyleStrategy Corresponds to the QFont::StyleStrategy property \value FontKerning Specifies whether the font has kerning turned on. + \value FontHintingPreference Controls the use of hinting according to values + of the QFont::HintingPreference enum. \omitvalue FirstFontProperty \omitvalue LastFontProperty @@ -583,8 +590,13 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt) List properties - \value ListStyle - \value ListIndent + \value ListStyle Specifies the style used for the items in a list, + described by values of the QTextListFormat::Style enum. + \value ListIndent Specifies the amount of indentation used for a list. + \value ListNumberPrefix Defines the text which is prepended to item numbers in + numeric lists. + \value ListNumberSuffix Defines the text which is appended to item numbers in + numeric lists. Table and frame properties @@ -933,7 +945,7 @@ qreal QTextFormat::doubleProperty(int propertyId) const const QVariant prop = d->property(propertyId); if (prop.userType() != QVariant::Double && prop.userType() != QMetaType::Float) return 0.; - return qVariantValue<qreal>(prop); + return qvariant_cast<qreal>(prop); } /*! @@ -1258,16 +1270,18 @@ bool QTextFormat::operator==(const QTextFormat &rhs) const \value AlignNormal Adjacent characters are positioned in the standard way for text in the writing system in use. - \value AlignSuperScript Characters are placed above the baseline for + \value AlignSuperScript Characters are placed above the base line for normal text. - \value AlignSubScript Characters are placed below the baseline for + \value AlignSubScript Characters are placed below the base line for normal text. - \value AlignMiddle The center of the object is vertically aligned with the base line. - Currently, this is only implemented for inline objects. + \value AlignMiddle The center of the object is vertically aligned with the + base line. Currently, this is only implemented for + inline objects. \value AlignBottom The bottom edge of the object is vertically aligned with the base line. \value AlignTop The top edge of the object is vertically aligned with the base line. + \value AlignBaseline The base lines of the characters are aligned. */ /*! @@ -1563,6 +1577,25 @@ void QTextCharFormat::setUnderlineStyle(UnderlineStyle style) \sa font() */ +/*! + \since 4.8 + + \fn void QTextCharFormat::setFontHintingPreference(QFont::HintingPreference hintingPreference) + + Sets the hinting preference of the text format's font to be \a hintingPreference. + + \sa setFont(), QFont::setHintingPreference() +*/ + +/*! + \since 4.8 + + \fn QFont::HintingPreference QTextCharFormat::fontHintingPreference() const + + Returns the hinting preference set for this text format. + + \sa font(), QFont::hintingPreference() +*/ /*! \fn QPen QTextCharFormat::textOutline() const @@ -1856,6 +1889,10 @@ QFont QTextCharFormat::font() const indentation is set with setIndent(), the indentation of the first line with setTextIndent(). + Line spacing is set with setLineHeight() and retrieved via lineHeight() + and lineHeightType(). The types of line spacing available are in the + LineHeightTypes enum. + Line breaking can be enabled and disabled with setNonBreakableLines(). The brush used to paint the paragraph's background @@ -1872,6 +1909,22 @@ QFont QTextCharFormat::font() const */ /*! + \since 4.8 + \enum QTextBlockFormat::LineHeightTypes + + This enum describes the various types of line spacing support paragraphs can have. + + \value SingleHeight This is the default line height: single spacing. + \value ProportionalHeight This sets the spacing proportional to the line (in percentage). + For example, set to 200 for double spacing. + \value FixedHeight This sets the line height to a fixed line height (in pixels). + \value MinimumHeight This sets the minimum line height (in pixels). + \value LineDistanceHeight This adds the specified height between lines (in pixels). + + \sa lineHeight(), lineHeightType(), setLineHeight() +*/ + +/*! \fn QTextBlockFormat::QTextBlockFormat() Constructs a new QTextBlockFormat. @@ -1903,7 +1956,7 @@ void QTextBlockFormat::setTabPositions(const QList<QTextOption::Tab> &tabs) QList<QTextOption::Tab>::ConstIterator iter = tabs.constBegin(); while (iter != tabs.constEnd()) { QVariant v; - qVariantSetValue<QTextOption::Tab>(v, *iter); + v.setValue<QTextOption::Tab>(*iter); list.append(v); ++iter; } @@ -1925,7 +1978,7 @@ QList<QTextOption::Tab> QTextBlockFormat::tabPositions() const QList<QVariant> variantsList = qvariant_cast<QList<QVariant> >(variant); QList<QVariant>::Iterator iter = variantsList.begin(); while(iter != variantsList.end()) { - answer.append( qVariantValue<QTextOption::Tab>(*iter)); + answer.append( qvariant_cast<QTextOption::Tab>(*iter)); ++iter; } return answer; @@ -2089,6 +2142,57 @@ QList<QTextOption::Tab> QTextBlockFormat::tabPositions() const /*! + \fn void QTextBlockFormat::setLineHeight(qreal height, int heightType) + \since 4.8 + + Sets the line height for the paragraph to the value given by \a height + which is dependent on \a heightType in the way described by the + LineHeightTypes enum. + + \sa LineHeightTypes, lineHeight(), lineHeightType() +*/ + + +/*! + \fn qreal QTextBlockFormat::lineHeight(qreal scriptLineHeight, qreal scaling) const + \since 4.8 + + Returns the height of the lines in the paragraph based on the height of the + script line given by \a scriptLineHeight and the specified \a scaling + factor. + + The value that is returned is also dependent on the given LineHeightType of + the paragraph as well as the LineHeight setting that has been set for the + paragraph. + + The scaling is needed for heights that include a fixed number of pixels, to + scale them appropriately for printing. + + \sa LineHeightTypes, setLineHeight(), lineHeightType() +*/ + + +/*! + \fn qreal QTextBlockFormat::lineHeight() const + \since 4.8 + + This returns the LineHeight property for the paragraph. + + \sa LineHeightTypes, setLineHeight(), lineHeightType() +*/ + + +/*! + \fn qreal QTextBlockFormat::lineHeightType() const + \since 4.8 + + This returns the LineHeightType property of the paragraph. + + \sa LineHeightTypes, setLineHeight(), lineHeight() +*/ + + +/*! \fn void QTextBlockFormat::setNonBreakableLines(bool b) If \a b is true, the lines in the paragraph are treated as @@ -2148,6 +2252,13 @@ QList<QTextOption::Tab> QTextBlockFormat::tabPositions() const numbering scheme used for items in the list. Note that lists that use the decimal numbering scheme begin counting at 1 rather than 0. + Style properties can be set to further configure the appearance of list + items; for example, the ListNumberPrefix and ListNumberSuffix properties + can be used to customize the numbers used in an ordered list so that they + appear as (1), (2), (3), etc.: + + \snippet doc/src/snippets/textdocument-listitemstyles/mainwindow.cpp add a styled, ordered list + \sa QTextList */ @@ -2235,6 +2346,49 @@ QTextListFormat::QTextListFormat(const QTextFormat &fmt) \sa setIndent() */ +/*! + \fn void QTextListFormat::setNumberPrefix(const QString &numberPrefix) + \since 4.8 + + Sets the list format's number prefix to the string specified by + \a numberPrefix. This can be used with all sorted list types. It does not + have any effect on unsorted list types. + + The default prefix is an empty string. + + \sa numberPrefix() +*/ + +/*! + \fn int QTextListFormat::numberPrefix() const + \since 4.8 + + Returns the list format's number prefix. + + \sa setNumberPrefix() +*/ + +/*! + \fn void QTextListFormat::setNumberSuffix(const QString &numberSuffix) + \since 4.8 + + Sets the list format's number suffix to the string specified by + \a numberSuffix. This can be used with all sorted list types. It does not + have any effect on unsorted list types. + + The default suffix is ".". + + \sa numberSuffix() +*/ + +/*! + \fn int QTextListFormat::numberSuffix() const + \since 4.8 + + Returns the list format's number suffix. + + \sa setNumberSuffix() +*/ /*! \class QTextFrameFormat diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h index 5bd447a..a7bc15e 100644 --- a/src/gui/text/qtextformat.h +++ b/src/gui/text/qtextformat.h @@ -164,6 +164,8 @@ public: TextIndent = 0x1034, TabPositions = 0x1035, BlockIndent = 0x1040, + LineHeight = 0x1048, + LineHeightType = 0x1049, BlockNonBreakableLines = 0x1050, BlockTrailingHorizontalRulerWidth = 0x1060, @@ -175,6 +177,7 @@ public: FontStyleHint = 0x1FE3, FontStyleStrategy = 0x1FE4, FontKerning = 0x1FE5, + FontHintingPreference = 0x1FE6, FontFamily = 0x2000, FontPointSize = 0x2001, FontSizeAdjustment = 0x2002, @@ -202,6 +205,8 @@ public: // list properties ListStyle = 0x3000, ListIndent = 0x3001, + ListNumberPrefix = 0x3002, + ListNumberSuffix = 0x3003, // table and frame properties FrameBorder = 0x4000, @@ -373,7 +378,8 @@ public: AlignSubScript, AlignMiddle, AlignTop, - AlignBottom + AlignBottom, + AlignBaseline }; enum UnderlineStyle { // keep in sync with Qt::PenStyle! NoUnderline, @@ -456,6 +462,16 @@ public: QFont::StyleStrategy fontStyleStrategy() const { return static_cast<QFont::StyleStrategy>(intProperty(FontStyleStrategy)); } + inline void setFontHintingPreference(QFont::HintingPreference hintingPreference) + { + setProperty(FontHintingPreference, hintingPreference); + } + + inline QFont::HintingPreference fontHintingPreference() const + { + return static_cast<QFont::HintingPreference>(intProperty(FontHintingPreference)); + } + inline void setFontKerning(bool enable) { setProperty(FontKerning, enable); } inline bool fontKerning() const @@ -529,6 +545,14 @@ inline void QTextCharFormat::setTableCellColumnSpan(int _tableCellColumnSpan) class Q_GUI_EXPORT QTextBlockFormat : public QTextFormat { public: + enum LineHeightTypes { + SingleHeight = 0, + ProportionalHeight = 1, + FixedHeight = 2, + MinimumHeight = 3, + LineDistanceHeight = 4 + }; + QTextBlockFormat(); bool isValid() const { return isBlockFormat(); } @@ -566,6 +590,14 @@ public: inline int indent() const { return intProperty(BlockIndent); } + inline void setLineHeight(qreal height, int heightType) + { setProperty(LineHeight, height); setProperty(LineHeightType, heightType); } + inline qreal lineHeight(qreal scriptLineHeight, qreal scaling) const; + inline qreal lineHeight() const + { return doubleProperty(LineHeight); } + inline int lineHeightType() const + { return intProperty(LineHeightType); } + inline void setNonBreakableLines(bool b) { setProperty(BlockNonBreakableLines, b); } inline bool nonBreakableLines() const @@ -590,6 +622,23 @@ inline void QTextBlockFormat::setAlignment(Qt::Alignment aalignment) inline void QTextBlockFormat::setIndent(int aindent) { setProperty(BlockIndent, aindent); } +inline qreal QTextBlockFormat::lineHeight(qreal scriptLineHeight, qreal scaling = 1.0) const +{ + switch(intProperty(LineHeightType)) { + case SingleHeight: + return(scriptLineHeight); + case ProportionalHeight: + return(scriptLineHeight * doubleProperty(LineHeight) / 100.0); + case FixedHeight: + return(doubleProperty(LineHeight) * scaling); + case MinimumHeight: + return(qMax(scriptLineHeight, doubleProperty(LineHeight) * scaling)); + case LineDistanceHeight: + return(scriptLineHeight + doubleProperty(LineHeight) * scaling); + } + return(0); +} + class Q_GUI_EXPORT QTextListFormat : public QTextFormat { public: @@ -617,6 +666,14 @@ public: inline int indent() const { return intProperty(ListIndent); } + inline void setNumberPrefix(const QString &numberPrefix); + inline QString numberPrefix() const + { return stringProperty(ListNumberPrefix); } + + inline void setNumberSuffix(const QString &numberSuffix); + inline QString numberSuffix() const + { return stringProperty(ListNumberSuffix); } + protected: explicit QTextListFormat(const QTextFormat &fmt); friend class QTextFormat; @@ -628,6 +685,12 @@ inline void QTextListFormat::setStyle(Style astyle) inline void QTextListFormat::setIndent(int aindent) { setProperty(ListIndent, aindent); } +inline void QTextListFormat::setNumberPrefix(const QString &np) +{ setProperty(ListNumberPrefix, np); } + +inline void QTextListFormat::setNumberSuffix(const QString &ns) +{ setProperty(ListNumberSuffix, ns); } + class Q_GUI_EXPORT QTextImageFormat : public QTextCharFormat { public: diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index ecda6f0..d130c61 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -820,15 +820,11 @@ QString QTextHtmlParser::parseEntity() if (uc >= 0x80 && uc < 0x80 + (sizeof(windowsLatin1ExtendedCharacters)/sizeof(windowsLatin1ExtendedCharacters[0]))) uc = windowsLatin1ExtendedCharacters[uc - 0x80]; QString str; - if (uc > 0xffff) { - // surrogate pair - uc -= 0x10000; - ushort high = uc/0x400 + 0xd800; - ushort low = uc%0x400 + 0xdc00; - str.append(QChar(high)); - str.append(QChar(low)); + if (QChar::requiresSurrogates(uc)) { + str += QChar(QChar::highSurrogate(uc)); + str += QChar(QChar::lowSurrogate(uc)); } else { - str.append(QChar(uc)); + str = QChar(uc); } return str; } @@ -1250,6 +1246,20 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration> case QCss::QtBlockIndent: blockFormat.setIndent(decl.d->values.first().variant.toInt()); break; + case QCss::LineHeight: { + qreal lineHeight; + if (decl.realValue(&lineHeight, "px")) { + blockFormat.setLineHeight(lineHeight, QTextBlockFormat::FixedHeight); + } else { + bool ok; + QString value = decl.d->values.first().toString(); + lineHeight = value.toDouble(&ok); + if (ok) + blockFormat.setLineHeight(lineHeight, QTextBlockFormat::ProportionalHeight); + else + blockFormat.setLineHeight(0, QTextBlockFormat::SingleHeight); + } + break; } case QCss::TextIndent: { qreal indent = 0; if (decl.realValue(&indent, "px")) @@ -1323,6 +1333,12 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration> case QCss::ListStyle: setListStyle(decl.d->values); break; + case QCss::QtListNumberPrefix: + textListNumberPrefix = decl.d->values.first().variant.toString(); + break; + case QCss::QtListNumberSuffix: + textListNumberSuffix = decl.d->values.first().variant.toString(); + break; default: break; } } diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h index e13939d..9019942 100644 --- a/src/gui/text/qtexthtmlparser_p.h +++ b/src/gui/text/qtexthtmlparser_p.h @@ -180,6 +180,8 @@ struct QTextHtmlParserNode { uint displayMode : 3; // QTextHtmlElement::DisplayMode uint hasHref : 1; QTextListFormat::Style listStyle; + QString textListNumberPrefix; + QString textListNumberSuffix; QString imageName; qreal imageWidth; qreal imageHeight; diff --git a/src/gui/text/qtextimagehandler.cpp b/src/gui/text/qtextimagehandler.cpp index 35e73db..7876118 100644 --- a/src/gui/text/qtextimagehandler.cpp +++ b/src/gui/text/qtextimagehandler.cpp @@ -123,7 +123,6 @@ static QSize getPixmapSize(QTextDocument *doc, const QTextImageFormat &format) qreal scale = 1.0; QPaintDevice *pdev = doc->documentLayout()->paintDevice(); if (pdev) { - Q_GUI_EXPORT extern int qt_defaultDpi(); if (pm.isNull()) pm = getPixmap(doc, format); if (!pm.isNull()) @@ -191,7 +190,6 @@ static QSize getImageSize(QTextDocument *doc, const QTextImageFormat &format) qreal scale = 1.0; QPaintDevice *pdev = doc->documentLayout()->paintDevice(); if (pdev) { - extern int qt_defaultDpi(); if (image.isNull()) image = getImage(doc, format); if (!image.isNull()) diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index f2d3de1..83c3676 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -52,35 +52,26 @@ #include "qtextformat_p.h" #include "qstyleoption.h" #include "qpainterpath.h" +#include "qglyphrun.h" +#include "qglyphrun_p.h" +#include "qrawfont.h" +#include "qrawfont_p.h" #include <limits.h> #include <qdebug.h> #include "qfontengine_p.h" +#if !defined(QT_NO_FREETYPE) +# include "qfontengine_ft_p.h" +#endif + QT_BEGIN_NAMESPACE #define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1) #define SuppressText 0x5012 #define SuppressBackground 0x513 -static QFixed alignLine(QTextEngine *eng, const QScriptLine &line) -{ - QFixed x = 0; - eng->justify(line); - // if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned. - if (!line.justified && line.width != QFIXED_MAX) { - int align = eng->option.alignment(); - if (align & Qt::AlignJustify && eng->isRightToLeft()) - align = Qt::AlignRight; - if (align & Qt::AlignRight) - x = line.width - (line.textAdvance + eng->leadingSpaceWidth(line)); - else if (align & Qt::AlignHCenter) - x = (line.width - (line.textAdvance))/2 - eng->leadingSpaceWidth(line); - } - return x; -} - /*! \class QTextLayout::FormatRange \reentrant @@ -148,7 +139,7 @@ static QFixed alignLine(QTextEngine *eng, const QScriptLine &line) /*! Returns the inline object's rectangle. - \sa ascent() descent() width() + \sa ascent(), descent(), width() */ QRectF QTextInlineObject::rect() const { @@ -159,7 +150,7 @@ QRectF QTextInlineObject::rect() const /*! Returns the inline object's width. - \sa ascent() descent() rect() + \sa ascent(), descent(), rect() */ qreal QTextInlineObject::width() const { @@ -169,7 +160,7 @@ qreal QTextInlineObject::width() const /*! Returns the inline object's ascent. - \sa descent() width() rect() + \sa descent(), width(), rect() */ qreal QTextInlineObject::ascent() const { @@ -179,7 +170,7 @@ qreal QTextInlineObject::ascent() const /*! Returns the inline object's descent. - \sa ascent() width() rect() + \sa ascent(), width(), rect() */ qreal QTextInlineObject::descent() const { @@ -190,18 +181,17 @@ qreal QTextInlineObject::descent() const Returns the inline object's total height. This is equal to ascent() + descent() + 1. - \sa ascent() descent() width() rect() + \sa ascent(), descent(), width(), rect() */ qreal QTextInlineObject::height() const { return eng->layoutData->items[itm].height().toReal(); } - /*! Sets the inline object's width to \a w. - \sa width() ascent() descent() rect() + \sa width(), ascent(), descent(), rect() */ void QTextInlineObject::setWidth(qreal w) { @@ -211,7 +201,7 @@ void QTextInlineObject::setWidth(qreal w) /*! Sets the inline object's ascent to \a a. - \sa ascent() setDescent() width() rect() + \sa ascent(), setDescent(), width(), rect() */ void QTextInlineObject::setAscent(qreal a) { @@ -221,7 +211,7 @@ void QTextInlineObject::setAscent(qreal a) /*! Sets the inline object's decent to \a d. - \sa descent() setAscent() width() rect() + \sa descent(), setAscent(), width(), rect() */ void QTextInlineObject::setDescent(qreal d) { @@ -229,7 +219,7 @@ void QTextInlineObject::setDescent(qreal d) } /*! - The position of the inline object within the text layout. + The position of the inline object within the text layout. */ int QTextInlineObject::textPosition() const { @@ -237,8 +227,8 @@ int QTextInlineObject::textPosition() const } /*! - Returns an integer describing the format of the inline object - within the text layout. + Returns an integer describing the format of the inline object + within the text layout. */ int QTextInlineObject::formatIndex() const { @@ -246,7 +236,7 @@ int QTextInlineObject::formatIndex() const } /*! - Returns format of the inline object within the text layout. + Returns format of the inline object within the text layout. */ QTextFormat QTextInlineObject::format() const { @@ -256,7 +246,7 @@ QTextFormat QTextInlineObject::format() const } /*! - Returns if the object should be laid out right-to-left or left-to-right. + Returns if the object should be laid out right-to-left or left-to-right. */ Qt::LayoutDirection QTextInlineObject::textDirection() const { @@ -308,7 +298,6 @@ Qt::LayoutDirection QTextInlineObject::textDirection() const boundingRect(), and a minimumWidth() and a maximumWidth(). \sa QStaticText - */ /*! @@ -381,7 +370,7 @@ QTextLayout::~QTextLayout() Sets the layout's font to the given \a font. The layout is invalidated and must be laid out again. - \sa text() + \sa font() */ void QTextLayout::setFont(const QFont &font) { @@ -392,6 +381,8 @@ void QTextLayout::setFont(const QFont &font) /*! Returns the current font that is used for the layout, or a default font if none is set. + + \sa setFont() */ QFont QTextLayout::font() const { @@ -425,10 +416,10 @@ QString QTextLayout::text() const } /*! - Sets the text option structure that controls the layout process to the - given \a option. + Sets the text option structure that controls the layout process to the + given \a option. - \sa textOption() QTextOption + \sa textOption() */ void QTextLayout::setTextOption(const QTextOption &option) { @@ -436,9 +427,9 @@ void QTextLayout::setTextOption(const QTextOption &option) } /*! - Returns the current text option used to control the layout process. + Returns the current text option used to control the layout process. - \sa setTextOption() QTextOption + \sa setTextOption() */ QTextOption QTextLayout::textOption() const { @@ -448,6 +439,8 @@ QTextOption QTextLayout::textOption() const /*! Sets the \a position and \a text of the area in the layout that is processed before editing occurs. + + \sa preeditAreaPosition(), preeditAreaText() */ void QTextLayout::setPreeditArea(int position, const QString &text) { @@ -476,6 +469,8 @@ void QTextLayout::setPreeditArea(int position, const QString &text) /*! Returns the position of the area in the text layout that will be processed before editing occurs. + + \sa preeditAreaText() */ int QTextLayout::preeditAreaPosition() const { @@ -484,6 +479,8 @@ int QTextLayout::preeditAreaPosition() const /*! Returns the text that is inserted in the layout before editing occurs. + + \sa preeditAreaPosition() */ QString QTextLayout::preeditAreaText() const { @@ -492,8 +489,7 @@ QString QTextLayout::preeditAreaText() const /*! - Sets the additional formats supported by the text layout to \a - formatList. + Sets the additional formats supported by the text layout to \a formatList. \sa additionalFormats(), clearAdditionalFormats() */ @@ -583,7 +579,37 @@ bool QTextLayout::cacheEnabled() const } /*! + \since 4.8 + + Set the cursor movement style. If the QTextLayout is backed by + a document, you can ignore this and use the option in QTextDocument, + this option is for widgets like QLineEdit or custom widgets without + a QTextDocument. Default value is Qt::LogicalMoveStyle. + + \sa cursorMoveStyle() +*/ +void QTextLayout::setCursorMoveStyle(Qt::CursorMoveStyle style) +{ + d->visualMovement = style == Qt::VisualMoveStyle ? true : false; +} + +/*! + \since 4.8 + + The cursor movement style of this QTextLayout. The default is + Qt::LogicalMoveStyle. + + \sa setCursorMoveStyle() +*/ +Qt::CursorMoveStyle QTextLayout::cursorMoveStyle() const +{ + return d->visualMovement ? Qt::VisualMoveStyle : Qt::LogicalMoveStyle; +} + +/*! Begins the layout process. + + \sa endLayout() */ void QTextLayout::beginLayout() { @@ -601,6 +627,8 @@ void QTextLayout::beginLayout() /*! Ends the layout process. + + \sa beginLayout() */ void QTextLayout::endLayout() { @@ -619,35 +647,33 @@ void QTextLayout::endLayout() d->freeMemory(); } -/*! \since 4.4 +/*! + \since 4.4 -Clears the line information in the layout. After having called -this function, lineCount() returns 0. - */ + Clears the line information in the layout. After having called + this function, lineCount() returns 0. +*/ void QTextLayout::clearLayout() { d->clearLineData(); } - /*! Returns the next valid cursor position after \a oldPos that respects the given cursor \a mode. + Returns value of \a oldPos, if \a oldPos is not a valid cursor position. - \sa isValidCursorPosition() previousCursorPosition() + \sa isValidCursorPosition(), previousCursorPosition() */ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const { -// qDebug("looking for next cursor pos for %d", oldPos); const HB_CharAttributes *attributes = d->attributes(); - if (!attributes) - return 0; - int len = d->block.isValid() ? - (d->block.length() - 1) - : d->layoutData->string.length(); - - if (oldPos >= len) + int len = d->block.isValid() ? d->block.length() - 1 + : d->layoutData->string.length(); + Q_ASSERT(len <= d->layoutData->string.length()); + if (!attributes || oldPos < 0 || oldPos >= len) return oldPos; + if (mode == SkipCharacters) { oldPos++; while (oldPos < len && !attributes[oldPos].charStop) @@ -664,22 +690,23 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const while (oldPos < len && d->atSpace(oldPos)) oldPos++; } -// qDebug(" -> %d", oldPos); + return oldPos; } /*! Returns the first valid cursor position before \a oldPos that respects the given cursor \a mode. + Returns value of \a oldPos, if \a oldPos is not a valid cursor position. - \sa isValidCursorPosition() nextCursorPosition() + \sa isValidCursorPosition(), nextCursorPosition() */ int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const { -// qDebug("looking for previous cursor pos for %d", oldPos); const HB_CharAttributes *attributes = d->attributes(); - if (!attributes || oldPos <= 0) - return 0; + if (!attributes || oldPos <= 0 || oldPos > d->layoutData->string.length()) + return oldPos; + if (mode == SkipCharacters) { oldPos--; while (oldPos && !attributes[oldPos].charStop) @@ -697,11 +724,43 @@ int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const oldPos--; } } -// qDebug(" -> %d", oldPos); + return oldPos; } /*! + \since 4.8 + + Returns the cursor position to the right of \a oldPos, next to it. + The position is dependent on the visual position of characters, after + bi-directional reordering. + + \sa leftCursorPosition(), nextCursorPosition() +*/ +int QTextLayout::rightCursorPosition(int oldPos) const +{ + int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right); +// qDebug("%d -> %d", oldPos, newPos); + return newPos; +} + +/*! + \since 4.8 + + Returns the cursor position to the left of \a oldPos, next to it. + The position is dependent on the visual position of characters, after + bi-directional reordering. + + \sa rightCursorPosition(), previousCursorPosition() +*/ +int QTextLayout::leftCursorPosition(int oldPos) const +{ + int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left); +// qDebug("%d -> %d", oldPos, newPos); + return newPos; +} + +/*!/ Returns true if position \a pos is a valid cursor position. In a Unicode context some positions in the text are not valid @@ -724,7 +783,6 @@ bool QTextLayout::isValidCursorPosition(int pos) const return attributes[pos].charStop; } - /*! Returns a new text line to be laid out if there is text to be inserted into the layout; otherwise returns an invalid text line. @@ -756,7 +814,7 @@ QTextLine QTextLayout::createLine() if (l && d->lines.at(l-1).length < 0) { QTextLine(l-1, d).setNumColumns(INT_MAX); } - int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length : 0; + int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0; int strlen = d->layoutData->string.length(); if (l && from >= strlen) { if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator) @@ -786,7 +844,7 @@ int QTextLayout::lineCount() const /*! Returns the \a{i}-th line of text in this text layout. - \sa lineCount() lineForTextPosition() + \sa lineCount(), lineForTextPosition() */ QTextLine QTextLayout::lineAt(int i) const { @@ -796,20 +854,12 @@ QTextLine QTextLayout::lineAt(int i) const /*! Returns the line that contains the cursor position specified by \a pos. - \sa isValidCursorPosition() lineAt() + \sa isValidCursorPosition(), lineAt() */ QTextLine QTextLayout::lineForTextPosition(int pos) const { - for (int i = 0; i < d->lines.size(); ++i) { - const QScriptLine& line = d->lines[i]; - if (line.from + (int)line.length > pos) - return QTextLine(i, d); - } - if (!d->layoutData) - d->itemize(); - if (pos == d->layoutData->string.length() && d->lines.size()) - return QTextLine(d->lines.size()-1, d); - return QTextLine(); + int lineNum = d->lineNumberForTextPosition(pos); + return lineNum >= 0 ? lineAt(lineNum) : QTextLine(); } /*! @@ -887,8 +937,9 @@ qreal QTextLayout::maximumWidth() const return d->maxWidth.toReal(); } + /*! - \internal + \internal */ void QTextLayout::setFlags(int flags) { @@ -903,201 +954,6 @@ void QTextLayout::setFlags(int flags) } } -struct QTextLineItemIterator -{ - QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(), - const QTextLayout::FormatRange *_selection = 0); - - inline bool atEnd() const { return logicalItem >= nItems - 1; } - QScriptItem &next(); - - bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const; - inline bool isOutsideSelection() const { - QFixed tmp1, tmp2; - return !getSelectionBounds(&tmp1, &tmp2); - } - - QTextEngine *eng; - - QFixed x; - QFixed pos_x; - const QScriptLine &line; - QScriptItem *si; - - int lineEnd; - int firstItem; - int lastItem; - int nItems; - int logicalItem; - int item; - int itemLength; - - int glyphsStart; - int glyphsEnd; - int itemStart; - int itemEnd; - - QFixed itemWidth; - - QVarLengthArray<int> visualOrder; - QVarLengthArray<uchar> levels; - - const QTextLayout::FormatRange *selection; -}; - -QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int lineNum, const QPointF &pos, - const QTextLayout::FormatRange *_selection) - : eng(_eng), - line(eng->lines[lineNum]), - si(0), - lineEnd(line.from + line.length), - firstItem(eng->findItem(line.from)), - lastItem(eng->findItem(lineEnd - 1)), - nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0), - logicalItem(-1), - item(-1), - visualOrder(nItems), - levels(nItems), - selection(_selection) -{ - pos_x = x = QFixed::fromReal(pos.x()); - - x += line.x; - - x += alignLine(eng, line); - - for (int i = 0; i < nItems; ++i) - levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel; - QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data()); - - eng->shapeLine(line); -} - -QScriptItem &QTextLineItemIterator::next() -{ - x += itemWidth; - - ++logicalItem; - item = visualOrder[logicalItem] + firstItem; - itemLength = eng->length(item); - si = &eng->layoutData->items[item]; - if (!si->num_glyphs) - eng->shape(item); - - if (si->analysis.flags >= QScriptAnalysis::TabOrObject) { - itemWidth = si->width; - return *si; - } - - unsigned short *logClusters = eng->logClusters(si); - QGlyphLayout glyphs = eng->shapedGlyphs(si); - - itemStart = qMax(line.from, si->position); - glyphsStart = logClusters[itemStart - si->position]; - if (lineEnd < si->position + itemLength) { - itemEnd = lineEnd; - glyphsEnd = logClusters[itemEnd-si->position]; - } else { - itemEnd = si->position + itemLength; - glyphsEnd = si->num_glyphs; - } - // show soft-hyphen at line-break - if (si->position + itemLength >= lineEnd - && eng->layoutData->string.at(lineEnd - 1) == 0x00ad) - glyphs.attributes[glyphsEnd - 1].dontPrint = false; - - itemWidth = 0; - for (int g = glyphsStart; g < glyphsEnd; ++g) - itemWidth += glyphs.effectiveAdvance(g); - - return *si; -} - -static QFixed offsetInLigature(const unsigned short *logClusters, - const QGlyphLayout &glyphs, - int pos, int max, int glyph_pos) -{ - int offsetInCluster = 0; - for (int i = pos - 1; i >= 0; i--) { - if (logClusters[i] == glyph_pos) - offsetInCluster++; - else - break; - } - - // in the case that the offset is inside a (multi-character) glyph, - // interpolate the position. - if (offsetInCluster > 0) { - int clusterLength = 0; - for (int i = pos - offsetInCluster; i < max; i++) { - if (logClusters[i] == glyph_pos) - clusterLength++; - else - break; - } - if (clusterLength) - return glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength; - } - - return 0; -} - -bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const -{ - *selectionX = *selectionWidth = 0; - - if (!selection) - return false; - - if (si->analysis.flags >= QScriptAnalysis::TabOrObject) { - if (si->position >= selection->start + selection->length - || si->position + itemLength <= selection->start) - return false; - - *selectionX = x; - *selectionWidth = itemWidth; - } else { - unsigned short *logClusters = eng->logClusters(si); - QGlyphLayout glyphs = eng->shapedGlyphs(si); - - int from = qMax(itemStart, selection->start) - si->position; - int to = qMin(itemEnd, selection->start + selection->length) - si->position; - if (from >= to) - return false; - - int start_glyph = logClusters[from]; - int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to]; - QFixed soff; - QFixed swidth; - if (si->analysis.bidiLevel %2) { - for (int g = glyphsEnd - 1; g >= end_glyph; --g) - soff += glyphs.effectiveAdvance(g); - for (int g = end_glyph - 1; g >= start_glyph; --g) - swidth += glyphs.effectiveAdvance(g); - } else { - for (int g = glyphsStart; g < start_glyph; ++g) - soff += glyphs.effectiveAdvance(g); - for (int g = start_glyph; g < end_glyph; ++g) - swidth += glyphs.effectiveAdvance(g); - } - - // If the starting character is in the middle of a ligature, - // selection should only contain the right part of that ligature - // glyph, so we need to get the width of the left part here and - // add it to *selectionX - QFixed leftOffsetInLigature = offsetInLigature(logClusters, glyphs, from, - to, start_glyph); - *selectionX = x + soff + leftOffsetInLigature; - *selectionWidth = swidth - leftOffsetInLigature; - // If the ending character is also part of a ligature, swidth does - // not contain that part yet, we also need to find out the width of - // that left part - *selectionWidth += offsetInLigature(logClusters, glyphs, to, - eng->length(item), end_glyph); - } - return true; -} - static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection, QPainterPath *region, QRectF boundingRect) { @@ -1139,9 +995,28 @@ static inline QRectF clipIfValid(const QRectF &rect, const QRectF &clip) return clip.isValid() ? (rect & clip) : rect; } + /*! - Draws the whole layout on the painter \a p at the position specified by - \a pos. + Returns the glyph indexes and positions for all glyphs in this QTextLayout. This is an + expensive function, and should not be called in a time sensitive context. + + \since 4.8 + + \sa draw(), QPainter::drawGlyphRun() +*/ +#if !defined(QT_NO_RAWFONT) +QList<QGlyphRun> QTextLayout::glyphRuns() const +{ + QList<QGlyphRun> glyphs; + for (int i=0; i<d->lines.size(); ++i) + glyphs += QTextLine(i, d).glyphs(-1, -1); + + return glyphs; +} +#endif // QT_NO_RAWFONT + +/*! + Draws the whole layout on the painter \a p at the position specified by \a pos. The rendered layout includes the given \a selections and is clipped within the rectangle specified by \a clip. */ @@ -1193,6 +1068,7 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRang QRectF lineRect(tl.naturalTextRect()); lineRect.translate(position); + lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0); bool isLastLineInBlock = (line == d->lines.size()-1); int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline @@ -1315,12 +1191,12 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRang } /*! - \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition) const - \overload + \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition) const + \overload - Draws a text cursor with the current pen at the given \a position using the - \a painter specified. - The corresponding position within the text is specified by \a cursorPosition. + Draws a text cursor with the current pen at the given \a position using the + \a painter specified. + The corresponding position within the text is specified by \a cursorPosition. */ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition) const { @@ -1328,11 +1204,11 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition } /*! - \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition, int width) const + \fn void QTextLayout::drawCursor(QPainter *painter, const QPointF &position, int cursorPosition, int width) const - Draws a text cursor with the current pen and the specified \a width at the given \a position using the - \a painter specified. - The corresponding position within the text is specified by \a cursorPosition. + Draws a text cursor with the current pen and the specified \a width at the given \a position using the + \a painter specified. + The corresponding position within the text is specified by \a cursorPosition. */ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition, int width) const { @@ -1343,22 +1219,11 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition d->itemize(); QPointF position = pos + d->position; - QFixed pos_x = QFixed::fromReal(position.x()); - QFixed pos_y = QFixed::fromReal(position.y()); cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length()); - int line = 0; - if (cursorPosition == d->layoutData->string.length()) { - line = d->lines.size() - 1; - } else { - // ### binary search - for (line = 0; line < d->lines.size(); line++) { - const QScriptLine &sl = d->lines[line]; - if (sl.from <= cursorPosition && sl.from + (int)sl.length > cursorPosition) - break; - } - } - + int line = d->lineNumberForTextPosition(cursorPosition); + if (line < 0) + line = 0; if (line >= d->lines.size()) return; @@ -1367,7 +1232,15 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition qreal x = position.x() + l.cursorToX(cursorPosition); - int itm = d->findItem(cursorPosition - 1); + int itm; + + if (d->visualCursorMovement()) { + if (cursorPosition == sl.from + sl.length) + cursorPosition--; + itm = d->findItem(cursorPosition); + } else + itm = d->findItem(cursorPosition - 1); + QFixed base = sl.base(); QFixed descent = sl.descent; bool rightToLeft = d->isRightToLeft(); @@ -1463,7 +1336,7 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition /*! Returns the line's bounding rectangle. - \sa x() y() textLength() width() + \sa x(), y(), textLength(), width() */ QRectF QTextLine::rect() const { @@ -1477,7 +1350,7 @@ QRectF QTextLine::rect() const QRectF QTextLine::naturalTextRect() const { const QScriptLine& sl = eng->lines[i]; - QFixed x = sl.x + alignLine(eng, sl); + QFixed x = sl.x + eng->alignLine(sl); QFixed width = sl.textWidth; if (sl.justified) @@ -1489,7 +1362,7 @@ QRectF QTextLine::naturalTextRect() const /*! Returns the line's x position. - \sa rect() y() textLength() width() + \sa rect(), y(), textLength(), width() */ qreal QTextLine::x() const { @@ -1499,7 +1372,7 @@ qreal QTextLine::x() const /*! Returns the line's y position. - \sa x() rect() textLength() width() + \sa x(), rect(), textLength(), width() */ qreal QTextLine::y() const { @@ -1509,7 +1382,7 @@ qreal QTextLine::y() const /*! Returns the line's width as specified by the layout() function. - \sa naturalTextWidth() x() y() textLength() rect() + \sa naturalTextWidth(), x(), y(), textLength(), rect() */ qreal QTextLine::width() const { @@ -1520,7 +1393,7 @@ qreal QTextLine::width() const /*! Returns the line's ascent. - \sa descent() height() + \sa descent(), height() */ qreal QTextLine::ascent() const { @@ -1530,7 +1403,7 @@ qreal QTextLine::ascent() const /*! Returns the line's descent. - \sa ascent() height() + \sa ascent(), height() */ qreal QTextLine::descent() const { @@ -1542,7 +1415,7 @@ qreal QTextLine::descent() const if leading is not included. If leading is included, this equals to ascent() + descent() + leading() + 1. - \sa ascent() descent() leading() setLeadingIncluded() + \sa ascent(), descent(), leading(), setLeadingIncluded() */ qreal QTextLine::height() const { @@ -1554,24 +1427,25 @@ qreal QTextLine::height() const Returns the line's leading. - \sa ascent() descent() height() + \sa ascent(), descent(), height() */ qreal QTextLine::leading() const { return eng->lines[i].leading.toReal(); } -/*! \since 4.6 +/*! + \since 4.6 - Includes positive leading into the line's height if \a included is true; - otherwise does not include leading. + Includes positive leading into the line's height if \a included is true; + otherwise does not include leading. - By default, leading is not included. + By default, leading is not included. - Note that negative leading is ignored, it must be handled - in the code using the text lines by letting the lines overlap. + Note that negative leading is ignored, it must be handled + in the code using the text lines by letting the lines overlap. - \sa leadingIncluded() + \sa leadingIncluded() */ void QTextLine::setLeadingIncluded(bool included) @@ -1580,20 +1454,21 @@ void QTextLine::setLeadingIncluded(bool included) } -/*! \since 4.6 +/*! + \since 4.6 - Returns true if positive leading is included into the line's height; otherwise returns false. + Returns true if positive leading is included into the line's height; + otherwise returns false. - By default, leading is not included. + By default, leading is not included. - \sa setLeadingIncluded() + \sa setLeadingIncluded() */ bool QTextLine::leadingIncluded() const { return eng->lines[i].leadingIncluded; } - /*! Returns the width of the line that is occupied by text. This is always \<= to width(), and is the minimum width that could be used @@ -1604,14 +1479,15 @@ qreal QTextLine::naturalTextWidth() const return eng->lines[i].textWidth.toReal(); } -/*! \since 4.7 - Returns the horizontal advance of the text. The advance of the text - is the distance from its position to the next position at which - text would naturally be drawn. +/*! + \since 4.7 + Returns the horizontal advance of the text. The advance of the text + is the distance from its position to the next position at which + text would naturally be drawn. - By adding the advance to the position of the text line and using this - as the position of a second text line, you will be able to position - the two lines side-by-side without gaps in-between. + By adding the advance to the position of the text line and using this + as the position of a second text line, you will be able to position + the two lines side-by-side without gaps in-between. */ qreal QTextLine::horizontalAdvance() const { @@ -1816,7 +1692,8 @@ static inline void addNextCluster(int &pos, int end, QScriptLine &line, int &gly ++line.length; } while (pos < end && logClusters[pos] == glyphPosition); do { // calculate the textWidth for the rest of the current cluster. - line.textWidth += glyphs.advances_x[glyphPosition] * !glyphs.attributes[glyphPosition].dontPrint; + if (!glyphs.attributes[glyphPosition].dontPrint) + line.textWidth += glyphs.advances_x[glyphPosition]; ++glyphPosition; } while (glyphPosition < current.num_glyphs && !glyphs.attributes[glyphPosition].clusterStart); @@ -1831,6 +1708,7 @@ void QTextLine::layout_helper(int maxGlyphs) { QScriptLine &line = eng->lines[i]; line.length = 0; + line.trailingSpaces = 0; line.textWidth = 0; line.hasTrailingSpaces = false; @@ -2053,9 +1931,10 @@ found: eng->maxWidth += lbh.spaceData.textWidth; if (eng->option.flags() & QTextOption::IncludeTrailingSpaces) line.textWidth += lbh.spaceData.textWidth; - line.length += lbh.spaceData.length; - if (lbh.spaceData.length) + if (lbh.spaceData.length) { + line.trailingSpaces = lbh.spaceData.length; line.hasTrailingSpaces = true; + } line.justified = false; line.gridfitted = false; @@ -2117,7 +1996,7 @@ int QTextLine::textLength() const && eng->block.isValid() && i == eng->lines.count()-1) { return eng->lines[i].length - 1; } - return eng->lines[i].length; + return eng->lines[i].length + eng->lines[i].trailingSpaces; } static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si, QTextItemInt &gf, QTextEngine *eng, @@ -2209,6 +2088,192 @@ static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const Q } +namespace { + struct GlyphInfo + { + GlyphInfo(const QGlyphLayout &layout, const QPointF &position, + const QTextItemInt::RenderFlags &renderFlags) + : glyphLayout(layout), itemPosition(position), flags(renderFlags) + { + } + + QGlyphLayout glyphLayout; + QPointF itemPosition; + QTextItem::RenderFlags flags; + }; +} + +/*! + \internal + + Returns the glyph indexes and positions for all glyphs in this QTextLine which reside in + QScriptItems that overlap with the range defined by \a from and \a length. The arguments + specify characters, relative to the text in the layout. Note that it is not possible to + use this function to retrieve a subset of the glyphs in a QScriptItem. + + \since 4.8 + + \sa QTextLayout::glyphRuns() +*/ +#if !defined(QT_NO_RAWFONT) +QList<QGlyphRun> QTextLine::glyphs(int from, int length) const +{ + const QScriptLine &line = eng->lines[i]; + + if (line.length == 0) + return QList<QGlyphRun>(); + + QHash<QFontEngine *, GlyphInfo> glyphLayoutHash; + + QTextLineItemIterator iterator(eng, i); + qreal y = line.y.toReal() + line.base().toReal(); + while (!iterator.atEnd()) { + QScriptItem &si = iterator.next(); + if (si.analysis.flags >= QScriptAnalysis::TabOrObject) + continue; + + QPointF pos(iterator.x.toReal(), y); + if (from >= 0 && length >= 0 && + (from >= si.position + eng->length(&si) || from + length <= si.position)) + continue; + + QFont font = eng->font(si); + + QTextItem::RenderFlags flags; + if (font.overline()) + flags |= QTextItem::Overline; + if (font.underline()) + flags |= QTextItem::Underline; + if (font.strikeOut()) + flags |= QTextItem::StrikeOut; + if (si.analysis.bidiLevel % 2) + flags |= QTextItem::RightToLeft; + + QGlyphLayout glyphLayout = eng->shapedGlyphs(&si).mid(iterator.glyphsStart, + iterator.glyphsEnd - iterator.glyphsStart); + + if (glyphLayout.numGlyphs > 0) { + QFontEngine *mainFontEngine = font.d->engineForScript(si.analysis.script); + if (mainFontEngine->type() == QFontEngine::Multi) { + QFontEngineMulti *multiFontEngine = static_cast<QFontEngineMulti *>(mainFontEngine); + int start = 0; + int end; + int which = glyphLayout.glyphs[0] >> 24; + for (end = 0; end < glyphLayout.numGlyphs; ++end) { + const int e = glyphLayout.glyphs[end] >> 24; + if (e == which) + continue; + + QGlyphLayout subLayout = glyphLayout.mid(start, end - start); + glyphLayoutHash.insertMulti(multiFontEngine->engine(which), + GlyphInfo(subLayout, pos, flags)); + for (int i = 0; i < subLayout.numGlyphs; i++) + pos += QPointF(subLayout.advances_x[i].toReal(), + subLayout.advances_y[i].toReal()); + + start = end; + which = e; + } + + QGlyphLayout subLayout = glyphLayout.mid(start, end - start); + glyphLayoutHash.insertMulti(multiFontEngine->engine(which), + GlyphInfo(subLayout, pos, flags)); + + } else { + glyphLayoutHash.insertMulti(mainFontEngine, + GlyphInfo(glyphLayout, pos, flags)); + } + } + } + + QHash<QPair<QFontEngine *, int>, QGlyphRun> glyphsHash; + + QList<QFontEngine *> keys = glyphLayoutHash.uniqueKeys(); + for (int i=0; i<keys.size(); ++i) { + QFontEngine *fontEngine = keys.at(i); + + // Make a font for this particular engine + QRawFont font; + QRawFontPrivate *fontD = QRawFontPrivate::get(font); + fontD->fontEngine = fontEngine; + fontD->fontEngine->ref.ref(); + +#if defined(Q_WS_WIN) + if (fontEngine->supportsSubPixelPositions()) + fontD->hintingPreference = QFont::PreferVerticalHinting; + else + fontD->hintingPreference = QFont::PreferFullHinting; +#elif defined(Q_WS_MAC) + fontD->hintingPreference = QFont::PreferNoHinting; +#elif !defined(QT_NO_FREETYPE) + if (fontEngine->type() == QFontEngine::Freetype) { + QFontEngineFT *freeTypeEngine = static_cast<QFontEngineFT *>(fontEngine); + switch (freeTypeEngine->defaultHintStyle()) { + case QFontEngineFT::HintNone: + fontD->hintingPreference = QFont::PreferNoHinting; + break; + case QFontEngineFT::HintLight: + fontD->hintingPreference = QFont::PreferVerticalHinting; + break; + case QFontEngineFT::HintMedium: + case QFontEngineFT::HintFull: + fontD->hintingPreference = QFont::PreferFullHinting; + break; + }; + } +#endif + + QList<GlyphInfo> glyphLayouts = glyphLayoutHash.values(fontEngine); + for (int j=0; j<glyphLayouts.size(); ++j) { + const QPointF &pos = glyphLayouts.at(j).itemPosition; + const QGlyphLayout &glyphLayout = glyphLayouts.at(j).glyphLayout; + const QTextItem::RenderFlags &flags = glyphLayouts.at(j).flags; + + QVarLengthArray<glyph_t> glyphsArray; + QVarLengthArray<QFixedPoint> positionsArray; + + fontEngine->getGlyphPositions(glyphLayout, QTransform(), flags, glyphsArray, + positionsArray); + Q_ASSERT(glyphsArray.size() == positionsArray.size()); + + QVector<quint32> glyphs; + QVector<QPointF> positions; + for (int i=0; i<glyphsArray.size(); ++i) { + glyphs.append(glyphsArray.at(i) & 0xffffff); + positions.append(positionsArray.at(i).toPointF() + pos); + } + + QGlyphRun glyphIndexes; + glyphIndexes.setGlyphIndexes(glyphs); + glyphIndexes.setPositions(positions); + + glyphIndexes.setOverline(flags.testFlag(QTextItem::Overline)); + glyphIndexes.setUnderline(flags.testFlag(QTextItem::Underline)); + glyphIndexes.setStrikeOut(flags.testFlag(QTextItem::StrikeOut)); + glyphIndexes.setRawFont(font); + + QPair<QFontEngine *, int> key(fontEngine, int(flags)); + if (!glyphsHash.contains(key)) { + glyphsHash.insert(key, glyphIndexes); + } else { + QGlyphRun &glyphRun = glyphsHash[key]; + + QVector<quint32> indexes = glyphRun.glyphIndexes(); + QVector<QPointF> positions = glyphRun.positions(); + + indexes += glyphIndexes.glyphIndexes(); + positions += glyphIndexes.positions(); + + glyphRun.setGlyphIndexes(indexes); + glyphRun.setPositions(positions); + } + } + } + + return glyphsHash.values(); +} +#endif // QT_NO_RAWFONT + /*! \fn void QTextLine::draw(QPainter *painter, const QPointF &position, const QTextLayout::FormatRange *selection) const @@ -2258,8 +2323,12 @@ void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatR QTextCharFormat format; if (eng->hasFormats() || selection) { - if (!suppressColors) - format = eng->format(&si); + format = eng->format(&si); + if (suppressColors) { + format.clearForeground(); + format.clearBackground(); + format.clearProperty(QTextFormat::TextUnderlineColor); + } if (selection) format.merge(selection->format); @@ -2405,21 +2474,20 @@ void QTextLine::draw(QPainter *p, const QPointF &pos, const QTextLayout::FormatR } /*! - \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const + \fn int QTextLine::cursorToX(int cursorPos, Edge edge) const - \overload + \overload */ - /*! - Converts the cursor position \a cursorPos to the corresponding x position - inside the line, taking account of the \a edge. + Converts the cursor position \a cursorPos to the corresponding x position + inside the line, taking account of the \a edge. - If \a cursorPos is not a valid cursor position, the nearest valid - cursor position will be used instead, and cpos will be modified to - point to this valid cursor position. + If \a cursorPos is not a valid cursor position, the nearest valid + cursor position will be used instead, and cpos will be modified to + point to this valid cursor position. - \sa xToCursor() + \sa xToCursor() */ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const { @@ -2427,9 +2495,10 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const eng->itemize(); const QScriptLine &line = eng->lines[i]; + bool lastLine = i >= eng->lines.size() - 1; QFixed x = line.x; - x += alignLine(eng, line); + x += eng->alignLine(line); if (!i && !eng->layoutData->items.size()) { *cursorPos = 0; @@ -2515,21 +2584,29 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const logClusters = eng->logClusters(si); glyphs = eng->shapedGlyphs(si); if (si->analysis.flags >= QScriptAnalysis::TabOrObject) { - if(pos == l) + if (pos == (reverse ? 0 : l)) x += si->width; } else { + bool rtl = eng->isRightToLeft(); + bool visual = eng->visualCursorMovement(); int end = qMin(lineEnd, si->position + l) - si->position; if (reverse) { int glyph_end = end == l ? si->num_glyphs : logClusters[end]; - for (int i = glyph_end - 1; i >= glyph_pos; i--) + int glyph_start = glyph_pos; + if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem))) + glyph_start++; + for (int i = glyph_end - 1; i >= glyph_start; i--) x += glyphs.effectiveAdvance(i); } else { int start = qMax(line.from - si->position, 0); int glyph_start = logClusters[start]; - for (int i = glyph_start; i < glyph_pos; i++) + int glyph_end = glyph_pos; + if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem)) + glyph_end--; + for (int i = glyph_start; i <= glyph_end; i++) x += glyphs.effectiveAdvance(i); } - x += offsetInLigature(logClusters, glyphs, pos, end, glyph_pos); + x += eng->offsetInLigature(si, pos, end, glyph_pos); } *cursorPos = pos + si->position; @@ -2537,17 +2614,19 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const } /*! - \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const + \fn int QTextLine::xToCursor(qreal x, CursorPosition cpos) const - Converts the x-coordinate \a x, to the nearest matching cursor - position, depending on the cursor position type, \a cpos. + Converts the x-coordinate \a x, to the nearest matching cursor + position, depending on the cursor position type, \a cpos. - \sa cursorToX() + \sa cursorToX() */ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const { QFixed x = QFixed::fromReal(_x); const QScriptLine &line = eng->lines[i]; + bool lastLine = i >= eng->lines.size() - 1; + int lineNum = i; if (!eng->layoutData) eng->itemize(); @@ -2565,7 +2644,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const return 0; x -= line.x; - x -= alignLine(eng, line); + x -= eng->alignLine(line); // qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos); QVarLengthArray<int> visualOrder(nItems); @@ -2574,6 +2653,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel; QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data()); + bool visual = eng->visualCursorMovement(); if (x <= 0) { // left of first item int item = visualOrder[0]+firstItem; @@ -2590,8 +2670,13 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const || (line.justified && x < line.width)) { // has to be in one of the runs QFixed pos; + bool rtl = eng->isRightToLeft(); eng->shapeLine(line); + QVector<int> insertionPoints; + if (visual && rtl) + eng->insertionPointsForLine(lineNum, insertionPoints); + int nchars = 0; for (int i = 0; i < nItems; ++i) { int item = visualOrder[i]+firstItem; QScriptItem &si = eng->layoutData->items[item]; @@ -2621,6 +2706,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const if (pos + item_width < x) { pos += item_width; + nchars += end; continue; } // qDebug(" inside run"); @@ -2668,30 +2754,65 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const } else { QFixed dist = INT_MAX/256; if (si.analysis.bidiLevel % 2) { - pos += item_width; - while (gs <= ge) { - if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) { - glyph_pos = gs; - edge = pos; - dist = qAbs(x-pos); + if (!visual || rtl || (lastLine && i == nItems - 1)) { + pos += item_width; + while (gs <= ge) { + if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) { + glyph_pos = gs; + edge = pos; + dist = qAbs(x-pos); + } + pos -= glyphs.effectiveAdvance(gs); + ++gs; + } + } else { + while (ge >= gs) { + if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) { + glyph_pos = ge; + edge = pos; + dist = qAbs(x-pos); + } + pos += glyphs.effectiveAdvance(ge); + --ge; } - pos -= glyphs.effectiveAdvance(gs); - ++gs; } } else { - while (gs <= ge) { - if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) { - glyph_pos = gs; - edge = pos; - dist = qAbs(x-pos); + if (!visual || !rtl || (lastLine && i == 0)) { + while (gs <= ge) { + if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) { + glyph_pos = gs; + edge = pos; + dist = qAbs(x-pos); + } + pos += glyphs.effectiveAdvance(gs); + ++gs; } - pos += glyphs.effectiveAdvance(gs); - ++gs; + } else { + QFixed oldPos = pos; + while (gs <= ge) { + pos += glyphs.effectiveAdvance(gs); + if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) { + glyph_pos = gs; + edge = pos; + dist = qAbs(x-pos); + } + ++gs; + } + pos = oldPos; } } - if (qAbs(x-pos) < dist) + if (qAbs(x-pos) < dist) { + if (visual) { + if (!rtl && i < nItems - 1) { + nchars += end; + continue; + } + if (rtl && nchars > 0) + return insertionPoints[lastLine ? nchars : nchars - 1]; + } return eng->positionInLigature(&si, end, x, pos, -1, cpos == QTextLine::CursorOnCharacter); + } } Q_ASSERT(glyph_pos != -1); return eng->positionInLigature(&si, end, x, edge, glyph_pos, diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h index 5486edf..caff299 100644 --- a/src/gui/text/qtextlayout.h +++ b/src/gui/text/qtextlayout.h @@ -49,6 +49,8 @@ #include <QtCore/qobject.h> #include <QtGui/qevent.h> #include <QtGui/qtextformat.h> +#include <QtGui/qglyphrun.h> +#include <QtGui/qtextcursor.h> QT_BEGIN_HEADER @@ -135,6 +137,9 @@ public: void setCacheEnabled(bool enable); bool cacheEnabled() const; + void setCursorMoveStyle(Qt::CursorMoveStyle style); + Qt::CursorMoveStyle cursorMoveStyle() const; + void beginLayout(); void endLayout(); void clearLayout(); @@ -152,6 +157,8 @@ public: bool isValidCursorPosition(int pos) const; int nextCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const; int previousCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const; + int leftCursorPosition(int oldPos) const; + int rightCursorPosition(int oldPos) const; void draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections = QVector<FormatRange>(), const QRectF &clip = QRectF()) const; @@ -166,6 +173,10 @@ public: qreal minimumWidth() const; qreal maximumWidth() const; +#if !defined(QT_NO_RAWFONT) + QList<QGlyphRun> glyphRuns() const; +#endif + QTextEngine *engine() const { return d; } void setFlags(int flags); private: @@ -236,7 +247,13 @@ public: private: QTextLine(int line, QTextEngine *e) : i(line), eng(e) {} void layout_helper(int numGlyphs); + +#if !defined(QT_NO_RAWFONT) + QList<QGlyphRun> glyphs(int from, int length) const; +#endif + friend class QTextLayout; + friend class QTextFragment; int i; QTextEngine *eng; }; diff --git a/src/gui/text/qtextlist.cpp b/src/gui/text/qtextlist.cpp index f207f54..92e509d 100644 --- a/src/gui/text/qtextlist.cpp +++ b/src/gui/text/qtextlist.cpp @@ -191,6 +191,13 @@ QString QTextList::itemText(const QTextBlock &blockIt) const QString result; const int style = format().style(); + QString numberPrefix; + QString numberSuffix = QLatin1String("."); + + if (format().hasProperty(QTextFormat::ListNumberPrefix)) + numberPrefix = format().numberPrefix(); + if (format().hasProperty(QTextFormat::ListNumberSuffix)) + numberSuffix = format().numberSuffix(); switch (style) { case QTextListFormat::ListDecimal: @@ -232,7 +239,7 @@ QString QTextList::itemText(const QTextBlock &blockIt) const if (q > 0) { int startDigit = i + (i+3)/4; int numDigits; - if (i % 4) { + if (i % 4) { // c[i] == 4|5|9|40|50|90|400|500|900 if ((i-2) % 4) { // c[i] == 4|9|40|90|400|900 => with subtraction (IV, IX, XL, XC, ...) @@ -263,8 +270,9 @@ QString QTextList::itemText(const QTextBlock &blockIt) const Q_ASSERT(false); } if (blockIt.textDirection() == Qt::RightToLeft) - return result.prepend(QLatin1Char('.')); - return result + QLatin1Char('.'); + return numberSuffix + result + numberPrefix; + else + return numberPrefix + result + numberSuffix; } /*! diff --git a/src/gui/text/qtextobject.cpp b/src/gui/text/qtextobject.cpp index 14fc117..c3f37d8 100644 --- a/src/gui/text/qtextobject.cpp +++ b/src/gui/text/qtextobject.cpp @@ -1655,6 +1655,37 @@ QTextBlock::iterator &QTextBlock::iterator::operator--() than the \a other text fragment; otherwise returns false. */ +/*! + Returns the glyphs of this text fragment. The positions of the glyphs are + relative to the position of the QTextBlock's layout. + + \sa QGlyphRun, QTextBlock::layout(), QTextLayout::position(), QPainter::drawGlyphRun() +*/ +#if !defined(QT_NO_RAWFONT) +QList<QGlyphRun> QTextFragment::glyphRuns() const +{ + if (!p || !n) + return QList<QGlyphRun>(); + + int pos = position(); + int len = length(); + if (len == 0) + return QList<QGlyphRun>(); + + int blockNode = p->blockMap().findNode(pos); + + const QTextBlockData *blockData = p->blockMap().fragment(blockNode); + QTextLayout *layout = blockData->layout; + + QList<QGlyphRun> ret; + for (int i=0; i<layout->lineCount(); ++i) { + QTextLine textLine = layout->lineAt(i); + ret += textLine.glyphs(pos, len); + } + + return ret; +} +#endif // QT_NO_RAWFONT /*! Returns the position of this text fragment in the document. diff --git a/src/gui/text/qtextobject.h b/src/gui/text/qtextobject.h index 0e8ad1e..dae1f92 100644 --- a/src/gui/text/qtextobject.h +++ b/src/gui/text/qtextobject.h @@ -44,6 +44,7 @@ #include <QtCore/qobject.h> #include <QtGui/qtextformat.h> +#include <QtGui/qglyphrun.h> QT_BEGIN_HEADER @@ -315,6 +316,10 @@ public: int charFormatIndex() const; QString text() const; +#if !defined(QT_NO_RAWFONT) + QList<QGlyphRun> glyphRuns() const; +#endif + private: const QTextDocumentPrivate *p; int n; diff --git a/src/gui/text/qtextodfwriter.cpp b/src/gui/text/qtextodfwriter.cpp index 6dc7d03..2addc0f 100644 --- a/src/gui/text/qtextodfwriter.cpp +++ b/src/gui/text/qtextodfwriter.cpp @@ -124,6 +124,7 @@ public: manifestWriter.writeNamespace(manifestNS, QString::fromLatin1("manifest")); manifestWriter.writeStartDocument(); manifestWriter.writeStartElement(manifestNS, QString::fromLatin1("manifest")); + manifestWriter.writeAttribute(manifestNS, QString::fromLatin1("version"), QString::fromLatin1("1.2")); addFile(QString::fromLatin1("/"), QString::fromLatin1("application/vnd.oasis.opendocument.text")); addFile(QString::fromLatin1("content.xml"), QString::fromLatin1("text/xml")); } @@ -590,6 +591,7 @@ void QTextOdfWriter::writeCharacterFormat(QXmlStreamWriter &writer, QTextCharFor QString value; switch (format.verticalAlignment()) { case QTextCharFormat::AlignMiddle: + case QTextCharFormat::AlignBaseline: case QTextCharFormat::AlignNormal: value = QString::fromLatin1("0%"); break; case QTextCharFormat::AlignSuperScript: value = QString::fromLatin1("super"); break; case QTextCharFormat::AlignSubScript: value = QString::fromLatin1("sub"); break; @@ -636,7 +638,15 @@ void QTextOdfWriter::writeListFormat(QXmlStreamWriter &writer, QTextListFormat f || style == QTextListFormat::ListUpperRoman) { writer.writeStartElement(textNS, QString::fromLatin1("list-level-style-number")); writer.writeAttribute(styleNS, QString::fromLatin1("num-format"), bulletChar(style)); - writer.writeAttribute(styleNS, QString::fromLatin1("num-suffix"), QString::fromLatin1(".")); + + if (format.hasProperty(QTextFormat::ListNumberSuffix)) + writer.writeAttribute(styleNS, QString::fromLatin1("num-suffix"), format.numberSuffix()); + else + writer.writeAttribute(styleNS, QString::fromLatin1("num-suffix"), QString::fromLatin1(".")); + + if (format.hasProperty(QTextFormat::ListNumberPrefix)) + writer.writeAttribute(styleNS, QString::fromLatin1("num-prefix"), format.numberPrefix()); + } else { writer.writeStartElement(textNS, QString::fromLatin1("list-level-style-bullet")); writer.writeAttribute(textNS, QString::fromLatin1("bullet-char"), bulletChar(style)); @@ -778,6 +788,7 @@ bool QTextOdfWriter::writeAll() writer.writeNamespace(svgNS, QString::fromLatin1("svg")); writer.writeStartDocument(); writer.writeStartElement(officeNS, QString::fromLatin1("document-content")); + writer.writeAttribute(officeNS, QString::fromLatin1("version"), QString::fromLatin1("1.2")); // add fragments. (for character formats) QTextDocumentPrivate::FragmentIterator fragIt = m_document->docHandle()->begin(); diff --git a/src/gui/text/qzip.cpp b/src/gui/text/qzip.cpp index 6470f8a..0ffcade 100644 --- a/src/gui/text/qzip.cpp +++ b/src/gui/text/qzip.cpp @@ -572,7 +572,7 @@ void QZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const "directory", "file ", "symlink " }; - ZDEBUG() << "adding" << entryTypes[type] <<":" << fileName.toUtf8().data() << (type == 2 ? (" -> " + contents).constData() : ""); + ZDEBUG() << "adding" << entryTypes[type] <<":" << fileName.toUtf8().data() << (type == 2 ? QByteArray(" -> " + contents).constData() : ""); #endif if (! (device->isOpen() || device->open(QIODevice::WriteOnly))) { diff --git a/src/gui/text/qzipwriter_p.h b/src/gui/text/qzipwriter_p.h index 18d286f..1ba93b8 100644 --- a/src/gui/text/qzipwriter_p.h +++ b/src/gui/text/qzipwriter_p.h @@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE class QZipWriterPrivate; -class Q_AUTOTEST_EXPORT QZipWriter +class Q_GUI_EXPORT QZipWriter { public: QZipWriter(const QString &fileName, QIODevice::OpenMode mode = (QIODevice::WriteOnly | QIODevice::Truncate) ); diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri index af8daa5..1cfacf7 100644 --- a/src/gui/text/text.pri +++ b/src/gui/text/text.pri @@ -25,7 +25,7 @@ HEADERS += \ text/qabstracttextdocumentlayout.h \ text/qtextdocumentlayout_p.h \ text/qtextcursor.h \ - text/qtextcursor_p.h \ + text/qtextcursor_p.h \ text/qtextdocumentfragment.h \ text/qtextdocumentfragment_p.h \ text/qtextimagehandler_p.h \ @@ -39,7 +39,11 @@ HEADERS += \ text/qzipwriter_p.h \ text/qtextodfwriter_p.h \ text/qstatictext_p.h \ - text/qstatictext.h + text/qstatictext.h \ + text/qrawfont.h \ + text/qrawfont_p.h \ + text/qglyphrun.h \ + text/qglyphrun_p.h SOURCES += \ text/qfont.cpp \ @@ -69,15 +73,24 @@ SOURCES += \ text/qcssparser.cpp \ text/qzip.cpp \ text/qtextodfwriter.cpp \ - text/qstatictext.cpp + text/qstatictext.cpp \ + text/qrawfont.cpp \ + text/qglyphrun.cpp win32 { SOURCES += \ text/qfont_win.cpp \ - text/qfontengine_win.cpp + text/qfontengine_win.cpp \ + text/qrawfont_win.cpp HEADERS += text/qfontengine_win_p.h } +contains(QT_CONFIG, directwrite) { + LIBS_PRIVATE += -ldwrite + HEADERS += text/qfontenginedirectwrite_p.h + SOURCES += text/qfontenginedirectwrite.cpp +} + unix:x11 { HEADERS += \ text/qfontengine_x11_p.h \ @@ -86,13 +99,27 @@ unix:x11 { SOURCES += \ text/qfont_x11.cpp \ text/qfontengine_x11.cpp \ - text/qfontengine_ft.cpp + text/qfontengine_ft.cpp \ + text/qrawfont_ft.cpp } -!embedded:!x11:mac { +!embedded:!qpa:!x11:mac { + HEADERS += \ + text/qfontengine_mac_p.h SOURCES += \ - text/qfont_mac.cpp - OBJECTIVE_SOURCES += text/qfontengine_mac.mm + text/qfont_mac.cpp \ + text/qrawfont_mac.cpp + OBJECTIVE_SOURCES += \ + text/qfontengine_mac.mm +} +!embedded:!x11:mac { + OBJECTIVE_HEADERS += \ + text/qfontengine_coretext_p.h + OBJECTIVE_SOURCES += \ + text/qfontengine_coretext.mm + contains(QT_CONFIG, harfbuzz) { + DEFINES += QT_ENABLE_HARFBUZZ_FOR_MAC + } } embedded { @@ -101,7 +128,8 @@ embedded { text/qfontengine_qws.cpp \ text/qfontengine_ft.cpp \ text/qfontengine_qpf.cpp \ - text/qabstractfontengine_qws.cpp + text/qabstractfontengine_qws.cpp \ + text/qrawfont_ft.cpp HEADERS += \ text/qfontengine_ft_p.h \ text/qfontengine_qpf_p.h \ @@ -110,12 +138,27 @@ embedded { DEFINES += QT_NO_FONTCONFIG } +qpa { + SOURCES += \ + text/qfont_qpa.cpp \ + text/qfontengine_qpa.cpp \ + text/qplatformfontdatabase_qpa.cpp \ + text/qrawfont_qpa.cpp + + HEADERS += \ + text/qplatformfontdatabase_qpa.h + + DEFINES += QT_NO_FONTCONFIG + DEFINES += QT_NO_FREETYPE +} + symbian { SOURCES += \ text/qfont_s60.cpp contains(QT_CONFIG, freetype) { SOURCES += \ - text/qfontengine_ft.cpp + text/qfontengine_ft.cpp \ + text/qrawfont_ft.cpp HEADERS += \ text/qfontengine_ft_p.h DEFINES += \ @@ -125,10 +168,11 @@ symbian { text/qfontengine_s60.cpp HEADERS += \ text/qfontengine_s60_p.h - LIBS += -lfntstr -lecom } + LIBS += -lfntstr -lecom } +!qpa { contains(QT_CONFIG, freetype) { SOURCES += \ ../3rdparty/freetype/src/base/ftbase.c \ @@ -202,6 +246,7 @@ contains(QT_CONFIG, freetype) { contains(QT_CONFIG, fontconfig) { CONFIG += opentype } +}#!qpa DEFINES += QT_NO_OPENTYPE INCLUDEPATH += ../3rdparty/harfbuzz/src diff --git a/src/gui/util/qcompleter.cpp b/src/gui/util/qcompleter.cpp index 505d09d..07bfac7 100644 --- a/src/gui/util/qcompleter.cpp +++ b/src/gui/util/qcompleter.cpp @@ -220,7 +220,7 @@ QModelIndex QCompletionModel::mapToSource(const QModelIndex& index) const { Q_D(const QCompletionModel); if (!index.isValid()) - return QModelIndex(); + return engine->curParent; int row; QModelIndex parent = engine->curParent; @@ -926,7 +926,7 @@ void QCompleterPrivate::_q_fileSystemModelDirectoryLoaded(const QString &path) // If we hide the popup because there was no match because the model was not loaded yet, // we re-start the completion when we get the results if (hiddenBecauseNoMatch - && prefix.startsWith(path) && prefix != (path + '/') + && prefix.startsWith(path) && prefix != (path + QLatin1Char('/')) && widget) { q->complete(); } diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp index 129397d..e79819e 100644 --- a/src/gui/util/qdesktopservices.cpp +++ b/src/gui/util/qdesktopservices.cpp @@ -45,7 +45,7 @@ #include <qdebug.h> -#if defined(Q_WS_QWS) +#if defined(Q_WS_QWS) || defined(Q_WS_QPA) #include "qdesktopservices_qws.cpp" #elif defined(Q_WS_X11) #include "qdesktopservices_x11.cpp" diff --git a/src/gui/util/qdesktopservices_mac.cpp b/src/gui/util/qdesktopservices_mac.cpp index 8b9c4a3..d1711b0 100644 --- a/src/gui/util/qdesktopservices_mac.cpp +++ b/src/gui/util/qdesktopservices_mac.cpp @@ -49,6 +49,8 @@ #include <private/qcore_mac_p.h> #include <qcoreapplication.h> +#include <ApplicationServices/ApplicationServices.h> + QT_BEGIN_NAMESPACE /* diff --git a/src/gui/util/qdesktopservices_s60.cpp b/src/gui/util/qdesktopservices_s60.cpp index ae203cd..7a53224 100644 --- a/src/gui/util/qdesktopservices_s60.cpp +++ b/src/gui/util/qdesktopservices_s60.cpp @@ -70,6 +70,10 @@ #include <schemehandler.h> #endif +#ifdef Q_WS_S60 +#include <CDirectoryLocalizer.h> // CDirectoryLocalizer +#endif + QT_BEGIN_NAMESPACE _LIT(KCacheSubDir, "Cache\\"); @@ -444,18 +448,31 @@ static QString defaultLocalizedDirectoryName(QString&) QString QDesktopServices::displayName(StandardLocation type) { - static LocalizerFunc ptrLocalizerFunc = NULL; - - if (!ptrLocalizerFunc) { - ptrLocalizerFunc = reinterpret_cast<LocalizerFunc> - (qt_resolveS60PluginFunc(S60Plugin_LocalizedDirectoryName)); - if (!ptrLocalizerFunc) - ptrLocalizerFunc = &defaultLocalizedDirectoryName; - } + QString ret; +#ifdef Q_WS_S60 QString rawPath = storageLocation(type); - return ptrLocalizerFunc(rawPath); -} + TRAPD(err, + QT_TRYCATCH_LEAVING( + CDirectoryLocalizer* localizer = CDirectoryLocalizer::NewL(); + CleanupStack::PushL(localizer); + localizer->SetFullPath(qt_QString2TPtrC(QDir::toNativeSeparators(rawPath))); + if (localizer->IsLocalized()) { + TPtrC locName(localizer->LocalizedName()); + ret = qt_TDesC2QString(locName); + } + CleanupStack::PopAndDestroy(localizer); + ) + ) + + if (err != KErrNone) + ret = QString(); +#else + qWarning("QDesktopServices::displayName() not implemented for this platform version"); +#endif + + return ret; +} QT_END_NAMESPACE diff --git a/src/gui/util/qflickgesture_p.h b/src/gui/util/qflickgesture_p.h new file mode 100644 index 0000000..e095cf5 --- /dev/null +++ b/src/gui/util/qflickgesture_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFLICKGESTURE_P_H +#define QFLICKGESTURE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qevent.h" +#include "qgesturerecognizer.h" +#include "private/qgesture_p.h" +#include "qscroller.h" +#include "qscopedpointer.h" + +#ifndef QT_NO_GESTURES + +QT_BEGIN_NAMESPACE + +class QFlickGesturePrivate; +class QGraphicsItem; + +class Q_GUI_EXPORT QFlickGesture : public QGesture +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QFlickGesture) + +public: + QFlickGesture(QObject *receiver, Qt::MouseButton button, QObject *parent = 0); + ~QFlickGesture(); + + friend class QFlickGestureRecognizer; +}; + +class PressDelayHandler; + +class QFlickGesturePrivate : public QGesturePrivate +{ + Q_DECLARE_PUBLIC(QFlickGesture) +public: + QFlickGesturePrivate(); + + QPointer<QObject> receiver; + QScroller *receiverScroller; + Qt::MouseButton button; // NoButton == Touch + bool macIgnoreWheel; + static PressDelayHandler *pressDelayHandler; +}; + +class QFlickGestureRecognizer : public QGestureRecognizer +{ +public: + QFlickGestureRecognizer(Qt::MouseButton button); + + QGesture *create(QObject *target); + QGestureRecognizer::Result recognize(QGesture *state, QObject *watched, QEvent *event); + void reset(QGesture *state); + +private: + Qt::MouseButton button; // NoButton == Touch +}; + +QT_END_NAMESPACE + +#endif // QT_NO_GESTURES + +#endif // QFLICKGESTURE_P_H diff --git a/src/gui/util/qsystemtrayicon_win.cpp b/src/gui/util/qsystemtrayicon_win.cpp index 338478a..7a57284 100644 --- a/src/gui/util/qsystemtrayicon_win.cpp +++ b/src/gui/util/qsystemtrayicon_win.cpp @@ -305,6 +305,7 @@ bool QSystemTrayIconSys::winEvent( MSG *m, long *result ) case WM_CONTEXTMENU: if (q->contextMenu()) { q->contextMenu()->popup(gpos); + q->contextMenu()->activateWindow(); } emit q->activated(QSystemTrayIcon::Context); break; diff --git a/src/gui/util/qundogroup.cpp b/src/gui/util/qundogroup.cpp index 59034b3..4f83e3a 100644 --- a/src/gui/util/qundogroup.cpp +++ b/src/gui/util/qundogroup.cpp @@ -375,15 +375,18 @@ bool QUndoGroup::isClean() const for undo, if the group is empty or if none of the stacks are active, this action will be disabled. - If \a prefix is empty, the default prefix "Undo" is used. + If \a prefix is empty, the default template "Undo %1" is used instead of prefix. + Before Qt 4.8, the prefix "Undo" was used by default. \sa createRedoAction() canUndo() QUndoCommand::text() */ QAction *QUndoGroup::createUndoAction(QObject *parent, const QString &prefix) const { - QString pref = prefix.isEmpty() ? tr("Undo") : prefix; - QUndoAction *result = new QUndoAction(pref, parent); + QUndoAction *result = new QUndoAction(prefix, parent); + if (prefix.isEmpty()) + result->setTextFormat(tr("Undo %1"), tr("Undo", "Default text for undo action")); + result->setEnabled(canUndo()); result->setPrefixedText(undoText()); connect(this, SIGNAL(canUndoChanged(bool)), @@ -403,15 +406,18 @@ QAction *QUndoGroup::createUndoAction(QObject *parent, const QString &prefix) co for redo, if the group is empty or if none of the stacks are active, this action will be disabled. - If \a prefix is empty, the default prefix "Undo" is used. + If \a prefix is empty, the default template "Redo %1" is used instead of prefix. + Before Qt 4.8, the prefix "Redo" was used by default. \sa createUndoAction() canRedo() QUndoCommand::text() */ QAction *QUndoGroup::createRedoAction(QObject *parent, const QString &prefix) const { - QString pref = prefix.isEmpty() ? tr("Redo") : prefix; - QUndoAction *result = new QUndoAction(pref, parent); + QUndoAction *result = new QUndoAction(prefix, parent); + if (prefix.isEmpty()) + result->setTextFormat(tr("Redo %1"), tr("Redo", "Default text for redo action")); + result->setEnabled(canRedo()); result->setPrefixedText(redoText()); connect(this, SIGNAL(canRedoChanged(bool)), diff --git a/src/gui/util/qundostack.cpp b/src/gui/util/qundostack.cpp index e5f2aef..59e73e6 100644 --- a/src/gui/util/qundostack.cpp +++ b/src/gui/util/qundostack.cpp @@ -114,7 +114,7 @@ QUndoCommand::QUndoCommand(const QString &text, QUndoCommand *parent) d = new QUndoCommandPrivate; if (parent != 0) parent->d->child_list.append(this); - d->text = text; + setText(text); } /*! @@ -230,10 +230,9 @@ void QUndoCommand::undo() Returns a short text string describing what this command does; for example, "insert text". - The text is used when the text properties of the stack's undo and redo - actions are updated. + The text is used for names of items in QUndoView. - \sa setText(), QUndoStack::createUndoAction(), QUndoStack::createRedoAction() + \sa actionText(), setText(), QUndoStack::createUndoAction(), QUndoStack::createRedoAction() */ QString QUndoCommand::text() const @@ -242,17 +241,47 @@ QString QUndoCommand::text() const } /*! + \since 4.8 + + Returns a short text string describing what this command does; for example, + "insert text". + + The text is used when the text properties of the stack's undo and redo + actions are updated. + + \sa text(), setText(), QUndoStack::createUndoAction(), QUndoStack::createRedoAction() +*/ + +QString QUndoCommand::actionText() const +{ + return d->actionText; +} + +/*! Sets the command's text to be the \a text specified. The specified text should be a short user-readable string describing what this command does. - \sa text() QUndoStack::createUndoAction() QUndoStack::createRedoAction() + If you need to have two different strings for text() and actionText(), separate + them with "\n" and pass into this function. Even if you do not use this feature + for English strings during development, you can still let translators use two + different strings in order to match specific languages' needs. + The described feature and the function actionText() are available since Qt 4.8. + + \sa text() actionText() QUndoStack::createUndoAction() QUndoStack::createRedoAction() */ void QUndoCommand::setText(const QString &text) { - d->text = text; + int cdpos = text.indexOf(QLatin1Char('\n')); + if (cdpos > 0) { + d->text = text.left(cdpos); + d->actionText = text.mid(cdpos + 1); + } else { + d->text = text; + d->actionText = text; + } } /*! @@ -374,11 +403,24 @@ QUndoAction::QUndoAction(const QString &prefix, QObject *parent) void QUndoAction::setPrefixedText(const QString &text) { - QString s = m_prefix; - if (!m_prefix.isEmpty() && !text.isEmpty()) - s.append(QLatin1Char(' ')); - s.append(text); - setText(s); + if (m_defaultText.isEmpty()) { + QString s = m_prefix; + if (!m_prefix.isEmpty() && !text.isEmpty()) + s.append(QLatin1Char(' ')); + s.append(text); + setText(s); + } else { + if (text.isEmpty()) + setText(m_defaultText); + else + setText(m_prefix.arg(text)); + } +} + +void QUndoAction::setTextFormat(const QString &textFormat, const QString &defaultText) +{ + m_prefix = textFormat; + m_defaultText = defaultText; } #endif // QT_NO_ACTION @@ -783,7 +825,7 @@ bool QUndoStack::canRedo() const /*! Returns the text of the command which will be undone in the next call to undo(). - \sa QUndoCommand::text() redoText() + \sa QUndoCommand::actionText() redoText() */ QString QUndoStack::undoText() const @@ -792,14 +834,14 @@ QString QUndoStack::undoText() const if (!d->macro_stack.isEmpty()) return QString(); if (d->index > 0) - return d->command_list.at(d->index - 1)->text(); + return d->command_list.at(d->index - 1)->actionText(); return QString(); } /*! Returns the text of the command which will be redone in the next call to redo(). - \sa QUndoCommand::text() undoText() + \sa QUndoCommand::actionText() undoText() */ QString QUndoStack::redoText() const @@ -808,7 +850,7 @@ QString QUndoStack::redoText() const if (!d->macro_stack.isEmpty()) return QString(); if (d->index < d->command_list.size()) - return d->command_list.at(d->index)->text(); + return d->command_list.at(d->index)->actionText(); return QString(); } @@ -822,15 +864,18 @@ QString QUndoStack::redoText() const prefixed by the specified \a prefix. If there is no command available for undo, this action will be disabled. - If \a prefix is empty, the default prefix "Undo" is used. + If \a prefix is empty, the default template "Undo %1" is used instead of prefix. + Before Qt 4.8, the prefix "Undo" was used by default. \sa createRedoAction(), canUndo(), QUndoCommand::text() */ QAction *QUndoStack::createUndoAction(QObject *parent, const QString &prefix) const { - QString pref = prefix.isEmpty() ? tr("Undo") : prefix; - QUndoAction *result = new QUndoAction(pref, parent); + QUndoAction *result = new QUndoAction(prefix, parent); + if (prefix.isEmpty()) + result->setTextFormat(tr("Undo %1"), tr("Undo", "Default text for undo action")); + result->setEnabled(canUndo()); result->setPrefixedText(undoText()); connect(this, SIGNAL(canUndoChanged(bool)), @@ -849,15 +894,18 @@ QAction *QUndoStack::createUndoAction(QObject *parent, const QString &prefix) co prefixed by the specified \a prefix. If there is no command available for redo, this action will be disabled. - If \a prefix is empty, the default prefix "Redo" is used. + If \a prefix is empty, the default template "Redo %1" is used instead of prefix. + Before Qt 4.8, the prefix "Redo" was used by default. \sa createUndoAction(), canRedo(), QUndoCommand::text() */ QAction *QUndoStack::createRedoAction(QObject *parent, const QString &prefix) const { - QString pref = prefix.isEmpty() ? tr("Redo") : prefix; - QUndoAction *result = new QUndoAction(pref, parent); + QUndoAction *result = new QUndoAction(prefix, parent); + if (prefix.isEmpty()) + result->setTextFormat(tr("Redo %1"), tr("Redo", "Default text for redo action")); + result->setEnabled(canRedo()); result->setPrefixedText(redoText()); connect(this, SIGNAL(canRedoChanged(bool)), diff --git a/src/gui/util/qundostack.h b/src/gui/util/qundostack.h index 1b4b927..2cbbc0a 100644 --- a/src/gui/util/qundostack.h +++ b/src/gui/util/qundostack.h @@ -70,6 +70,7 @@ public: virtual void redo(); QString text() const; + QString actionText() const; void setText(const QString &text); virtual int id() const; diff --git a/src/gui/util/qundostack_p.h b/src/gui/util/qundostack_p.h index e1c69e0..7162e19 100644 --- a/src/gui/util/qundostack_p.h +++ b/src/gui/util/qundostack_p.h @@ -70,6 +70,7 @@ public: QUndoCommandPrivate() : id(-1) {} QList<QUndoCommand*> child_list; QString text; + QString actionText; int id; }; @@ -98,10 +99,12 @@ class QUndoAction : public QAction Q_OBJECT public: QUndoAction(const QString &prefix, QObject *parent = 0); + void setTextFormat(const QString &textFormat, const QString &defaultText); public Q_SLOTS: void setPrefixedText(const QString &text); private: QString m_prefix; + QString m_defaultText; }; #endif // QT_NO_ACTION diff --git a/src/gui/util/util.pri b/src/gui/util/util.pri index d1c4ff8..7395604 100644 --- a/src/gui/util/util.pri +++ b/src/gui/util/util.pri @@ -33,12 +33,12 @@ unix:x11 { util/qsystemtrayicon_x11.cpp } -embedded { +embedded|qpa { SOURCES += \ util/qsystemtrayicon_qws.cpp } -!embedded:!x11:mac { +!embedded:!qpa:!x11:mac { OBJECTIVE_SOURCES += util/qsystemtrayicon_mac.mm } @@ -56,4 +56,10 @@ symbian { } else { DEFINES += USE_SCHEMEHANDLER } -}
\ No newline at end of file + + contains(CONFIG, is_using_gnupoc) { + LIBS += -ldirectorylocalizer + } else { + LIBS += -lDirectoryLocalizer + } +} diff --git a/src/gui/widgets/qabstractbutton.cpp b/src/gui/widgets/qabstractbutton.cpp index 354677d..57e517a 100644 --- a/src/gui/widgets/qabstractbutton.cpp +++ b/src/gui/widgets/qabstractbutton.cpp @@ -319,7 +319,7 @@ QList<QAbstractButton *>QAbstractButtonPrivate::queryButtonList() const return group->d_func()->buttonList; #endif - QList<QAbstractButton*>candidates = qFindChildren<QAbstractButton *>(parent); + QList<QAbstractButton*>candidates = parent->findChildren<QAbstractButton *>(); if (autoExclusive) { for (int i = candidates.count() - 1; i >= 0; --i) { QAbstractButton *candidate = candidates.at(i); diff --git a/src/gui/widgets/qabstractplatformmenubar_p.h b/src/gui/widgets/qabstractplatformmenubar_p.h new file mode 100644 index 0000000..10e6fdc --- /dev/null +++ b/src/gui/widgets/qabstractplatformmenubar_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QABSTRACTPLATFORMMENUBAR_P_H +#define QABSTRACTPLATFORMMENUBAR_P_H + +#include <qfactoryinterface.h> +#include <qglobal.h> +#include <qplugin.h> + +#ifndef QT_NO_MENUBAR + +QT_BEGIN_NAMESPACE + +class QAction; +class QActionEvent; +class QEvent; +class QMenuBar; +class QObject; +class QWidget; + +class QAbstractPlatformMenuBar; + +struct QPlatformMenuBarFactoryInterface : public QFactoryInterface +{ + virtual QAbstractPlatformMenuBar *create() = 0; +}; + +#define QPlatformMenuBarFactoryInterface_iid "com.nokia.qt.QPlatformMenuBarFactoryInterface" +Q_DECLARE_INTERFACE(QPlatformMenuBarFactoryInterface, QPlatformMenuBarFactoryInterface_iid) + +/*! + The platform-specific implementation of a menubar +*/ +class QAbstractPlatformMenuBar +{ +public: + virtual ~QAbstractPlatformMenuBar() {} + + virtual void init(QMenuBar *) = 0; + + virtual void setVisible(bool visible) = 0; + + virtual void actionEvent(QActionEvent *) = 0; + + virtual void handleReparent(QWidget *oldParent, QWidget *newParent, QWidget *oldWindow, QWidget *newWindow) = 0; + + virtual bool allowCornerWidgets() const = 0; + + virtual void popupAction(QAction *) = 0; + + virtual void setNativeMenuBar(bool) = 0; + + virtual bool isNativeMenuBar() const = 0; + + /*! + Return true if the native menubar is capable of listening to the + shortcut keys. If false is returned, QMenuBar will trigger actions on + shortcut itself. + */ + virtual bool shortcutsHandledByNativeMenuBar() const = 0; + + virtual bool menuBarEventFilter(QObject *, QEvent *event) = 0; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_MENUBAR + +#endif // QABSTRACTPLATFORMMENUBAR_P_H diff --git a/src/gui/widgets/qabstractslider_p.h b/src/gui/widgets/qabstractslider_p.h index d32335f..fdbc0f9 100644 --- a/src/gui/widgets/qabstractslider_p.h +++ b/src/gui/widgets/qabstractslider_p.h @@ -117,7 +117,7 @@ public: ; } - inline int bound(int val) const { return qMax(minimum, qMin(maximum, val)); } + virtual int bound(int val) const { return qMax(minimum, qMin(maximum, val)); } inline int overflowSafeAdd(int add) const { int newValue = value + add; diff --git a/src/gui/widgets/qabstractspinbox.cpp b/src/gui/widgets/qabstractspinbox.cpp index 4372ae3..7a985a1 100644 --- a/src/gui/widgets/qabstractspinbox.cpp +++ b/src/gui/widgets/qabstractspinbox.cpp @@ -1655,6 +1655,8 @@ void QAbstractSpinBox::initStyleOption(QStyleOptionSpinBox *option) const : (QAbstractSpinBox::StepDownEnabled|QAbstractSpinBox::StepUpEnabled); option->frame = d->frame; + if (d->readOnly) + option->state |= QStyle::State_ReadOnly; } /*! diff --git a/src/gui/widgets/qcheckbox.cpp b/src/gui/widgets/qcheckbox.cpp index 81270fc..2210488 100644 --- a/src/gui/widgets/qcheckbox.cpp +++ b/src/gui/widgets/qcheckbox.cpp @@ -300,6 +300,16 @@ QSize QCheckBox::sizeHint() const return d->sizeHint; } + +/*! + \reimp + \since 4.8 +*/ +QSize QCheckBox::minimumSizeHint() const +{ + return sizeHint(); +} + /*! \reimp */ diff --git a/src/gui/widgets/qcheckbox.h b/src/gui/widgets/qcheckbox.h index 38ac143..263fa4a 100644 --- a/src/gui/widgets/qcheckbox.h +++ b/src/gui/widgets/qcheckbox.h @@ -65,6 +65,7 @@ public: QSize sizeHint() const; + QSize minimumSizeHint() const; void setTristate(bool y = true); bool isTristate() const; diff --git a/src/gui/widgets/qcocoamenu_mac.mm b/src/gui/widgets/qcocoamenu_mac.mm index e40404e..95075b9 100644 --- a/src/gui/widgets/qcocoamenu_mac.mm +++ b/src/gui/widgets/qcocoamenu_mac.mm @@ -44,9 +44,11 @@ #ifdef QT_MAC_USE_COCOA #import <private/qcocoamenu_mac_p.h> #import <private/qcocoamenuloader_mac_p.h> +#import <private/qcocoaapplication_mac_p.h> #include <private/qt_cocoa_helpers_mac_p.h> #include <private/qapplication_p.h> #include <private/qaction_p.h> +#include <private/qcocoaapplication_mac_p.h> #include <QtGui/QMenu> @@ -60,6 +62,7 @@ QT_FORWARD_DECLARE_CLASS(QEvent) QT_BEGIN_NAMESPACE extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); //qapplication.cpp +extern NSString *qt_mac_removePrivateUnicode(NSString* string); QT_END_NAMESPACE QT_USE_NAMESPACE @@ -78,7 +81,7 @@ QT_USE_NAMESPACE return self; } -- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item; +- (void)menu:(NSMenu*)menu willHighlightItem:(NSMenuItem*)item { Q_UNUSED(menu); @@ -99,7 +102,7 @@ QT_USE_NAMESPACE } } -- (void)menuWillOpen:(NSMenu*)menu; +- (void)menuWillOpen:(NSMenu*)menu { while (QWidget *popup = QApplication::activePopupWidget()) @@ -109,7 +112,7 @@ QT_USE_NAMESPACE qt_mac_menu_collapseSeparators(menu, qtmenu->separatorsCollapsible()); } -- (void)menuDidClose:(NSMenu*)menu; +- (void)menuDidClose:(NSMenu*)menu { qt_mac_emit_menuSignals(((QT_MANGLE_NAMESPACE(QCocoaMenu) *)menu)->qmenu, false); if (previousAction) { @@ -156,10 +159,13 @@ QT_USE_NAMESPACE // In every other case we return NO, which means that Cocoa can do as it pleases // (i.e., fire the menu action). NSMenuItem *whichItem; + // Change the private unicode keys to the ones used in setting the "Key Equivalents" + NSString *characters = qt_mac_removePrivateUnicode([event characters]); if ([self hasShortcut:menu - forKey:[event characters] - forModifiers:([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) - whichItem:&whichItem]) { + forKey:characters + // Interested only in Shift, Cmd, Ctrl & Alt Keys, so ignoring masks like, Caps lock, Num Lock ... + forModifiers:([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask | NSAlternateKeyMask)) + whichItem:&whichItem]) { QWidget *widget = 0; QAction *qaction = 0; if (whichItem && [whichItem tag]) { @@ -170,6 +176,9 @@ QT_USE_NAMESPACE qApp->activePopupWidget()->focusWidget() : qApp->activePopupWidget()); else if (QApplicationPrivate::focus_widget) widget = QApplicationPrivate::focus_widget; + // If we could not find any receivers, pass it to the active window + if (!widget) + widget = qApp->activeWindow(); if (qaction && widget) { int key = qaction->shortcut(); QKeyEvent accel_ev(QEvent::ShortcutOverride, (key & (~Qt::KeyboardModifierMask)), @@ -177,11 +186,10 @@ QT_USE_NAMESPACE accel_ev.ignore(); qt_sendSpontaneousEvent(widget, &accel_ev); if (accel_ev.isAccepted()) { - if (qt_dispatchKeyEvent(event, widget)) { - *target = nil; - *action = nil; - return YES; - } + qt_dispatchKeyEvent(event, widget); + *target = nil; + *action = nil; + return YES; } } } diff --git a/src/gui/widgets/qcombobox.cpp b/src/gui/widgets/qcombobox.cpp index 8a69bdf..fc251bf 100644 --- a/src/gui/widgets/qcombobox.cpp +++ b/src/gui/widgets/qcombobox.cpp @@ -80,6 +80,9 @@ #if defined(Q_WS_S60) #include "private/qt_s60_p.h" #endif +#ifndef QT_NO_ACCESSIBILITY +#include "qaccessible.h" +#endif QT_BEGIN_NAMESPACE @@ -116,7 +119,7 @@ QStyleOptionMenuItem QComboMenuDelegate::getStyleOption(const QStyleOptionViewIt QPalette resolvedpalette = option.palette.resolve(QApplication::palette("QMenu")); QVariant value = index.data(Qt::ForegroundRole); - if (qVariantCanConvert<QBrush>(value)) { + if (value.canConvert<QBrush>()) { resolvedpalette.setBrush(QPalette::WindowText, qvariant_cast<QBrush>(value)); resolvedpalette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(value)); resolvedpalette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value)); @@ -152,7 +155,7 @@ QStyleOptionMenuItem QComboMenuDelegate::getStyleOption(const QStyleOptionViewIt menuOption.icon = qvariant_cast<QPixmap>(variant); break; } - if (qVariantCanConvert<QBrush>(index.data(Qt::BackgroundRole))) { + if (index.data(Qt::BackgroundRole).canConvert<QBrush>()) { menuOption.palette.setBrush(QPalette::All, QPalette::Background, qvariant_cast<QBrush>(index.data(Qt::BackgroundRole))); } @@ -369,6 +372,7 @@ void QComboBoxPrivateContainer::timerEvent(QTimerEvent *timerEvent) if (timerEvent->timerId() == adjustSizeTimer.timerId()) { adjustSizeTimer.stop(); if (combo->sizeAdjustPolicy() == QComboBox::AdjustToContents) { + combo->updateGeometry(); combo->adjustSize(); combo->update(); } @@ -1017,6 +1021,9 @@ void QComboBoxPrivate::_q_dataChanged(const QModelIndex &topLeft, const QModelIn } q->update(); } +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(q, 0, QAccessible::NameChanged); +#endif } void QComboBoxPrivate::_q_rowsInserted(const QModelIndex &parent, int start, int end) @@ -1093,6 +1100,8 @@ void QComboBoxPrivate::updateViewContainerPaletteAndOpacity() container->setPalette(q->palette()); container->setWindowOpacity(1.0); } + if (lineEdit) + lineEdit->setPalette(q->palette()); } /*! @@ -1268,6 +1277,9 @@ void QComboBoxPrivate::_q_emitCurrentIndexChanged(const QModelIndex &index) Q_Q(QComboBox); emit q->currentIndexChanged(index.row()); emit q->currentIndexChanged(itemText(index)); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(q, 0, QAccessible::NameChanged); +#endif } QString QComboBoxPrivate::itemText(const QModelIndex &index) const @@ -1303,7 +1315,7 @@ QComboBox::~QComboBox() By default, this property has a value of 10. \note This property is ignored for non-editable comboboxes in styles that returns - false for QStyle::SH_ComboBox_Popup such as the Mac style or the Gtk+ Style. + true for QStyle::SH_ComboBox_Popup such as the Mac style or the Gtk+ Style. */ int QComboBox::maxVisibleItems() const { @@ -2013,11 +2025,18 @@ void QComboBox::setCurrentIndex(int index) void QComboBoxPrivate::setCurrentIndex(const QModelIndex &mi) { Q_Q(QComboBox); - bool indexChanged = (mi != currentIndex); + + QModelIndex normalized; + if (mi.column() != modelColumn) + normalized = model->index(mi.row(), modelColumn, mi.parent()); + if (!normalized.isValid()) + normalized = mi; // Fallback to passed index. + + bool indexChanged = (normalized != currentIndex); if (indexChanged) - currentIndex = QPersistentModelIndex(mi); + currentIndex = QPersistentModelIndex(normalized); if (lineEdit) { - QString newText = q->itemText(currentIndex.row()); + QString newText = q->itemText(normalized.row()); if (lineEdit->text() != newText) lineEdit->setText(newText); updateLineEditGeometry(); @@ -2353,7 +2372,12 @@ void QComboBox::showPopup() initStyleOption(&opt); QRect listRect(style->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxListBoxPopup, this)); +#ifndef Q_WS_S60 QRect screen = d->popupGeometry(QApplication::desktop()->screenNumber(this)); +#else + QRect screen = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); +#endif + QPoint below = mapToGlobal(listRect.bottomLeft()); int belowHeight = screen.bottom() - below.y(); QPoint above = mapToGlobal(listRect.topLeft()); @@ -2476,18 +2500,10 @@ void QComboBox::showPopup() listRect.setWidth(screen.height()); //by default popup is centered on screen in landscape listRect.moveCenter(screen.center()); - if (staConTopRect.IsEmpty()) { - TRect cbaRect = TRect(); - AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EControlPane, cbaRect); - AknLayoutUtils::TAknCbaLocation cbaLocation = AknLayoutUtils::CbaLocation(); - switch (cbaLocation) { - case AknLayoutUtils::EAknCbaLocationRight: - listRect.setRight(screen.right()); - break; - case AknLayoutUtils::EAknCbaLocationLeft: - listRect.setLeft(screen.left()); - break; - } + if (staConTopRect.IsEmpty() && AknLayoutUtils::CbaLocation() != AknLayoutUtils::EAknCbaLocationBottom) { + // landscape without stacon, menu should be at the right + (opt.direction == Qt::LeftToRight) ? listRect.setRight(screen.right()) : + listRect.setLeft(screen.left()); } } #endif @@ -2628,6 +2644,9 @@ void QComboBox::clear() { Q_D(QComboBox); d->model->removeRows(0, d->model->rowCount(d->root), d->root); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::NameChanged); +#endif } /*! @@ -2644,6 +2663,9 @@ void QComboBox::clearEditText() Q_D(QComboBox); if (d->lineEdit) d->lineEdit->clear(); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::NameChanged); +#endif } /*! @@ -2654,6 +2676,9 @@ void QComboBox::setEditText(const QString &text) Q_D(QComboBox); if (d->lineEdit) d->lineEdit->setText(text); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(this, 0, QAccessible::NameChanged); +#endif } /*! @@ -2706,7 +2731,7 @@ void QComboBox::changeEvent(QEvent *e) initStyleOption(&opt); if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) { - const QRect screen = d->popupGeometry(QApplication::desktop()->screenNumber(this)); + QRect screen = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); QRect listRect(style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxListBoxPopup, this)); @@ -2721,13 +2746,14 @@ void QComboBox::changeEvent(QEvent *e) listRect.setWidth(listRect.height()); //by default popup is centered on screen in landscape listRect.moveCenter(screen.center()); - if (staConTopRect.IsEmpty()) { + if (staConTopRect.IsEmpty() && AknLayoutUtils::CbaLocation() != AknLayoutUtils::EAknCbaLocationBottom) { // landscape without stacon, menu should be at the right (opt.direction == Qt::LeftToRight) ? listRect.setRight(screen.right()) : listRect.setLeft(screen.left()); } - d->container->setGeometry(listRect); } + + d->container->setGeometry(listRect); } } #endif @@ -2760,6 +2786,10 @@ void QComboBox::changeEvent(QEvent *e) void QComboBox::resizeEvent(QResizeEvent *) { Q_D(QComboBox); +#ifdef Q_WS_S60 + if (d->viewContainer() && d->viewContainer()->isVisible()) + showPopup(); +#endif d->updateLineEditGeometry(); } diff --git a/src/gui/widgets/qcombobox.h b/src/gui/widgets/qcombobox.h index c08d83f..9cbf483 100644 --- a/src/gui/widgets/qcombobox.h +++ b/src/gui/widgets/qcombobox.h @@ -68,7 +68,7 @@ class Q_GUI_EXPORT QComboBox : public QWidget Q_PROPERTY(bool editable READ isEditable WRITE setEditable) Q_PROPERTY(int count READ count) Q_PROPERTY(QString currentText READ currentText) - Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged USER true) Q_PROPERTY(int maxVisibleItems READ maxVisibleItems WRITE setMaxVisibleItems) Q_PROPERTY(int maxCount READ maxCount WRITE setMaxCount) Q_PROPERTY(InsertPolicy insertPolicy READ insertPolicy WRITE setInsertPolicy) diff --git a/src/gui/widgets/qcombobox_p.h b/src/gui/widgets/qcombobox_p.h index e1bb1fb..a17f9d9 100644 --- a/src/gui/widgets/qcombobox_p.h +++ b/src/gui/widgets/qcombobox_p.h @@ -337,7 +337,7 @@ private: QComboBox *mCombo; }; -class QComboBoxPrivate : public QWidgetPrivate +class Q_AUTOTEST_EXPORT QComboBoxPrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QComboBox) public: diff --git a/src/gui/widgets/qdatetimeedit.cpp b/src/gui/widgets/qdatetimeedit.cpp index 61ae21e..d9ff8a6 100644 --- a/src/gui/widgets/qdatetimeedit.cpp +++ b/src/gui/widgets/qdatetimeedit.cpp @@ -890,7 +890,13 @@ void QDateTimeEdit::setDisplayFormat(const QString &format) const bool dateShown = (d->sections & DateSections_Mask); Q_ASSERT(dateShown || timeShown); if (timeShown && !dateShown) { + QTime time = d->value.toTime(); setDateRange(d->value.toDate(), d->value.toDate()); + if (d->minimum.toTime() >= d->maximum.toTime()) { + setTimeRange(QDATETIMEEDIT_TIME_MIN, QDATETIMEEDIT_TIME_MAX); + // if the time range became invalid during the adjustment, the time would have been reset + setTime(time); + } } else if (dateShown && !timeShown) { setTimeRange(QDATETIMEEDIT_TIME_MIN, QDATETIMEEDIT_TIME_MAX); d->value = QDateTime(d->value.toDate(), QTime(), d->spec); @@ -1654,6 +1660,15 @@ void QDateTimeEditPrivate::updateTimeSpec() minimum = minimum.toDateTime().toTimeSpec(spec); maximum = maximum.toDateTime().toTimeSpec(spec); value = value.toDateTime().toTimeSpec(spec); + + // time zone changes can lead to 00:00:00 becomes 01:00:00 and 23:59:59 becomes 00:59:59 (invalid range) + const bool dateShown = (sections & QDateTimeEdit::DateSections_Mask); + if (!dateShown) { + if (minimum.toTime() >= maximum.toTime()){ + minimum = QDateTime(value.toDate(), QDATETIMEEDIT_TIME_MIN, spec); + maximum = QDateTime(value.toDate(), QDATETIMEEDIT_TIME_MAX, spec); + } + } } void QDateTimeEditPrivate::updateEdit() @@ -2523,20 +2538,32 @@ void QDateTimeEditPrivate::syncCalendarWidget() } QCalendarPopup::QCalendarPopup(QWidget * parent, QCalendarWidget *cw) - : QWidget(parent, Qt::Popup), calendar(0) + : QWidget(parent, Qt::Popup) { setAttribute(Qt::WA_WindowPropagation); dateChanged = false; if (!cw) { - cw = new QCalendarWidget(this); + verifyCalendarInstance(); + } else { + setCalendarWidget(cw); + } +} + +QCalendarWidget *QCalendarPopup::verifyCalendarInstance() +{ + if (calendar.isNull()) { + QCalendarWidget *cw = new QCalendarWidget(this); cw->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader); #ifdef QT_KEYPAD_NAVIGATION if (QApplication::keypadNavigationEnabled()) cw->setHorizontalHeaderFormat(QCalendarWidget::SingleLetterDayNames); #endif + setCalendarWidget(cw); + return cw; + } else { + return calendar.data(); } - setCalendarWidget(cw); } void QCalendarPopup::setCalendarWidget(QCalendarWidget *cw) @@ -2548,28 +2575,29 @@ void QCalendarPopup::setCalendarWidget(QCalendarWidget *cw) widgetLayout->setMargin(0); widgetLayout->setSpacing(0); } - delete calendar; - calendar = cw; - widgetLayout->addWidget(calendar); + delete calendar.data(); + calendar = QWeakPointer<QCalendarWidget>(cw); + widgetLayout->addWidget(cw); - connect(calendar, SIGNAL(activated(QDate)), this, SLOT(dateSelected(QDate))); - connect(calendar, SIGNAL(clicked(QDate)), this, SLOT(dateSelected(QDate))); - connect(calendar, SIGNAL(selectionChanged()), this, SLOT(dateSelectionChanged())); + connect(cw, SIGNAL(activated(QDate)), this, SLOT(dateSelected(QDate))); + connect(cw, SIGNAL(clicked(QDate)), this, SLOT(dateSelected(QDate))); + connect(cw, SIGNAL(selectionChanged()), this, SLOT(dateSelectionChanged())); - calendar->setFocus(); + cw->setFocus(); } void QCalendarPopup::setDate(const QDate &date) { oldDate = date; - calendar->setSelectedDate(date); + verifyCalendarInstance()->setSelectedDate(date); } void QCalendarPopup::setDateRange(const QDate &min, const QDate &max) { - calendar->setMinimumDate(min); - calendar->setMaximumDate(max); + QCalendarWidget *cw = verifyCalendarInstance(); + cw->setMinimumDate(min); + cw->setMaximumDate(max); } void QCalendarPopup::mousePressEvent(QMouseEvent *event) @@ -2605,7 +2633,7 @@ bool QCalendarPopup::event(QEvent *event) void QCalendarPopup::dateSelectionChanged() { dateChanged = true; - emit newDateSelected(calendar->selectedDate()); + emit newDateSelected(verifyCalendarInstance()->selectedDate()); } void QCalendarPopup::dateSelected(const QDate &date) { diff --git a/src/gui/widgets/qdatetimeedit_p.h b/src/gui/widgets/qdatetimeedit_p.h index 164b8a5..3c6ca96 100644 --- a/src/gui/widgets/qdatetimeedit_p.h +++ b/src/gui/widgets/qdatetimeedit_p.h @@ -148,11 +148,11 @@ class QCalendarPopup : public QWidget Q_OBJECT public: QCalendarPopup(QWidget *parent = 0, QCalendarWidget *cw = 0); - QDate selectedDate() { return calendar->selectedDate(); } + QDate selectedDate() { return verifyCalendarInstance()->selectedDate(); } void setDate(const QDate &date); void setDateRange(const QDate &min, const QDate &max); - void setFirstDayOfWeek(Qt::DayOfWeek dow) { calendar->setFirstDayOfWeek(dow); } - QCalendarWidget *calendarWidget() const { return calendar; } + void setFirstDayOfWeek(Qt::DayOfWeek dow) { verifyCalendarInstance()->setFirstDayOfWeek(dow); } + QCalendarWidget *calendarWidget() const { return const_cast<QCalendarPopup*>(this)->verifyCalendarInstance(); } void setCalendarWidget(QCalendarWidget *cw); Q_SIGNALS: void activated(const QDate &date); @@ -171,7 +171,9 @@ protected: bool event(QEvent *e); private: - QCalendarWidget *calendar; + QCalendarWidget *verifyCalendarInstance(); + + QWeakPointer<QCalendarWidget> calendar; QDate oldDate; bool dateChanged; }; diff --git a/src/gui/widgets/qdial.cpp b/src/gui/widgets/qdial.cpp index f653589..e2b1983 100644 --- a/src/gui/widgets/qdial.cpp +++ b/src/gui/widgets/qdial.cpp @@ -83,6 +83,7 @@ public: int valueFromPoint(const QPoint &) const; double angle(const QPoint &, const QPoint &) const; void init(); + virtual int bound(int val) const; }; void QDialPrivate::init() @@ -97,6 +98,20 @@ void QDialPrivate::init() #endif } +int QDialPrivate::bound(int val) const +{ + if (wrapping) { + if ((val >= minimum) && (val <= maximum)) + return val; + val = minimum + ((val - minimum) % (maximum - minimum)); + if (val < minimum) + val += maximum - minimum; + return val; + } else { + return QAbstractSliderPrivate::bound(val); + } +} + /*! Initialize \a option with the values from this QDial. This method is useful for subclasses when they need a QStyleOptionSlider, but don't want diff --git a/src/gui/widgets/qdialogbuttonbox.cpp b/src/gui/widgets/qdialogbuttonbox.cpp index 04673a3..6c29141 100644 --- a/src/gui/widgets/qdialogbuttonbox.cpp +++ b/src/gui/widgets/qdialogbuttonbox.cpp @@ -562,6 +562,14 @@ QPushButton *QDialogButtonBoxPrivate::createButton(QDialogButtonBox::StandardBut } else { qWarning("QDialogButtonBox::createButton: Invalid ButtonRole, button not added"); } + +#ifdef Q_WS_MAC + // Since mnemonics is off by default on Mac, we add a Cmd-D + // shortcut here to e.g. make the "Don't Save" button work nativly: + if (sbutton == QDialogButtonBox::Discard) + button->setShortcut(QKeySequence(QLatin1String("Ctrl+D"))); +#endif + return button; } @@ -1229,7 +1237,7 @@ bool QDialogButtonBox::event(QEvent *event) break; } - foreach (QPushButton *pb, qFindChildren<QPushButton *>(dialog ? dialog : this)) { + foreach (QPushButton *pb, (dialog ? dialog : this)->findChildren<QPushButton *>()) { if (pb->isDefault() && pb != firstAcceptButton) { hasDefault = true; break; diff --git a/src/gui/widgets/qdockarealayout.cpp b/src/gui/widgets/qdockarealayout.cpp index e1c054b..83506e5 100644 --- a/src/gui/widgets/qdockarealayout.cpp +++ b/src/gui/widgets/qdockarealayout.cpp @@ -2100,7 +2100,6 @@ bool QDockAreaLayoutInfo::updateTabBar() const bool gap = false; int tab_idx = 0; - bool changed = false; for (int i = 0; i < item_list.count(); ++i) { const QDockAreaLayoutItem &item = item_list.at(i); if (item.skip()) @@ -2121,7 +2120,6 @@ bool QDockAreaLayoutInfo::updateTabBar() const tabBar->setTabToolTip(tab_idx, title); #endif tabBar->setTabData(tab_idx, id); - changed = true; } else if (qvariant_cast<quintptr>(tabBar->tabData(tab_idx)) != id) { if (tab_idx + 1 < tabBar->count() && qvariant_cast<quintptr>(tabBar->tabData(tab_idx + 1)) == id) @@ -2133,7 +2131,6 @@ bool QDockAreaLayoutInfo::updateTabBar() const #endif tabBar->setTabData(tab_idx, id); } - changed = true; } if (title != tabBar->tabText(tab_idx)) { @@ -2141,7 +2138,6 @@ bool QDockAreaLayoutInfo::updateTabBar() const #ifndef QT_NO_TOOLTIP tabBar->setTabToolTip(tab_idx, title); #endif - changed = true; } ++tab_idx; @@ -2149,7 +2145,6 @@ bool QDockAreaLayoutInfo::updateTabBar() const while (tab_idx < tabBar->count()) { tabBar->removeTab(tab_idx); - changed = true; } tabBar->blockSignals(blocked); diff --git a/src/gui/widgets/qdockwidget.cpp b/src/gui/widgets/qdockwidget.cpp index 6436c84..6e37683 100644 --- a/src/gui/widgets/qdockwidget.cpp +++ b/src/gui/widgets/qdockwidget.cpp @@ -161,7 +161,6 @@ void QDockWidgetTitleButton::paintEvent(QPaintEvent *) { QPainter p(this); - QRect r = rect(); QStyleOptionToolButton opt; opt.init(this); opt.state |= QStyle::State_AutoRaise; diff --git a/src/gui/widgets/qeffects.cpp b/src/gui/widgets/qeffects.cpp index 6d86464..adf1cc1 100644 --- a/src/gui/widgets/qeffects.cpp +++ b/src/gui/widgets/qeffects.cpp @@ -55,19 +55,6 @@ QT_BEGIN_NAMESPACE /* - Internal class to get access to protected QWidget-members -*/ - -class QAccessWidget : public QWidget -{ - friend class QAlphaWidget; - friend class QRollEffect; -public: - QAccessWidget(QWidget* parent=0, Qt::WindowFlags f = 0) - : QWidget(parent, f) {} -}; - -/* Internal class QAlphaWidget. The QAlphaWidget is shown while the animation lasts @@ -98,13 +85,12 @@ private: QImage backImage; QImage frontImage; QImage mixedImage; - QPointer<QAccessWidget> widget; + QPointer<QWidget> widget; int duration; int elapsed; bool showWidget; QTimer anim; QElapsedTimer checkTime; - double windowOpacity; }; static QAlphaWidget* q_blend = 0; @@ -119,8 +105,7 @@ QAlphaWidget::QAlphaWidget(QWidget* w, Qt::WindowFlags f) setEnabled(false); #endif setAttribute(Qt::WA_NoSystemBackground, true); - widget = (QAccessWidget*)w; - windowOpacity = w->windowOpacity(); + widget = w; alpha = 0; } @@ -129,7 +114,7 @@ QAlphaWidget::~QAlphaWidget() #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) // Restore user-defined opacity value if (widget) - widget->setWindowOpacity(windowOpacity); + widget->setWindowOpacity(1); #endif } @@ -268,10 +253,10 @@ void QAlphaWidget::render() alpha = 1; #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) - if (alpha >= windowOpacity || !showWidget) { + if (alpha >= 1 || !showWidget) { anim.stop(); qApp->removeEventFilter(this); - widget->setWindowOpacity(windowOpacity); + widget->setWindowOpacity(1); q_blend = 0; deleteLater(); } else { @@ -370,7 +355,7 @@ private slots: void scroll(); private: - QPointer<QAccessWidget> widget; + QPointer<QWidget> widget; int currentHeight; int currentWidth; @@ -401,7 +386,7 @@ QRollEffect::QRollEffect(QWidget* w, Qt::WindowFlags f, DirFlags orient) setEnabled(false); #endif - widget = (QAccessWidget*) w; + widget = w; Q_ASSERT(widget); setAttribute(Qt::WA_NoSystemBackground, true); diff --git a/src/gui/widgets/qfocusframe.cpp b/src/gui/widgets/qfocusframe.cpp index fb0f316..b41a71a 100644 --- a/src/gui/widgets/qfocusframe.cpp +++ b/src/gui/widgets/qfocusframe.cpp @@ -85,6 +85,9 @@ void QFocusFramePrivate::update() void QFocusFramePrivate::updateSize() { Q_Q(QFocusFrame); + if (!widget) + return; + int vmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameVMargin), hmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin); QPoint pos(widget->x(), widget->y()); diff --git a/src/gui/widgets/qlabel.cpp b/src/gui/widgets/qlabel.cpp index f2a6965..ab88f38 100644 --- a/src/gui/widgets/qlabel.cpp +++ b/src/gui/widgets/qlabel.cpp @@ -55,6 +55,10 @@ #include "private/qstylesheetstyle_p.h" #include <qmath.h> +#ifndef QT_NO_ACCESSIBILITY +#include <qaccessible.h> +#endif + QT_BEGIN_NAMESPACE /*! @@ -88,6 +92,13 @@ QT_BEGIN_NAMESPACE by clear(). \endtable + \warning When passing a QString to the constructor or calling setText(), + make sure to sanitize your input, as QLabel tries to guess whether it + displays the text as plain text or as rich text. You may want to call + setTextFormat() explicitly, e.g. in case you expect the text to be in + plain format but cannot control the text source (for instance when + displaying data loaded from the Web). + When the content is changed using any of these functions, any previous content is cleared. @@ -370,6 +381,11 @@ void QLabel::setText(const QString &text) #endif d->updateLabel(); + +#ifndef QT_NO_ACCESSIBILITY + if (accessibleName().isEmpty()) + QAccessible::updateAccessibility(this, 0, QAccessible::NameChanged); +#endif } QString QLabel::text() const @@ -1072,7 +1088,7 @@ void QLabel::paintEvent(QPaintEvent *) else #endif if (d->isTextLabel) { - QRectF lr = d->layoutRect(); + QRectF lr = d->layoutRect().toAlignedRect(); QStyleOption opt; opt.initFrom(this); #ifndef QT_NO_STYLE_STYLESHEET diff --git a/src/gui/widgets/qlinecontrol.cpp b/src/gui/widgets/qlinecontrol.cpp index 198bc04..d58da37 100644 --- a/src/gui/widgets/qlinecontrol.cpp +++ b/src/gui/widgets/qlinecontrol.cpp @@ -144,7 +144,7 @@ void QLineControl::updateDisplayText(bool forceUpdate) Copies the currently selected text into the clipboard using the given \a mode. - + \note If the echo mode is set to a mode other than Normal then copy will not work. This is to prevent using copy as a method of bypassing password features of the line control. @@ -198,12 +198,10 @@ void QLineControl::backspace() --m_cursor; if (m_maskData) m_cursor = prevMaskBlank(m_cursor); - QChar uc = m_text.at(m_cursor); - if (m_cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) { + if (m_cursor > 0 && m_text.at(m_cursor).isLowSurrogate()) { // second half of a surrogate, check if we have the first half as well, // if yes delete both at once - uc = m_text.at(m_cursor - 1); - if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) { + if (m_text.at(m_cursor - 1).isHighSurrogate()) { internalDelete(true); --m_cursor; } @@ -536,7 +534,7 @@ void QLineControl::processInputMethodEvent(QInputMethodEvent *event) /*! \internal - Draws the display text for the line control using the given + Draws the display text for the line control using the given \a painter, \a clip, and \a offset. Which aspects of the display text are drawn is specified by the given \a flags. @@ -687,7 +685,12 @@ void QLineControl::internalSetText(const QString &txt, int pos, bool edited) m_modifiedState = m_undoState = 0; m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos; m_textDirty = (oldText != m_text); - finishChange(-1, true, edited); + bool changed = finishChange(-1, true, edited); + +#ifndef QT_NO_ACCESSIBILITY + if (changed) + QAccessible::updateAccessibility(parent(), 0, QAccessible::TextUpdated); +#endif } @@ -1284,6 +1287,9 @@ void QLineControl::emitCursorPositionChanged() const int oldLast = m_lastCursorPos; m_lastCursorPos = m_cursor; cursorPositionChanged(oldLast, m_cursor); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::updateAccessibility(parent(), 0, QAccessible::TextCaretMoved); +#endif } } @@ -1434,9 +1440,9 @@ bool QLineControl::processEvent(QEvent* ev) case QEvent::GraphicsSceneMouseRelease: case QEvent::GraphicsSceneMousePress:{ QGraphicsSceneMouseEvent *gvEv = static_cast<QGraphicsSceneMouseEvent*>(ev); - QMouseEvent* mouse = new QMouseEvent(ev->type(), + QMouseEvent mouse(ev->type(), gvEv->pos().toPoint(), gvEv->button(), gvEv->buttons(), gvEv->modifiers()); - processMouseEvent(mouse); break; + processMouseEvent(&mouse); break; } #endif case QEvent::MouseButtonPress: @@ -1637,6 +1643,7 @@ void QLineControl::processKeyEvent(QKeyEvent* event) } bool unknown = false; + bool visual = cursorMoveStyle() == Qt::VisualMoveStyle; if (false) { } @@ -1701,11 +1708,11 @@ void QLineControl::processKeyEvent(QKeyEvent* event) #endif moveCursor(selectionEnd(), false); } else { - cursorForward(0, layoutDirection() == Qt::LeftToRight ? 1 : -1); + cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1)); } } else if (event == QKeySequence::SelectNextChar) { - cursorForward(1, layoutDirection() == Qt::LeftToRight ? 1 : -1); + cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1)); } else if (event == QKeySequence::MoveToPreviousChar) { #if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER) @@ -1716,11 +1723,11 @@ void QLineControl::processKeyEvent(QKeyEvent* event) #endif moveCursor(selectionStart(), false); } else { - cursorForward(0, layoutDirection() == Qt::LeftToRight ? -1 : 1); + cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1)); } } else if (event == QKeySequence::SelectPreviousChar) { - cursorForward(1, layoutDirection() == Qt::LeftToRight ? -1 : 1); + cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1)); } else if (event == QKeySequence::MoveToNextWord) { if (echoMode() == QLineEdit::Normal) diff --git a/src/gui/widgets/qlinecontrol_p.h b/src/gui/widgets/qlinecontrol_p.h index 0ab454b..6a1b4e3 100644 --- a/src/gui/widgets/qlinecontrol_p.h +++ b/src/gui/widgets/qlinecontrol_p.h @@ -61,10 +61,10 @@ #include "QtGui/qtextlayout.h" #include "QtGui/qstyleoption.h" #include "QtCore/qpointer.h" -#include "QtGui/qlineedit.h" #include "QtGui/qclipboard.h" #include "QtCore/qpoint.h" #include "QtGui/qcompleter.h" +#include "QtGui/qaccessible.h" #include "qplatformdefs.h" @@ -165,6 +165,8 @@ public: int cursorWidth() const { return m_cursorWidth; } void setCursorWidth(int value) { m_cursorWidth = value; } + Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); } + void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); } void moveCursor(int pos, bool mark = false); void cursorForward(bool mark, int steps) @@ -172,10 +174,12 @@ public: int c = m_cursor; if (steps > 0) { while (steps--) - c = m_textLayout.nextCursorPosition(c); + c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.rightCursorPosition(c) + : m_textLayout.nextCursorPosition(c); } else if (steps < 0) { while (steps++) - c = m_textLayout.previousCursorPosition(c); + c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.leftCursorPosition(c) + : m_textLayout.previousCursorPosition(c); } moveCursor(c, mark); } diff --git a/src/gui/widgets/qlineedit.cpp b/src/gui/widgets/qlineedit.cpp index 4d683fb..7d35766 100644 --- a/src/gui/widgets/qlineedit.cpp +++ b/src/gui/widgets/qlineedit.cpp @@ -1112,6 +1112,45 @@ void QLineEdit::setDragEnabled(bool b) /*! + \property QLineEdit::cursorMoveStyle + \brief the movement style of cursor in this line edit + \since 4.8 + + When this property is set to Qt::VisualMoveStyle, the line edit will use visual + movement style. Pressing the left arrow key will always cause the cursor to move + left, regardless of the text's writing direction. The same behavior applies to + right arrow key. + + When the property is Qt::LogicalMoveStyle (the default), within a LTR text block, + increase cursor position when pressing left arrow key, decrease cursor position + when pressing the right arrow key. If the text block is right to left, the opposite + behavior applies. +*/ + +/*! + \since 4.8 + + Returns the movement style for the cursor in the line edit. +*/ +Qt::CursorMoveStyle QLineEdit::cursorMoveStyle() const +{ + Q_D(const QLineEdit); + return d->control->cursorMoveStyle(); +} + +/*! + \since 4.8 + + Sets the movement style for the cursor in the line edit to the given + \a style. +*/ +void QLineEdit::setCursorMoveStyle(Qt::CursorMoveStyle style) +{ + Q_D(QLineEdit); + d->control->setCursorMoveStyle(style); +} + +/*! \property QLineEdit::acceptableInput \brief whether the input satisfies the inputMask and the validator. diff --git a/src/gui/widgets/qlineedit.h b/src/gui/widgets/qlineedit.h index 9daa94e..73d2289 100644 --- a/src/gui/widgets/qlineedit.h +++ b/src/gui/widgets/qlineedit.h @@ -43,6 +43,7 @@ #define QLINEEDIT_H #include <QtGui/qframe.h> +#include <QtGui/qtextcursor.h> #include <QtCore/qstring.h> #include <QtCore/qmargins.h> @@ -84,6 +85,7 @@ class Q_GUI_EXPORT QLineEdit : public QWidget Q_PROPERTY(bool redoAvailable READ isRedoAvailable) Q_PROPERTY(bool acceptableInput READ hasAcceptableInput) Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText) + Q_PROPERTY(Qt::CursorMoveStyle cursorMoveStyle READ cursorMoveStyle WRITE setCursorMoveStyle) public: explicit QLineEdit(QWidget* parent=0); @@ -158,6 +160,9 @@ public: void setDragEnabled(bool b); bool dragEnabled() const; + void setCursorMoveStyle(Qt::CursorMoveStyle style); + Qt::CursorMoveStyle cursorMoveStyle() const; + QString inputMask() const; void setInputMask(const QString &inputMask); bool hasAcceptableInput() const; @@ -283,6 +288,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_editFocusChange(bool)) #endif Q_PRIVATE_SLOT(d_func(), void _q_selectionChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_updateNeeded(const QRect &)) }; #endif // QT_NO_LINEEDIT diff --git a/src/gui/widgets/qlineedit_p.cpp b/src/gui/widgets/qlineedit_p.cpp index c6ce7da..090c0b6 100644 --- a/src/gui/widgets/qlineedit_p.cpp +++ b/src/gui/widgets/qlineedit_p.cpp @@ -59,6 +59,13 @@ QT_BEGIN_NAMESPACE const int QLineEditPrivate::verticalMargin(1); const int QLineEditPrivate::horizontalMargin(2); +QRect QLineEditPrivate::adjustedControlRect(const QRect &rect) const +{ + QRect cr = adjustedContentsRect(); + int cix = cr.x() - hscroll + horizontalMargin; + return rect.translated(QPoint(cix, vscroll)); +} + int QLineEditPrivate::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const { QRect cr = adjustedContentsRect(); @@ -68,11 +75,7 @@ int QLineEditPrivate::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const QRect QLineEditPrivate::cursorRect() const { - QRect cr = adjustedContentsRect(); - int cix = cr.x() - hscroll + horizontalMargin; - QRect crect = control->cursorRect(); - crect.moveTo(crect.topLeft() + QPoint(cix, vscroll)); - return crect; + return adjustedControlRect(control->cursorRect()); } #ifndef QT_NO_COMPLETER @@ -141,10 +144,16 @@ void QLineEditPrivate::_q_selectionChanged() emit q->selectionChanged(); } +void QLineEditPrivate::_q_updateNeeded(const QRect &rect) +{ + q_func()->update(adjustedControlRect(rect)); +} + void QLineEditPrivate::init(const QString& txt) { Q_Q(QLineEdit); control = new QLineControl(txt); + control->setParent(q); control->setFont(q->font()); QObject::connect(control, SIGNAL(textChanged(QString)), q, SIGNAL(textChanged(QString))); @@ -176,7 +185,7 @@ void QLineEditPrivate::init(const QString& txt) q, SLOT(update())); QObject::connect(control, SIGNAL(updateNeeded(QRect)), - q, SLOT(update())); + q, SLOT(_q_updateNeeded(QRect))); QStyleOptionFrameV2 opt; q->initStyleOption(&opt); @@ -216,9 +225,8 @@ void QLineEditPrivate::setCursorVisible(bool visible) if ((bool)cursorVisible == visible) return; cursorVisible = visible; - QRect r = cursorRect(); if (control->inputMask().isEmpty()) - q->update(r); + q->update(cursorRect()); else q->update(); } diff --git a/src/gui/widgets/qlineedit_p.h b/src/gui/widgets/qlineedit_p.h index c0d2044..f396af6 100644 --- a/src/gui/widgets/qlineedit_p.h +++ b/src/gui/widgets/qlineedit_p.h @@ -84,7 +84,6 @@ public: ~QLineEditPrivate() { - delete control; } QLineControl *control; @@ -94,6 +93,8 @@ public: #endif void init(const QString&); + QRect adjustedControlRect(const QRect &) const; + int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const; QRect cursorRect() const; void setCursorVisible(bool visible); @@ -129,6 +130,7 @@ public: void _q_editFocusChange(bool); #endif void _q_selectionChanged(); + void _q_updateNeeded(const QRect &); #ifndef QT_NO_COMPLETER void _q_completionHighlighted(QString); #endif diff --git a/src/gui/widgets/qmainwindow.cpp b/src/gui/widgets/qmainwindow.cpp index 3b073e9..8f468c6 100644 --- a/src/gui/widgets/qmainwindow.cpp +++ b/src/gui/widgets/qmainwindow.cpp @@ -1514,15 +1514,28 @@ void QMainWindow::setUnifiedTitleAndToolBarOnMac(bool set) if (!isWindow() || d->useHIToolBar == set || QSysInfo::MacintoshVersion < QSysInfo::MV_10_3) return; - // ### Disable the unified toolbar when using anything but the native graphics system. - // ### Disable when using alien widgets as well - if (windowSurface() || testAttribute(Qt::WA_NativeWindow) == false) - return; - d->useHIToolBar = set; createWinId(); // We need the hiview for down below. +#ifdef QT_MAC_USE_COCOA + // Activate the unified toolbar with the raster engine. + if (windowSurface() && set) { + d->layout->unifiedSurface = new QUnifiedToolbarSurface(this); + } +#endif // QT_MAC_USE_COCOA + d->layout->updateHIToolBarStatus(); + +#ifdef QT_MAC_USE_COCOA + // Deactivate the unified toolbar with the raster engine. + if (windowSurface() && !set) { + if (d->layout->unifiedSurface) { + delete d->layout->unifiedSurface; + d->layout->unifiedSurface = 0; + } + } +#endif // QT_MAC_USE_COCOA + // Enabling the unified toolbar clears the opaque size grip setting, update it. d->macUpdateOpaqueSizeGrip(); #else @@ -1632,7 +1645,7 @@ QMenu *QMainWindow::createPopupMenu() Q_D(QMainWindow); QMenu *menu = 0; #ifndef QT_NO_DOCKWIDGET - QList<QDockWidget *> dockwidgets = qFindChildren<QDockWidget *>(this); + QList<QDockWidget *> dockwidgets = findChildren<QDockWidget *>(); if (dockwidgets.size()) { menu = new QMenu(this); for (int i = 0; i < dockwidgets.size(); ++i) { @@ -1646,7 +1659,7 @@ QMenu *QMainWindow::createPopupMenu() } #endif // QT_NO_DOCKWIDGET #ifndef QT_NO_TOOLBAR - QList<QToolBar *> toolbars = qFindChildren<QToolBar *>(this); + QList<QToolBar *> toolbars = findChildren<QToolBar *>(); if (toolbars.size()) { if (!menu) menu = new QMenu(this); diff --git a/src/gui/widgets/qmainwindowlayout.cpp b/src/gui/widgets/qmainwindowlayout.cpp index f5b2805..fbde3a9 100644 --- a/src/gui/widgets/qmainwindowlayout.cpp +++ b/src/gui/widgets/qmainwindowlayout.cpp @@ -73,6 +73,10 @@ # include <private/qt_cocoa_helpers_mac_p.h> #endif +#ifdef QT_NO_DOCKWIDGET +extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window); +#endif + #ifdef Q_DEBUG_MAINWINDOW_LAYOUT # include <QTextStream> #endif diff --git a/src/gui/widgets/qmainwindowlayout_mac.mm b/src/gui/widgets/qmainwindowlayout_mac.mm index 591841f..28e8764 100644 --- a/src/gui/widgets/qmainwindowlayout_mac.mm +++ b/src/gui/widgets/qmainwindowlayout_mac.mm @@ -43,6 +43,7 @@ #include <qtoolbar.h> #include <private/qtoolbarlayout_p.h> #include <private/qt_cocoa_helpers_mac_p.h> +#include <private/qtoolbar_p.h> #ifndef QT_MAC_USE_COCOA #include <Carbon/Carbon.h> @@ -354,7 +355,11 @@ void QMainWindowLayout::updateHIToolBarStatus() // Move everything out of the HIToolbar into the main toolbar. while (!qtoolbarsInUnifiedToolbarList.isEmpty()) { // Should shrink the list by one every time. - layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, qtoolbarsInUnifiedToolbarList.first()); + QToolBar *toolbar = qtoolbarsInUnifiedToolbarList.first(); +#if defined(QT_MAC_USE_COCOA) + unifiedSurface->removeToolbar(toolbar); +#endif + layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, toolbar); } macWindowToolbarSet(qt_mac_window_for(layoutState.mainWindow), 0); } else { @@ -362,7 +367,8 @@ void QMainWindowLayout::updateHIToolBarStatus() for (int i = 0; i < toolbars.size(); ++i) { QToolBar *toolbar = toolbars.at(i); if (toolBarArea(toolbar) == Qt::TopToolBarArea) { - removeWidget(toolbar); // Do this here, because we are in an in-between state. + // Do this here, because we are in an in-between state. + removeWidget(toolbar); layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, toolbar); } } @@ -386,10 +392,20 @@ void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar if (toolbar == 0) return; +#if defined(QT_MAC_USE_COCOA) + // toolbar will now become native (if not already) since we need + // an nsview for it inside the corresponding NSToolbarItem. + // Setting isInUnifiedToolbar will (among other things) stop alien + // siblings from becoming native when this happends since the toolbar + // will not overlap with other children of the QMainWindow. NB: Switching + // unified toolbar off after this stage is not supported, as this means + // that either the menubar must be alien again, or the sibling must + // be backed by an nsview to protect from overlapping issues: + toolbar->d_func()->isInUnifiedToolbar = true; +#endif QToolBarLayout *toolbarLayout = static_cast<QToolBarLayout *>(toolbar->layout()); - toolbarSaveState.insert(toolbar, ToolBarSaveState(toolbar->isMovable(), - toolbar->maximumSize())); + toolbarSaveState.insert(toolbar, ToolBarSaveState(toolbar->isMovable(), toolbar->maximumSize())); if (toolbarLayout->hasExpandFlag() == false) toolbar->setMaximumSize(toolbar->sizeHint()); @@ -398,8 +414,8 @@ void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar toolbarLayout->setUsePopupMenu(true); // Make the toolbar a child of the mainwindow to avoid creating a window. toolbar->setParent(layoutState.mainWindow); - toolbar->createWinId(); // Now create the OSViewRef. + toolbar->winId(); // Now create the OSViewRef. layoutState.mainWindow->createWinId(); OSWindowRef window = qt_mac_window_for(layoutState.mainWindow); @@ -408,6 +424,7 @@ void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar beforeIndex = qtoolbarsInUnifiedToolbarList.size(); int toolbarIndex = qtoolbarsInUnifiedToolbarList.indexOf(toolbar); + #ifndef QT_MAC_USE_COCOA HIToolbarRef macToolbar = NULL; if ((GetWindowToolbar(window, &macToolbar) == noErr) && !macToolbar) { @@ -444,6 +461,18 @@ void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar #endif } qtoolbarsInUnifiedToolbarList.insert(beforeIndex, toolbar); + + // Adding to the unified toolbar surface for the raster engine. + if (layoutState.mainWindow->windowSurface()) { + QPoint offset(0, 0); + for (int i = 0; i < beforeIndex; ++i) { + offset.setX(offset.x() + qtoolbarsInUnifiedToolbarList.at(i)->size().width()); + } +#ifdef QT_MAC_USE_COCOA + unifiedSurface->insertToolbar(toolbar, offset); +#endif // QT_MAC_USE_COCOA + } + #ifndef QT_MAC_USE_COCOA QCFType<HIToolbarItemRef> outItem; const QObject *stupidArray[] = { toolbar, this }; @@ -460,6 +489,19 @@ void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar #endif } +#ifdef QT_MAC_USE_COCOA +void QMainWindowLayout::updateUnifiedToolbarOffset() +{ + QPoint offset(0, 0); + + for (int i = 1; i < qtoolbarsInUnifiedToolbarList.length(); ++i) { + offset.setX(offset.x() + qtoolbarsInUnifiedToolbarList.at(i - 1)->size().width()); + qtoolbarsInUnifiedToolbarList.at(i)->d_func()->toolbar_offset = offset; + } +} +#endif // QT_MAC_USE_COCOA + + void QMainWindowLayout::removeFromMacToolbar(QToolBar *toolbar) { QHash<void *, QToolBar *>::iterator it = unifiedToolbarHash.begin(); @@ -533,11 +575,11 @@ void QMainWindowLayout::fixSizeInUnifiedToolbar(QToolBar *tb) const QMacCocoaAutoReleasePool pool; QWidgetItem layoutItem(tb); QSize size = layoutItem.maximumSize(); - NSSize nssize = NSMakeSize(size.width(), size.height() - 2); + NSSize nssize = NSMakeSize(size.width(), size.height()); [item setMaxSize:nssize]; size = layoutItem.minimumSize(); nssize.width = size.width(); - nssize.height = size.height() - 2; + nssize.height = size.height(); [item setMinSize:nssize]; } #else diff --git a/src/gui/widgets/qmainwindowlayout_p.h b/src/gui/widgets/qmainwindowlayout_p.h index d32ca80..14ec1fb 100644 --- a/src/gui/widgets/qmainwindowlayout_p.h +++ b/src/gui/widgets/qmainwindowlayout_p.h @@ -70,12 +70,12 @@ //#define Q_DEBUG_MAINWINDOW_LAYOUT -#ifdef Q_DEBUG_MAINWINDOW_LAYOUT +#if defined(Q_DEBUG_MAINWINDOW_LAYOUT) && !defined(QT_NO_DOCKWIDGET) QT_BEGIN_NAMESPACE class QTextStream; Q_GUI_EXPORT void qt_dumpLayout(QTextStream &qout, QMainWindow *window); QT_END_NAMESPACE -#endif // Q_DEBUG_MAINWINDOW_LAYOUT +#endif // Q_DEBUG_MAINWINDOW_LAYOUT && !QT_NO_DOCKWIDGET #ifdef Q_WS_MAC // Forward defs to make avoid including Carbon.h (faster compile you know ;). @@ -85,7 +85,11 @@ typedef HIObjectRef HIToolbarItemRef; typedef const void * CFTypeRef; typedef const struct __CFString * CFStringRef; -#endif +# ifdef QT_MAC_USE_COCOA +#include <private/qunifiedtoolbarsurface_mac_p.h> +# endif // QT_MAC_USE_COCOA + +#endif // Q_WS_MAC QT_BEGIN_NAMESPACE @@ -337,7 +341,13 @@ public: bool activateUnifiedToolbarAfterFullScreen; void syncUnifiedToolbarVisibility(); bool blockVisiblityCheck; -#endif + +#ifdef QT_MAC_USE_COCOA + QUnifiedToolbarSurface *unifiedSurface; + void updateUnifiedToolbarOffset(); +#endif // QT_MAC_USE_COCOA + +#endif // Q_WS_MAC }; QT_END_NAMESPACE diff --git a/src/gui/widgets/qmdiarea.cpp b/src/gui/widgets/qmdiarea.cpp index 4e587eb..2fe9706 100644 --- a/src/gui/widgets/qmdiarea.cpp +++ b/src/gui/widgets/qmdiarea.cpp @@ -674,6 +674,8 @@ QMdiAreaPrivate::QMdiAreaPrivate() viewMode(QMdiArea::SubWindowView), #ifndef QT_NO_TABBAR documentMode(false), + tabsClosable(false), + tabsMovable(false), #endif #ifndef QT_NO_TABWIDGET tabShape(QTabWidget::Rounded), @@ -792,6 +794,27 @@ void QMdiAreaPrivate::_q_currentTabChanged(int index) #endif // QT_NO_TABBAR } +void QMdiAreaPrivate::_q_closeTab(int index) +{ +#ifdef QT_NO_TABBAR + Q_UNUSED(index); +#else + QMdiSubWindow *subWindow = childWindows.at(index); + Q_ASSERT(subWindow); + subWindow->close(); +#endif // QT_NO_TABBAR +} + +void QMdiAreaPrivate::_q_moveTab(int from, int to) +{ +#ifdef QT_NO_TABBAR + Q_UNUSED(from); + Q_UNUSED(to); +#else + childWindows.move(from, to); +#endif // QT_NO_TABBAR +} + /*! \internal */ @@ -1519,6 +1542,8 @@ void QMdiAreaPrivate::setViewMode(QMdiArea::ViewMode mode) Q_ASSERT(!tabBar); tabBar = new QMdiAreaTabBar(q); tabBar->setDocumentMode(documentMode); + tabBar->setTabsClosable(tabsClosable); + tabBar->setMovable(tabsMovable); #ifndef QT_NO_TABWIDGET tabBar->setShape(tabBarShapeFrom(tabShape, tabPosition)); #endif @@ -1550,6 +1575,8 @@ void QMdiAreaPrivate::setViewMode(QMdiArea::ViewMode mode) updateTabBarGeometry(); QObject::connect(tabBar, SIGNAL(currentChanged(int)), q, SLOT(_q_currentTabChanged(int))); + QObject::connect(tabBar, SIGNAL(tabCloseRequested(int)), q, SLOT(_q_closeTab(int))); + QObject::connect(tabBar, SIGNAL(tabMoved(int,int)), q, SLOT(_q_moveTab(int,int))); } else #endif // QT_NO_TABBAR { // SubWindowView @@ -1636,6 +1663,8 @@ void QMdiAreaPrivate::refreshTabBar() return; tabBar->setDocumentMode(documentMode); + tabBar->setTabsClosable(tabsClosable); + tabBar->setMovable(tabsMovable); #ifndef QT_NO_TABWIDGET tabBar->setShape(tabBarShapeFrom(tabShape, tabPosition)); #endif @@ -2114,6 +2143,56 @@ void QMdiArea::setDocumentMode(bool enabled) d->documentMode = enabled; d->refreshTabBar(); } + +/*! + \property QMdiArea::tabsClosable + \brief whether the tab bar should place close buttons on each tab in tabbed view mode. + \since 4.8 + + Tabs are not closable by default. + + \sa QTabBar::tabsClosable, setViewMode() +*/ +bool QMdiArea::tabsClosable() const +{ + Q_D(const QMdiArea); + return d->tabsClosable; +} + +void QMdiArea::setTabsClosable(bool closable) +{ + Q_D(QMdiArea); + if (d->tabsClosable == closable) + return; + + d->tabsClosable = closable; + d->refreshTabBar(); +} + +/*! + \property QMdiArea::tabsMovable + \brief whether the user can move the tabs within the tabbar area in tabbed view mode. + \since 4.8 + + Tabs are not movable by default. + + \sa QTabBar::movable, setViewMode() +*/ +bool QMdiArea::tabsMovable() const +{ + Q_D(const QMdiArea); + return d->tabsMovable; +} + +void QMdiArea::setTabsMovable(bool movable) +{ + Q_D(QMdiArea); + if (d->tabsMovable == movable) + return; + + d->tabsMovable = movable; + d->refreshTabBar(); +} #endif // QT_NO_TABBAR #ifndef QT_NO_TABWIDGET diff --git a/src/gui/widgets/qmdiarea.h b/src/gui/widgets/qmdiarea.h index 3b14eee..48c1c7b 100644 --- a/src/gui/widgets/qmdiarea.h +++ b/src/gui/widgets/qmdiarea.h @@ -65,6 +65,8 @@ class Q_GUI_EXPORT QMdiArea : public QAbstractScrollArea Q_PROPERTY(ViewMode viewMode READ viewMode WRITE setViewMode) #ifndef QT_NO_TABBAR Q_PROPERTY(bool documentMode READ documentMode WRITE setDocumentMode) + Q_PROPERTY(bool tabsClosable READ tabsClosable WRITE setTabsClosable) + Q_PROPERTY(bool tabsMovable READ tabsMovable WRITE setTabsMovable) #endif #ifndef QT_NO_TABWIDGET Q_PROPERTY(QTabWidget::TabShape tabShape READ tabShape WRITE setTabShape) @@ -116,6 +118,12 @@ public: #ifndef QT_NO_TABBAR bool documentMode() const; void setDocumentMode(bool enabled); + + void setTabsClosable(bool closable); + bool tabsClosable() const; + + void setTabsMovable(bool movable); + bool tabsMovable() const; #endif #ifndef QT_NO_TABWIDGET void setTabShape(QTabWidget::TabShape shape); @@ -156,7 +164,9 @@ private: Q_DECLARE_PRIVATE(QMdiArea) Q_PRIVATE_SLOT(d_func(), void _q_deactivateAllWindows()) Q_PRIVATE_SLOT(d_func(), void _q_processWindowStateChanged(Qt::WindowStates, Qt::WindowStates)) - Q_PRIVATE_SLOT(d_func(), void _q_currentTabChanged(int index)) + Q_PRIVATE_SLOT(d_func(), void _q_currentTabChanged(int)) + Q_PRIVATE_SLOT(d_func(), void _q_closeTab(int)) + Q_PRIVATE_SLOT(d_func(), void _q_moveTab(int, int)) }; Q_DECLARE_OPERATORS_FOR_FLAGS(QMdiArea::AreaOptions) diff --git a/src/gui/widgets/qmdiarea_p.h b/src/gui/widgets/qmdiarea_p.h index faaf48a..fe368f2 100644 --- a/src/gui/widgets/qmdiarea_p.h +++ b/src/gui/widgets/qmdiarea_p.h @@ -165,6 +165,8 @@ public: QMdiArea::ViewMode viewMode; #ifndef QT_NO_TABBAR bool documentMode; + bool tabsClosable; + bool tabsMovable; #endif #ifndef QT_NO_TABWIDGET QTabWidget::TabShape tabShape; @@ -189,6 +191,8 @@ public: void _q_deactivateAllWindows(QMdiSubWindow *aboutToActivate = 0); void _q_processWindowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState); void _q_currentTabChanged(int index); + void _q_closeTab(int index); + void _q_moveTab(int from, int to); // Functions. void appendChild(QMdiSubWindow *child); diff --git a/src/gui/widgets/qmdisubwindow.cpp b/src/gui/widgets/qmdisubwindow.cpp index e8175a9..5cbd620 100644 --- a/src/gui/widgets/qmdisubwindow.cpp +++ b/src/gui/widgets/qmdisubwindow.cpp @@ -2203,7 +2203,7 @@ void QMdiSubWindowPrivate::setSizeGrip(QSizeGrip *newSizeGrip) void QMdiSubWindowPrivate::setSizeGripVisible(bool visible) const { // See if we can find any size grips - QList<QSizeGrip *> sizeGrips = qFindChildren<QSizeGrip *>(q_func()); + QList<QSizeGrip *> sizeGrips = q_func()->findChildren<QSizeGrip *>(); foreach (QSizeGrip *grip, sizeGrips) grip->setVisible(visible); } @@ -2319,7 +2319,7 @@ void QMdiSubWindow::setWidget(QWidget *widget) widget->setParent(this); #ifndef QT_NO_SIZEGRIP - QSizeGrip *sizeGrip = qFindChild<QSizeGrip *>(widget); + QSizeGrip *sizeGrip = widget->findChild<QSizeGrip *>(); if (sizeGrip) sizeGrip->installEventFilter(this); if (d->sizeGrip) diff --git a/src/gui/widgets/qmenu.cpp b/src/gui/widgets/qmenu.cpp index 118c073..a490286 100644 --- a/src/gui/widgets/qmenu.cpp +++ b/src/gui/widgets/qmenu.cpp @@ -240,6 +240,12 @@ QList<QPointer<QWidget> > QMenuPrivate::calcCausedStack() const void QMenuPrivate::updateActionRects() const { Q_Q(const QMenu); + updateActionRects(popupGeometry(q)); +} + +void QMenuPrivate::updateActionRects(const QRect &screen) const +{ + Q_Q(const QMenu); if (!itemsDirty) return; @@ -249,20 +255,10 @@ void QMenuPrivate::updateActionRects() const actionRects.resize(actions.count()); actionRects.fill(QRect()); - //let's try to get the last visible action - int lastVisibleAction = actions.count() - 1; - for(;lastVisibleAction >= 0; --lastVisibleAction) { - const QAction *action = actions.at(lastVisibleAction); - if (action->isVisible()) { - //removing trailing separators - if (action->isSeparator() && collapsibleSeparators) - continue; - break; - } - } + int lastVisibleAction = getLastVisibleAction(); int max_column_width = 0, - dh = popupGeometry(q).height(), + dh = screen.height(), y = 0; QStyle *style = q->style(); QStyleOption opt; @@ -363,7 +359,6 @@ void QMenuPrivate::updateActionRects() const const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin)); max_column_width = qMax(min_column_width, max_column_width); - //calculate position const int base_y = vmargin + fw + topmargin + (scroll ? scroll->scrollOffset : 0) + @@ -394,6 +389,34 @@ void QMenuPrivate::updateActionRects() const itemsDirty = 0; } +QSize QMenuPrivate::adjustMenuSizeForScreen(const QRect &screen) +{ + Q_Q(QMenu); + QSize ret = screen.size(); + itemsDirty = true; + updateActionRects(screen); + const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); + ret.setWidth(actionRects.at(getLastVisibleAction()).right() + fw); + return ret; +} + +int QMenuPrivate::getLastVisibleAction() const +{ + //let's try to get the last visible action + int lastVisibleAction = actions.count() - 1; + for (;lastVisibleAction >= 0; --lastVisibleAction) { + const QAction *action = actions.at(lastVisibleAction); + if (action->isVisible()) { + //removing trailing separators + if (action->isSeparator() && collapsibleSeparators) + continue; + break; + } + } + return lastVisibleAction; +} + + QRect QMenuPrivate::actionRect(QAction *act) const { int index = actions.indexOf(act); @@ -1825,9 +1848,20 @@ void QMenu::popup(const QPoint &p, QAction *atAction) else #endif screen = d->popupGeometry(QApplication::desktop()->screenNumber(p)); - const int desktopFrame = style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, this); bool adjustToDesktop = !window()->testAttribute(Qt::WA_DontShowOnScreen); + + // if the screens have very different geometries and the menu is too big, we have to recalculate + if (size.height() > screen.height() || size.width() > screen.width()) { + size = d->adjustMenuSizeForScreen(screen); + adjustToDesktop = true; + } + // Layout is not right, we might be able to save horizontal space + if (d->ncols >1 && size.height() < screen.height()) { + size = d->adjustMenuSizeForScreen(screen); + adjustToDesktop = true; + } + #ifdef QT_KEYPAD_NAVIGATION if (!atAction && QApplication::keypadNavigationEnabled()) { // Try to have one item activated @@ -1921,6 +1955,27 @@ void QMenu::popup(const QPoint &p, QAction *atAction) } } } + const int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this); + const QSize menuSize(sizeHint()); + QMenu *caused = qobject_cast<QMenu*>(d_func()->causedPopup.widget); + if (caused && caused->geometry().width() + menuSize.width() + subMenuOffset < screen.width()) { + QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction)); + const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft()); + parentActionRect.moveTopLeft(actionTopLeft); + if (isRightToLeft()) { + if ((pos.x() + menuSize.width() > parentActionRect.left() - subMenuOffset) + && (pos.x() < parentActionRect.right())) + { + pos.rx() = parentActionRect.right(); + } + } else { + if ((pos.x() < parentActionRect.right() + subMenuOffset) + && (pos.x() + menuSize.width() > parentActionRect.left())) + { + pos.rx() = parentActionRect.left() - menuSize.width(); + } + } + } setGeometry(QRect(pos, size)); #ifndef QT_NO_EFFECTS int hGuess = isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll; @@ -2799,7 +2854,7 @@ void QMenu::mouseMoveEvent(QMouseEvent *e) if (d->sloppyRegion.contains(e->pos())) { d->sloppyAction = action; QMenuPrivate::sloppyDelayTimer = startTimer(style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this)*6); - } else { + } else if (action != d->currentAction) { d->setCurrentAction(action, style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this)); } } @@ -2953,28 +3008,8 @@ void QMenu::internalDelayedPopup() const QRect actionRect(d->actionRect(d->currentAction)); const QSize menuSize(d->activeMenu->sizeHint()); const QPoint rightPos(mapToGlobal(QPoint(actionRect.right() + subMenuOffset + 1, actionRect.top()))); - const QPoint leftPos(mapToGlobal(QPoint(actionRect.left() - subMenuOffset - menuSize.width(), actionRect.top()))); QPoint pos(rightPos); - QMenu *caused = qobject_cast<QMenu*>(d->activeMenu->d_func()->causedPopup.widget); - - const QRect availGeometry(d->popupGeometry(caused)); - if (isRightToLeft()) { - pos = leftPos; - if ((caused && caused->x() < x()) || pos.x() < availGeometry.left()) { - if(rightPos.x() + menuSize.width() < availGeometry.right()) - pos = rightPos; - else - pos.rx() = availGeometry.left(); - } - } else { - if ((caused && caused->x() > x()) || pos.x() + menuSize.width() > availGeometry.right()) { - if(leftPos.x() < availGeometry.left()) - pos.rx() = availGeometry.right() - menuSize.width(); - else - pos = leftPos; - } - } //calc sloppy focus buffer if (style()->styleHint(QStyle::SH_Menu_SloppySubMenus, 0, this)) { diff --git a/src/gui/widgets/qmenu.h b/src/gui/widgets/qmenu.h index 7c552a6..053fdee 100644 --- a/src/gui/widgets/qmenu.h +++ b/src/gui/widgets/qmenu.h @@ -51,6 +51,10 @@ #include <QtGui/qpixmap.h> #endif +#ifdef Q_WS_WINCE +#include <windef.h> // for HMENU +#endif + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE diff --git a/src/gui/widgets/qmenu_mac.mm b/src/gui/widgets/qmenu_mac.mm index 9f7b130..56658b3 100644 --- a/src/gui/widgets/qmenu_mac.mm +++ b/src/gui/widgets/qmenu_mac.mm @@ -1254,15 +1254,11 @@ QMenuPrivate::QMacMenuPrivate::addAction(QMacMenuAction *action, QMacMenuAction NSString *keySequenceToKeyEqivalent(const QKeySequence &accel) { quint32 accel_key = (accel[0] & ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL)); - extern QChar qt_macSymbolForQtKey(int key); // qkeysequence.cpp - QChar keyEquiv = qt_macSymbolForQtKey(accel_key); - if (keyEquiv.isNull()) { - if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15) - keyEquiv = (accel_key - Qt::Key_F1) + NSF1FunctionKey; - else - keyEquiv = unichar(QChar(accel_key).toLower().unicode()); - } - return [NSString stringWithCharacters:&keyEquiv.unicode() length:1]; + extern QChar qtKey2CocoaKey(Qt::Key key); + QChar cocoa_key = qtKey2CocoaKey(Qt::Key(accel_key)); + if (cocoa_key.isNull()) + cocoa_key = QChar(accel_key).toLower().unicode(); + return [NSString stringWithCharacters:&cocoa_key.unicode() length:1]; } // return the cocoa modifier mask for the QKeySequence (currently only looks at the first one). @@ -1643,7 +1639,7 @@ QMenuBarPrivate::QMacMenuBarPrivate::~QMacMenuBarPrivate() } void -QMenuBarPrivate::QMacMenuBarPrivate::addAction(QAction *a, QMacMenuAction *before) +QMenuBarPrivate::QMacMenuBarPrivate::addAction(QAction *a, QAction *before) { if (a->isSeparator() || !menu) return; @@ -1653,7 +1649,7 @@ QMenuBarPrivate::QMacMenuBarPrivate::addAction(QAction *a, QMacMenuAction *befor #ifndef QT_MAC_USE_COCOA action->command = qt_mac_menu_static_cmd_id++; #endif - addAction(action, before); + addAction(action, findAction(before)); } void @@ -2052,8 +2048,7 @@ bool QMenuBar::macUpdateMenuBar() { #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; - if (!qt_cocoaPostMessage(getMenuLoader(), @selector(qtUpdateMenubar))) - return QMenuBarPrivate::macUpdateMenuBarImmediatly(); + qt_cocoaPostMessage(getMenuLoader(), @selector(qtUpdateMenubar)); return true; #else return QMenuBarPrivate::macUpdateMenuBarImmediatly(); diff --git a/src/gui/widgets/qmenu_p.h b/src/gui/widgets/qmenu_p.h index 809555d..82bd932 100644 --- a/src/gui/widgets/qmenu_p.h +++ b/src/gui/widgets/qmenu_p.h @@ -154,6 +154,9 @@ public: #endif scroll(0), eventLoop(0), tearoff(0), tornoff(0), tearoffHighlighted(0), hasCheckableItems(0), sloppyAction(0), doChildEffects(false) +#ifdef QT3_SUPPORT + ,emitHighlighted(false) +#endif #ifdef Q_WS_MAC ,mac_menu(0) #endif @@ -163,9 +166,6 @@ public: #ifdef Q_WS_S60 ,symbian_menu(0) #endif -#ifdef QT3_SUPPORT - ,emitHighlighted(false) -#endif { } ~QMenuPrivate() { @@ -194,10 +194,13 @@ public: mutable QVector<QRect> actionRects; mutable QHash<QAction *, QWidget *> widgetItems; void updateActionRects() const; + void updateActionRects(const QRect &screen) const; QRect popupGeometry(const QWidget *widget) const; QRect popupGeometry(int screen = -1) const; mutable uint ncols : 4; //4 bits is probably plenty uint collapsibleSeparators : 1; + QSize adjustMenuSizeForScreen(const QRect & screen); + int getLastVisibleAction() const; bool activationRecursionGuard; @@ -296,7 +299,6 @@ public: void updateLayoutDirection(); - //menu fading/scrolling effects bool doChildEffects; diff --git a/src/gui/widgets/qmenu_symbian.cpp b/src/gui/widgets/qmenu_symbian.cpp index 2f9ada6..99a1d78 100644 --- a/src/gui/widgets/qmenu_symbian.cpp +++ b/src/gui/widgets/qmenu_symbian.cpp @@ -264,7 +264,8 @@ void qt_symbian_show_submenu( CEikMenuPane* menuPane, int id) // However if we don't have any items, we still need the item array. Otherwise // menupane will crash. That's why we create item array here manually, and // AddMenuItemL will then use the existing array. - CEikMenuPane::CItemArray* itemArray = q_check_ptr(new CEikMenuPane::CItemArray); + CEikMenuPane::CItemArray* itemArray = new CEikMenuPane::CItemArray; + Q_CHECK_PTR(itemArray); menuPane->SetItemArray(itemArray); menuPane->SetItemArrayOwnedExternally(EFalse); @@ -356,7 +357,7 @@ QMenuPrivate::QSymbianMenuPrivate::QSymbianMenuPrivate() QMenuPrivate::QSymbianMenuPrivate::~QSymbianMenuPrivate() { - + qDeleteAll(actionItems); } void QMenuPrivate::QSymbianMenuPrivate::addAction(QAction *a, QSymbianMenuAction *before) @@ -397,12 +398,12 @@ void QMenuPrivate::QSymbianMenuPrivate::rebuild(bool) { } -void QMenuBarPrivate::QSymbianMenuBarPrivate::addAction(QAction *a, QSymbianMenuAction *before) +void QMenuBarPrivate::QSymbianMenuBarPrivate::addAction(QAction *a, QAction *before) { QSymbianMenuAction *action = new QSymbianMenuAction; action->action = a; action->command = qt_symbian_menu_static_cmd_id++; - addAction(action, before); + addAction(action, findAction(before)); } void QMenuBarPrivate::QSymbianMenuBarPrivate::addAction(QSymbianMenuAction *action, QSymbianMenuAction *before) diff --git a/src/gui/widgets/qmenu_wince.cpp b/src/gui/widgets/qmenu_wince.cpp index 24df6a2..b0c6c1b 100644 --- a/src/gui/widgets/qmenu_wince.cpp +++ b/src/gui/widgets/qmenu_wince.cpp @@ -113,8 +113,6 @@ static void resolveAygLibs() if (!aygResolved) { aygResolved = true; QLibrary aygLib(QLatin1String("aygshell")); - if (!aygLib.load()) - return; ptrCreateMenuBar = (AygCreateMenuBar) aygLib.resolve("SHCreateMenuBar"); ptrEnableSoftKey = (AygEnableSoftKey) aygLib.resolve("SHEnableSoftkey"); } @@ -506,12 +504,12 @@ void QMenuPrivate::QWceMenuPrivate::removeAction(QWceMenuAction *action) rebuild(); } -void QMenuBarPrivate::QWceMenuBarPrivate::addAction(QAction *a, QWceMenuAction *before) +void QMenuBarPrivate::QWceMenuBarPrivate::addAction(QAction *a, QAction *before) { QWceMenuAction *action = new QWceMenuAction; action->action = a; action->command = qt_wce_menu_static_cmd_id++; - addAction(action, before); + addAction(action, findAction(before)); } void QMenuBarPrivate::QWceMenuBarPrivate::addAction(QWceMenuAction *action, QWceMenuAction *before) diff --git a/src/gui/widgets/qmenubar.cpp b/src/gui/widgets/qmenubar.cpp index 6a41c74..3e5365c 100644 --- a/src/gui/widgets/qmenubar.cpp +++ b/src/gui/widgets/qmenubar.cpp @@ -55,6 +55,9 @@ #include <qtoolbar.h> #include <qtoolbutton.h> #include <qwhatsthis.h> +#ifdef Q_WS_X11 +#include <qpluginloader.h> +#endif #ifndef QT_NO_MENUBAR @@ -66,6 +69,9 @@ #include "qmenu_p.h" #include "qmenubar_p.h" #include "qdebug.h" +#ifdef Q_WS_X11 +#include "qmenubar_x11_p.h" +#endif #ifdef Q_WS_WINCE extern bool qt_wince_is_mobile(); //defined in qguifunctions_wce.cpp @@ -173,7 +179,11 @@ void QMenuBarPrivate::updateGeometries() return; int q_width = q->width()-(q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q)*2); int q_start = -1; - if(leftWidget || rightWidget) { + if( +#ifdef Q_WS_X11 + platformMenuBar->allowCornerWidgets() && +#endif + (leftWidget || rightWidget)) { int vmargin = q->style()->pixelMetric(QStyle::PM_MenuBarVMargin, 0, q) + q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q); int hmargin = q->style()->pixelMetric(QStyle::PM_MenuBarHMargin, 0, q) @@ -204,7 +214,11 @@ void QMenuBarPrivate::updateGeometries() calcActionRects(q_width, q_start); currentAction = 0; #ifndef QT_NO_SHORTCUT - if(itemsDirty) { + if( +#ifdef Q_WS_X11 + !platformMenuBar->shortcutsHandledByNativeMenuBar() && +#endif + itemsDirty) { for(int j = 0; j < shortcutIndexMap.size(); ++j) q->releaseShortcut(shortcutIndexMap.value(j)); shortcutIndexMap.resize(0); // faster than clear @@ -212,6 +226,12 @@ void QMenuBarPrivate::updateGeometries() shortcutIndexMap.append(q->grabShortcut(QKeySequence::mnemonic(actions.at(i)->text()))); } #endif +#ifdef Q_WS_X11 + if(q->isNativeMenuBar()) {//nothing to see here folks, move along.. + itemsDirty = false; + return; + } +#endif itemsDirty = false; hiddenActions.clear(); @@ -743,6 +763,11 @@ void QMenuBarPrivate::init() QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, true); } #endif +#ifdef Q_WS_X11 + platformMenuBar = qt_guiPlatformMenuBarFactory()->create(); + platformMenuBar->init(q); +#endif + q->setBackgroundRole(QPalette::Button); oldWindow = oldParent = 0; #ifdef QT3_SUPPORT @@ -751,6 +776,10 @@ void QMenuBarPrivate::init() #ifdef QT_SOFTKEYS_ENABLED menuBarAction = 0; #endif +#ifdef Q_WS_X11 + cornerWidgetToolBar = 0; + cornerWidgetContainer = 0; +#endif handleReparent(); q->setMouseTracking(q->style()->styleHint(QStyle::SH_MenuBar_MouseTracking, 0, q)); @@ -821,6 +850,10 @@ QMenuBar::~QMenuBar() Q_D(QMenuBar); d->symbianDestroyMenuBar(); #endif +#ifdef Q_WS_X11 + Q_D(QMenuBar); + delete d->cornerWidgetToolBar; +#endif } /*! @@ -1072,11 +1105,19 @@ void QMenuBar::paintEvent(QPaintEvent *e) */ void QMenuBar::setVisible(bool visible) { +#ifdef Q_WS_X11 + Q_D(QMenuBar); + d->platformMenuBar->setVisible(visible); +#else #if defined(Q_WS_MAC) || defined(Q_OS_WINCE) || defined(Q_WS_S60) - if (isNativeMenuBar()) + if (isNativeMenuBar()) { + if (!visible) + QWidget::setVisible(false); return; + } #endif QWidget::setVisible(visible); +#endif // Q_WS_X11 } /*! @@ -1272,6 +1313,9 @@ void QMenuBar::actionEvent(QActionEvent *e) { Q_D(QMenuBar); d->itemsDirty = true; +#ifdef Q_WS_X11 + d->platformMenuBar->actionEvent(e); +#endif #if defined (Q_WS_MAC) || defined(Q_OS_WINCE) || defined(Q_WS_S60) if (isNativeMenuBar()) { #ifdef Q_WS_MAC @@ -1284,7 +1328,7 @@ void QMenuBar::actionEvent(QActionEvent *e) if (!nativeMenuBar) return; if(e->type() == QEvent::ActionAdded) - nativeMenuBar->addAction(e->action(), nativeMenuBar->findAction(e->before())); + nativeMenuBar->addAction(e->action(), e->before()); else if(e->type() == QEvent::ActionRemoved) nativeMenuBar->removeAction(e->action()); else if(e->type() == QEvent::ActionChanged) @@ -1366,6 +1410,10 @@ void QMenuBarPrivate::handleReparent() newWindow->installEventFilter(q); } +#ifdef Q_WS_X11 + platformMenuBar->handleReparent(oldParent, newParent, oldWindow, newWindow); +#endif + oldParent = newParent; oldWindow = newWindow; @@ -1563,6 +1611,11 @@ bool QMenuBar::event(QEvent *e) bool QMenuBar::eventFilter(QObject *object, QEvent *event) { Q_D(QMenuBar); +#ifdef Q_WS_X11 + if (d->platformMenuBar->menuBarEventFilter(object, event)) { + return true; + } +#endif if (object == parent() && object) { #ifdef QT3_SUPPORT if (d->doAutoResize && event->type() == QEvent::Resize) { @@ -1656,7 +1709,7 @@ QRect QMenuBar::actionGeometry(QAction *act) const QSize QMenuBar::minimumSizeHint() const { Q_D(const QMenuBar); -#if defined(Q_WS_MAC) || defined(Q_WS_WINCE) || defined(Q_WS_S60) +#if defined(Q_WS_MAC) || defined(Q_WS_WINCE) || defined(Q_WS_S60) || defined(Q_WS_X11) const bool as_gui_menubar = !isNativeMenuBar(); #else const bool as_gui_menubar = true; @@ -1679,6 +1732,9 @@ QSize QMenuBar::minimumSizeHint() const ret += QSize(2*fw + hmargin, 2*fw + vmargin); } int margin = 2*vmargin + 2*fw + spaceBelowMenuBar; +#ifdef Q_WS_X11 + if (d->platformMenuBar->allowCornerWidgets()) { +#endif if(d->leftWidget) { QSize sz = d->leftWidget->minimumSizeHint(); ret.setWidth(ret.width() + sz.width()); @@ -1691,6 +1747,9 @@ QSize QMenuBar::minimumSizeHint() const if(sz.height() + margin > ret.height()) ret.setHeight(sz.height() + margin); } +#ifdef Q_WS_X11 + } +#endif if(as_gui_menubar) { QStyleOptionMenuItem opt; opt.rect = rect(); @@ -1712,7 +1771,7 @@ QSize QMenuBar::minimumSizeHint() const QSize QMenuBar::sizeHint() const { Q_D(const QMenuBar); -#if defined(Q_WS_MAC) || defined(Q_WS_WINCE) || defined(Q_WS_S60) +#if defined(Q_WS_MAC) || defined(Q_WS_WINCE) || defined(Q_WS_S60) || defined(Q_WS_X11) const bool as_gui_menubar = !isNativeMenuBar(); #else const bool as_gui_menubar = true; @@ -1738,6 +1797,9 @@ QSize QMenuBar::sizeHint() const ret += QSize(fw + hmargin, fw + vmargin); } int margin = 2*vmargin + 2*fw + spaceBelowMenuBar; +#ifdef Q_WS_X11 + if(d->platformMenuBar->allowCornerWidgets()) { +#endif if(d->leftWidget) { QSize sz = d->leftWidget->sizeHint(); ret.setWidth(ret.width() + sz.width()); @@ -1750,6 +1812,9 @@ QSize QMenuBar::sizeHint() const if(sz.height() + margin > ret.height()) ret.setHeight(sz.height() + margin); } +#ifdef Q_WS_X11 + } +#endif if(as_gui_menubar) { QStyleOptionMenuItem opt; opt.rect = rect(); @@ -1771,7 +1836,7 @@ QSize QMenuBar::sizeHint() const int QMenuBar::heightForWidth(int) const { Q_D(const QMenuBar); -#if defined(Q_WS_MAC) || defined(Q_WS_WINCE) || defined(Q_WS_S60) +#if defined(Q_WS_MAC) || defined(Q_WS_WINCE) || defined(Q_WS_S60) || defined(Q_WS_X11) const bool as_gui_menubar = !isNativeMenuBar(); #else const bool as_gui_menubar = true; @@ -1791,10 +1856,16 @@ int QMenuBar::heightForWidth(int) const height += 2*vmargin; } int margin = 2*vmargin + 2*fw + spaceBelowMenuBar; +#ifdef Q_WS_X11 + if(d->platformMenuBar->allowCornerWidgets()) { +#endif if(d->leftWidget) height = qMax(d->leftWidget->sizeHint().height() + margin, height); if(d->rightWidget) height = qMax(d->rightWidget->sizeHint().height() + margin, height); +#ifdef Q_WS_X11 + } +#endif if(as_gui_menubar) { QStyleOptionMenuItem opt; opt.init(this); @@ -1814,7 +1885,15 @@ void QMenuBarPrivate::_q_internalShortcutActivated(int id) { Q_Q(QMenuBar); QAction *act = actions.at(id); - setCurrentAction(act, true, true); +#ifdef Q_WS_X11 + if (q->isNativeMenuBar()) { + platformMenuBar->popupAction(act); + } else { +#endif + setCurrentAction(act, true, true); +#ifdef Q_WS_X11 + } +#endif if (act && !act->menu()) { activateAction(act, QAction::Trigger); //100 is the same as the default value in QPushButton::animateClick @@ -1835,6 +1914,39 @@ void QMenuBarPrivate::_q_updateLayout() } } +#ifdef Q_WS_X11 +void QMenuBarPrivate::updateCornerWidgetToolBar() +{ + Q_Q(QMenuBar); + if (!cornerWidgetToolBar) { + QMainWindow *window = qobject_cast<QMainWindow *>(q->window()); + if (!window) { + qWarning() << "Menubar parent is not a QMainWindow, not showing corner widgets"; + return; + } + cornerWidgetToolBar = window->addToolBar(QApplication::translate("QMenuBar", "Corner Toolbar")); + cornerWidgetToolBar->setObjectName(QLatin1String("CornerToolBar")); + cornerWidgetContainer = new QWidget; + cornerWidgetToolBar->addWidget(cornerWidgetContainer); + new QHBoxLayout(cornerWidgetContainer); + } else { + QLayout *layout = cornerWidgetContainer->layout(); + while (layout->count() > 0) { + layout->takeAt(0); + } + } + if (leftWidget) { + leftWidget->setParent(cornerWidgetContainer); + cornerWidgetContainer->layout()->addWidget(leftWidget); + } + if (rightWidget) { + rightWidget->setParent(cornerWidgetContainer); + cornerWidgetContainer->layout()->addWidget(rightWidget); + } +} +#endif + + /*! \fn void QMenuBar::setCornerWidget(QWidget *widget, Qt::Corner corner) @@ -1867,10 +1979,18 @@ void QMenuBar::setCornerWidget(QWidget *w, Qt::Corner corner) return; } +#ifdef Q_WS_X11 + if(!d->platformMenuBar->allowCornerWidgets()) { + d->updateCornerWidgetToolBar(); + } else { +#endif if (w) { w->setParent(this); w->installEventFilter(this); } +#ifdef Q_WS_X11 + } +#endif d->_q_updateLayout(); } @@ -1920,6 +2040,9 @@ QWidget *QMenuBar::cornerWidget(Qt::Corner corner) const void QMenuBar::setNativeMenuBar(bool nativeMenuBar) { Q_D(QMenuBar); +#ifdef Q_WS_X11 + d->platformMenuBar->setNativeMenuBar(nativeMenuBar); +#else if (d->nativeMenuBar == -1 || (nativeMenuBar != bool(d->nativeMenuBar))) { d->nativeMenuBar = nativeMenuBar; #ifdef Q_WS_MAC @@ -1944,15 +2067,20 @@ void QMenuBar::setNativeMenuBar(bool nativeMenuBar) setVisible(true); #endif } +#endif // Q_WS_X11 } bool QMenuBar::isNativeMenuBar() const { Q_D(const QMenuBar); +#ifdef Q_WS_X11 + return d->platformMenuBar->isNativeMenuBar(); +#else if (d->nativeMenuBar == -1) { return !QApplication::instance()->testAttribute(Qt::AA_DontUseNativeMenuBar); } return d->nativeMenuBar; +#endif } /*! diff --git a/src/gui/widgets/qmenubar_p.h b/src/gui/widgets/qmenubar_p.h index 1ac694e..9a1f758 100644 --- a/src/gui/widgets/qmenubar_p.h +++ b/src/gui/widgets/qmenubar_p.h @@ -47,7 +47,7 @@ // ------------- // // This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to +// platformMenuBarementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. @@ -61,6 +61,10 @@ #include "qguifunctions_wince.h" #endif +#ifdef Q_WS_X11 +#include "qabstractplatformmenubar_p.h" +#endif + #ifndef QT_NO_MENUBAR #ifdef Q_WS_S60 class CCoeControl; @@ -71,21 +75,27 @@ class CEikMenuBar; QT_BEGIN_NAMESPACE #ifndef QT_NO_MENUBAR +class QToolBar; class QMenuBarExtension; class QMenuBarPrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QMenuBar) public: QMenuBarPrivate() : itemsDirty(0), currentAction(0), mouseDown(0), - closePopupMode(0), defaultPopDown(1), popupState(0), keyboardState(0), altPressed(0), - nativeMenuBar(-1), doChildEffects(false) + closePopupMode(0), defaultPopDown(1), popupState(0), keyboardState(0), altPressed(0) +#ifndef Q_WS_X11 + , nativeMenuBar(-1) +#endif + , doChildEffects(false) #ifdef QT3_SUPPORT , doAutoResize(false) #endif #ifdef Q_WS_MAC , mac_menubar(0) #endif - +#ifdef Q_WS_X11 + , platformMenuBar(0) +#endif #ifdef Q_WS_WINCE , wce_menubar(0), wceClassicMenu(false) #endif @@ -96,6 +106,9 @@ public: { } ~QMenuBarPrivate() { +#ifdef Q_WS_X11 + delete platformMenuBar; +#endif #ifdef Q_WS_MAC delete mac_menubar; #endif @@ -136,8 +149,9 @@ public: uint keyboardState : 1, altPressed : 1; QPointer<QWidget> keyboardFocusWidget; - +#ifndef Q_WS_X11 int nativeMenuBar : 3; // Only has values -1, 0, and 1 +#endif //firing of events void activateAction(QAction *, QAction::ActionEvent); @@ -173,6 +187,9 @@ public: #ifdef QT3_SUPPORT bool doAutoResize; #endif +#ifdef Q_WS_X11 + QAbstractPlatformMenuBar *platformMenuBar; +#endif #ifdef Q_WS_MAC //mac menubar binding struct QMacMenuBarPrivate { @@ -181,7 +198,7 @@ public: QMacMenuBarPrivate(); ~QMacMenuBarPrivate(); - void addAction(QAction *, QMacMenuAction* =0); + void addAction(QAction *, QAction* =0); void addAction(QMacMenuAction *, QMacMenuAction* =0); void syncAction(QMacMenuAction *); inline void syncAction(QAction *a) { syncAction(findAction(a)); } @@ -220,7 +237,7 @@ public: QWceMenuBarPrivate(QMenuBarPrivate *menubar); ~QWceMenuBarPrivate(); - void addAction(QAction *, QWceMenuAction* =0); + void addAction(QAction *, QAction* =0); void addAction(QWceMenuAction *, QWceMenuAction* =0); void syncAction(QWceMenuAction *); inline void syncAction(QAction *a) { syncAction(findAction(a)); } @@ -250,7 +267,7 @@ public: QMenuBarPrivate *d; QSymbianMenuBarPrivate(QMenuBarPrivate *menubar); ~QSymbianMenuBarPrivate(); - void addAction(QAction *, QSymbianMenuAction* =0); + void addAction(QAction *, QAction* =0); void addAction(QSymbianMenuAction *, QSymbianMenuAction* =0); void syncAction(QSymbianMenuAction *); inline void syncAction(QAction *a) { syncAction(findAction(a)); } @@ -273,6 +290,12 @@ public: #ifdef QT_SOFTKEYS_ENABLED QAction *menuBarAction; #endif + +#ifdef Q_WS_X11 + void updateCornerWidgetToolBar(); + QToolBar *cornerWidgetToolBar; + QWidget *cornerWidgetContainer; +#endif }; #endif diff --git a/src/gui/widgets/qmenubar_x11.cpp b/src/gui/widgets/qmenubar_x11.cpp new file mode 100644 index 0000000..25336d4 --- /dev/null +++ b/src/gui/widgets/qmenubar_x11.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmenubar_x11_p.h" + +#ifndef QT_NO_MENUBAR + +#include "qapplication.h" +#include "qdebug.h" +#include "qevent.h" +#include "qmenu.h" +#include "qmenubar.h" + +#include <private/qfactoryloader_p.h> + +QT_BEGIN_NAMESPACE + +QX11MenuBar::~QX11MenuBar() +{ +} + +void QX11MenuBar::init(QMenuBar *_menuBar) +{ + nativeMenuBar = -1; + menuBar = _menuBar; +} + +void QX11MenuBar::setVisible(bool visible) +{ + menuBar->QWidget::setVisible(visible); +} + +void QX11MenuBar::actionEvent(QActionEvent *e) +{ + Q_UNUSED(e); +} + +void QX11MenuBar::handleReparent(QWidget *oldParent, QWidget *newParent, QWidget *oldWindow, QWidget *newWindow) +{ + Q_UNUSED(oldParent) + Q_UNUSED(newParent) + Q_UNUSED(oldWindow) + Q_UNUSED(newWindow) +} + +bool QX11MenuBar::allowCornerWidgets() const +{ + return true; +} + +void QX11MenuBar::popupAction(QAction *) +{ +} + +void QX11MenuBar::setNativeMenuBar(bool value) +{ + if (nativeMenuBar == -1 || (value != bool(nativeMenuBar))) { + nativeMenuBar = value; + } +} + +bool QX11MenuBar::isNativeMenuBar() const +{ + return false; +} + +bool QX11MenuBar::shortcutsHandledByNativeMenuBar() const +{ + return false; +} + +bool QX11MenuBar::menuBarEventFilter(QObject *, QEvent *) +{ + return false; +} + +struct QX11MenuBarFactory : public QPlatformMenuBarFactoryInterface +{ + QAbstractPlatformMenuBar *create() { return new QX11MenuBar; } + virtual QStringList keys() const { return QStringList(); } +}; + +QPlatformMenuBarFactoryInterface *qt_guiPlatformMenuBarFactory() +{ + static QPlatformMenuBarFactoryInterface *factory = 0; + if (!factory) { +#ifndef QT_NO_LIBRARY + QFactoryLoader loader(QPlatformMenuBarFactoryInterface_iid, QLatin1String("/menubar")); + factory = qobject_cast<QPlatformMenuBarFactoryInterface *>(loader.instance(QLatin1String("default"))); +#endif // QT_NO_LIBRARY + if(!factory) { + static QX11MenuBarFactory def; + factory = &def; + } + } + return factory; +} + +QT_END_NAMESPACE + +#endif // QT_NO_MENUBAR diff --git a/src/gui/widgets/qmenubar_x11_p.h b/src/gui/widgets/qmenubar_x11_p.h new file mode 100644 index 0000000..41debd0 --- /dev/null +++ b/src/gui/widgets/qmenubar_x11_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QX11MENUBAR_P_H +#define QX11MENUBAR_P_H + +#ifndef QT_NO_MENUBAR + +#include "qabstractplatformmenubar_p.h" + +QT_BEGIN_NAMESPACE + +class QMenuBar; + +class QX11MenuBar : public QAbstractPlatformMenuBar +{ +public: + ~QX11MenuBar(); + + virtual void init(QMenuBar *); + + virtual void setVisible(bool visible); + + virtual void actionEvent(QActionEvent *e); + + virtual void handleReparent(QWidget *oldParent, QWidget *newParent, QWidget *oldWindow, QWidget *newWindow); + + virtual bool allowCornerWidgets() const; + + virtual void popupAction(QAction*); + + virtual void setNativeMenuBar(bool); + virtual bool isNativeMenuBar() const; + + virtual bool shortcutsHandledByNativeMenuBar() const; + virtual bool menuBarEventFilter(QObject *, QEvent *event); + +private: + QMenuBar *menuBar; + int nativeMenuBar : 3; // Only has values -1, 0, and 1 +}; + +QPlatformMenuBarFactoryInterface *qt_guiPlatformMenuBarFactory(); + +QT_END_NAMESPACE + +#endif // QT_NO_MENUBAR + +#endif /* QX11MENUBAR_P_H */ diff --git a/src/gui/widgets/qplaintextedit.cpp b/src/gui/widgets/qplaintextedit.cpp index 52efaef..f2fca8f 100644 --- a/src/gui/widgets/qplaintextedit.cpp +++ b/src/gui/widgets/qplaintextedit.cpp @@ -483,6 +483,7 @@ int QPlainTextEditPrivate::verticalOffset(int topBlock, int topLine) const QPlainTextDocumentLayout *documentLayout = qobject_cast<QPlainTextDocumentLayout*>(doc->documentLayout()); Q_ASSERT(documentLayout); QRectF r = documentLayout->blockBoundingRect(currentBlock); + Q_UNUSED(r); QTextLayout *layout = currentBlock.layout(); if (layout && topLine <= layout->lineCount()) { QTextLine line = layout->lineAt(topLine - 1); @@ -648,6 +649,11 @@ void QPlainTextEditPrivate::setTopBlock(int blockNumber, int lineNumber, int dx) } control->topBlock = blockNumber; topLine = lineNumber; + + bool vbarSignalsBlocked = vbar->blockSignals(true); + vbar->setValue(block.firstLineNumber() + lineNumber); + vbar->blockSignals(vbarSignalsBlocked); + if (dx || dy) viewport->scroll(q->isRightToLeft() ? -dx : dx, dy); else diff --git a/src/gui/widgets/qradiobutton.cpp b/src/gui/widgets/qradiobutton.cpp index 92d1a8d..9ce4eba 100644 --- a/src/gui/widgets/qradiobutton.cpp +++ b/src/gui/widgets/qradiobutton.cpp @@ -206,6 +206,15 @@ QSize QRadioButton::sizeHint() const /*! \reimp + \since 4.8 +*/ +QSize QRadioButton::minimumSizeHint() const +{ + return sizeHint(); +} + +/*! + \reimp */ bool QRadioButton::hitButton(const QPoint &pos) const { diff --git a/src/gui/widgets/qradiobutton.h b/src/gui/widgets/qradiobutton.h index 5b5cce8..2e29876 100644 --- a/src/gui/widgets/qradiobutton.h +++ b/src/gui/widgets/qradiobutton.h @@ -62,6 +62,7 @@ public: explicit QRadioButton(const QString &text, QWidget *parent=0); QSize sizeHint() const; + QSize minimumSizeHint() const; protected: bool event(QEvent *e); diff --git a/src/gui/widgets/qsizegrip.cpp b/src/gui/widgets/qsizegrip.cpp index d566000..dd77a2f 100644 --- a/src/gui/widgets/qsizegrip.cpp +++ b/src/gui/widgets/qsizegrip.cpp @@ -78,15 +78,6 @@ static QWidget *qt_sizegrip_topLevelWidget(QWidget* w) return w; } -static inline bool hasHeightForWidth(QWidget *widget) -{ - if (!widget) - return false; - if (QLayout *layout = widget->layout()) - return layout->hasHeightForWidth(); - return widget->sizePolicy().hasHeightForWidth(); -} - class QSizeGripPrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QSizeGrip) @@ -318,7 +309,8 @@ void QSizeGrip::mousePressEvent(QMouseEvent * e) #ifdef Q_WS_X11 // Use a native X11 sizegrip for "real" top-level windows if supported. if (tlw->isWindow() && X11->isSupportedByWM(ATOM(_NET_WM_MOVERESIZE)) - && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw)) { + && !(tlw->windowFlags() & Qt::X11BypassWindowManagerHint) + && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !qt_widget_private(tlw)->hasHeightForWidth()) { XEvent xev; xev.xclient.type = ClientMessage; xev.xclient.message_type = ATOM(_NET_WM_MOVERESIZE); @@ -340,7 +332,7 @@ void QSizeGrip::mousePressEvent(QMouseEvent * e) } #endif // Q_WS_X11 #ifdef Q_WS_WIN - if (tlw->isWindow() && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw)) { + if (tlw->isWindow() && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !qt_widget_private(tlw)->hasHeightForWidth()) { uint orientation = 0; if (d->atBottom()) orientation = d->atLeft() ? SZ_SIZEBOTTOMLEFT : SZ_SIZEBOTTOMRIGHT; @@ -429,12 +421,13 @@ void QSizeGrip::mouseMoveEvent(QMouseEvent * e) #ifdef Q_WS_X11 if (tlw->isWindow() && X11->isSupportedByWM(ATOM(_NET_WM_MOVERESIZE)) - && tlw->isTopLevel() && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw)) + && tlw->isTopLevel() && !(tlw->windowFlags() & Qt::X11BypassWindowManagerHint) + && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !qt_widget_private(tlw)->hasHeightForWidth()) return; #endif #ifdef Q_WS_WIN if (tlw->isWindow() && GetSystemMenu(tlw->winId(), FALSE) != 0 && internalWinId() - && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw)) { + && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !qt_widget_private(tlw)->hasHeightForWidth()) { MSG msg; while(PeekMessage(&msg, winId(), WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)); return; diff --git a/src/gui/widgets/qspinbox.cpp b/src/gui/widgets/qspinbox.cpp index e8382ed..952f9e2 100644 --- a/src/gui/widgets/qspinbox.cpp +++ b/src/gui/widgets/qspinbox.cpp @@ -697,6 +697,9 @@ void QDoubleSpinBox::setSuffix(const QString &suffix) d->suffix = suffix; d->updateEdit(); + + d->cachedSizeHint = QSize(); + updateGeometry(); } /*! diff --git a/src/gui/widgets/qsplashscreen.cpp b/src/gui/widgets/qsplashscreen.cpp index 40ce9a6..2da4a8b 100644 --- a/src/gui/widgets/qsplashscreen.cpp +++ b/src/gui/widgets/qsplashscreen.cpp @@ -64,7 +64,6 @@ public: int currAlign; inline QSplashScreenPrivate(); - void drawContents(); }; /*! @@ -121,10 +120,9 @@ public: perhaps Qt::WindowStaysOnTopHint. */ QSplashScreen::QSplashScreen(const QPixmap &pixmap, Qt::WindowFlags f) - : QWidget(*(new QSplashScreenPrivate()), 0, Qt::SplashScreen | f) + : QWidget(*(new QSplashScreenPrivate()), 0, Qt::SplashScreen | Qt::FramelessWindowHint | f) { - d_func()->pixmap = pixmap; - setPixmap(d_func()->pixmap); // Does an implicit repaint + setPixmap(pixmap); // Does an implicit repaint } /*! @@ -165,7 +163,6 @@ void QSplashScreen::mousePressEvent(QMouseEvent *) */ void QSplashScreen::repaint() { - d_func()->drawContents(); QWidget::repaint(); QApplication::flush(); } @@ -226,8 +223,8 @@ void QSplashScreen::finish(QWidget *mainWin) { if (mainWin) { #if defined(Q_WS_X11) - extern void qt_x11_wait_for_window_manager(QWidget *mainWin); - qt_x11_wait_for_window_manager(mainWin); + extern void qt_x11_wait_for_window_manager(QWidget *mainWin, bool); + qt_x11_wait_for_window_manager(mainWin, false); #endif } close(); @@ -241,23 +238,13 @@ void QSplashScreen::setPixmap(const QPixmap &pixmap) { Q_D(QSplashScreen); - if (pixmap.hasAlpha()) { - QPixmap opaque(pixmap.size()); - QPainter p(&opaque); - p.fillRect(0, 0, pixmap.width(), pixmap.height(), palette().background()); - p.drawPixmap(0, 0, pixmap); - p.end(); - d->pixmap = opaque; - } else { - d->pixmap = pixmap; - } + d->pixmap = pixmap; + setAttribute(Qt::WA_TranslucentBackground, pixmap.hasAlpha()); - QRect r(0, 0, d->pixmap.size().width(), d->pixmap.size().height()); - resize(d->pixmap.size()); + QRect r(QPoint(), d->pixmap.size()); + resize(r.size()); move(QApplication::desktop()->screenGeometry().center() - r.center()); - if (!isVisible()) - d->drawContents(); - else + if (isVisible()) repaint(); } @@ -271,23 +258,6 @@ const QPixmap QSplashScreen::pixmap() const } /*! - \internal -*/ -void QSplashScreenPrivate::drawContents() -{ - Q_Q(QSplashScreen); - QPixmap textPix = pixmap; - if (!textPix.isNull()) { - QPainter painter(&textPix); - painter.initFrom(q); - q->drawContents(&painter); - QPalette p = q->palette(); - p.setBrush(q->backgroundRole(), QBrush(textPix)); - q->setPalette(p); - } -} - -/*! \internal */ inline QSplashScreenPrivate::QSplashScreenPrivate() : currAlign(Qt::AlignLeft) @@ -304,8 +274,7 @@ void QSplashScreen::drawContents(QPainter *painter) { Q_D(QSplashScreen); painter->setPen(d->currColor); - QRect r = rect(); - r.setRect(r.x() + 5, r.y() + 5, r.width() - 10, r.height() - 10); + QRect r = rect().adjusted(5, 5, -5, -5); if (Qt::mightBeRichText(d->currStatus)) { QTextDocument doc; #ifdef QT_NO_TEXTHTMLPARSER @@ -346,6 +315,13 @@ void QSplashScreen::drawContents(QPainter *painter) /*! \reimp */ bool QSplashScreen::event(QEvent *e) { + if (e->type() == QEvent::Paint) { + Q_D(QSplashScreen); + QPainter painter(this); + if (!d->pixmap.isNull()) + painter.drawPixmap(QPoint(), d->pixmap); + drawContents(&painter); + } return QWidget::event(e); } diff --git a/src/gui/widgets/qstackedwidget.cpp b/src/gui/widgets/qstackedwidget.cpp index 294c780..c4bf79a 100644 --- a/src/gui/widgets/qstackedwidget.cpp +++ b/src/gui/widgets/qstackedwidget.cpp @@ -49,12 +49,54 @@ QT_BEGIN_NAMESPACE +/** + QStackedLayout does not support height for width (simply because it does not reimplement + heightForWidth() and hasHeightForWidth()). That is not possible to fix without breaking + binary compatibility. (QLayout is subject to multiple inheritance). + However, we can fix QStackedWidget by simply using a modified version of QStackedLayout + that reimplements the hfw-related functions: + */ +class QStackedLayoutHFW : public QStackedLayout +{ +public: + QStackedLayoutHFW(QWidget *parent = 0) : QStackedLayout(parent) {} + bool hasHeightForWidth() const; + int heightForWidth(int width) const; +}; + +bool QStackedLayoutHFW::hasHeightForWidth() const +{ + const int n = count(); + + for (int i = 0; i < n; ++i) { + if (QLayoutItem *item = itemAt(i)) { + if (item->hasHeightForWidth()) + return true; + } + } + return false; +} + +int QStackedLayoutHFW::heightForWidth(int width) const +{ + const int n = count(); + + int hfw = 0; + for (int i = 0; i < n; ++i) { + if (QLayoutItem *item = itemAt(i)) { + hfw = qMax(hfw, item->heightForWidth(width)); + } + } + return hfw; +} + + class QStackedWidgetPrivate : public QFramePrivate { Q_DECLARE_PUBLIC(QStackedWidget) public: QStackedWidgetPrivate():layout(0){} - QStackedLayout *layout; + QStackedLayoutHFW *layout; bool blockChildAdd; }; @@ -138,7 +180,7 @@ QStackedWidget::QStackedWidget(QWidget *parent) : QFrame(*new QStackedWidgetPrivate, parent) { Q_D(QStackedWidget); - d->layout = new QStackedLayout(this); + d->layout = new QStackedLayoutHFW(this); connect(d->layout, SIGNAL(widgetRemoved(int)), this, SIGNAL(widgetRemoved(int))); connect(d->layout, SIGNAL(currentChanged(int)), this, SIGNAL(currentChanged(int))); } diff --git a/src/gui/widgets/qtabbar.cpp b/src/gui/widgets/qtabbar.cpp index c551f3e..8faf156 100644 --- a/src/gui/widgets/qtabbar.cpp +++ b/src/gui/widgets/qtabbar.cpp @@ -171,7 +171,7 @@ void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const if (tabIndex > 0 && tabIndex - 1 == d->currentIndex) option->selectedPosition = QStyleOptionTab::PreviousIsSelected; - else if (tabIndex < totalTabs - 1 && tabIndex + 1 == d->currentIndex) + else if (tabIndex + 1 < totalTabs && tabIndex + 1 == d->currentIndex) option->selectedPosition = QStyleOptionTab::NextIsSelected; else option->selectedPosition = QStyleOptionTab::NotAdjacent; @@ -1292,6 +1292,8 @@ QSize QTabBar::sizeHint() const QSize QTabBar::minimumSizeHint() const { Q_D(const QTabBar); + if (d->layoutDirty) + const_cast<QTabBarPrivate*>(d)->layoutTabs(); if (!d->useScrollButtons) { QRect r; for (int i = 0; i < d->tabList.count(); ++i) @@ -1304,22 +1306,23 @@ QSize QTabBar::minimumSizeHint() const return QSize(d->rightB->sizeHint().width() * 2 + 75, sizeHint().height()); } +// Compute the most-elided possible text, for minimumSizeHint static QString computeElidedText(Qt::TextElideMode mode, const QString &text) { - if (text.length() <= 7) + if (text.length() <= 3) return text; static const QLatin1String Ellipses("..."); QString ret; switch (mode) { case Qt::ElideRight: - ret = text.left(4) + Ellipses; + ret = text.left(2) + Ellipses; break; case Qt::ElideMiddle: - ret = text.left(2) + Ellipses + text.right(2); + ret = text.left(1) + Ellipses + text.right(1); break; case Qt::ElideLeft: - ret = Ellipses + text.right(4); + ret = Ellipses + text.right(2); break; case Qt::ElideNone: ret = text; @@ -1966,7 +1969,7 @@ void QTabBar::keyPressEvent(QKeyEvent *event) event->ignore(); return; } - int offset = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1; + int offset = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1; d->setCurrentNextEnabledIndex(offset); } diff --git a/src/gui/widgets/qtabwidget.cpp b/src/gui/widgets/qtabwidget.cpp index ae56e62..78dda23 100644 --- a/src/gui/widgets/qtabwidget.cpp +++ b/src/gui/widgets/qtabwidget.cpp @@ -195,6 +195,7 @@ public: void _q_removeTab(int); void _q_tabMoved(int from, int to); void init(); + bool hasHeightForWidth() const; QTabBar *tabs; QStackedWidget *stack; @@ -246,6 +247,15 @@ void QTabWidgetPrivate::init() } +bool QTabWidgetPrivate::hasHeightForWidth() const +{ + bool has = size_policy.hasHeightForWidth(); + if (!has && stack) + has = qt_widget_private(stack)->hasHeightForWidth(); + return has; +} + + /*! Initialize \a option with the values from this QTabWidget. This method is useful for subclasses when they need a QStyleOptionTabWidgetFrame, but don't want to fill @@ -816,8 +826,8 @@ QSize QTabWidget::sizeHint() const { Q_D(const QTabWidget); QSize lc(0, 0), rc(0, 0); - QStyleOption opt(0); - opt.init(this); + QStyleOptionTabWidgetFrameV2 opt; + initStyleOption(&opt); opt.state = QStyle::State_None; if (d->leftCornerWidget) @@ -865,8 +875,8 @@ QSize QTabWidget::minimumSizeHint() const QSize sz = basicSize(d->pos == North || d->pos == South, lc, rc, s, t); - QStyleOption opt(0); - opt.rect = rect(); + QStyleOptionTabWidgetFrameV2 opt; + initStyleOption(&opt); opt.palette = palette(); opt.state = QStyle::State_None; return style()->sizeFromContents(QStyle::CT_TabWidget, &opt, sz, this) @@ -875,6 +885,51 @@ QSize QTabWidget::minimumSizeHint() const /*! \reimp + \since 4.8 +*/ +int QTabWidget::heightForWidth(int width) const +{ + Q_D(const QTabWidget); + QStyleOptionTabWidgetFrameV2 opt; + initStyleOption(&opt); + opt.state = QStyle::State_None; + + QSize zero(0,0); + const QSize padding = style()->sizeFromContents(QStyle::CT_TabWidget, &opt, zero, this) + .expandedTo(QApplication::globalStrut()); + + QSize lc(0, 0), rc(0, 0); + if (d->leftCornerWidget) + lc = d->leftCornerWidget->sizeHint(); + if(d->rightCornerWidget) + rc = d->rightCornerWidget->sizeHint(); + if (!d->dirty) { + QTabWidget *that = (QTabWidget*)this; + that->setUpLayout(true); + } + QSize t(d->tabs->sizeHint()); + + if(usesScrollButtons()) + t = t.boundedTo(QSize(200,200)); + else + t = t.boundedTo(QApplication::desktop()->size()); + + const bool tabIsHorizontal = (d->pos == North || d->pos == South); + const int contentsWidth = width - padding.width(); + int stackWidth = contentsWidth; + if (!tabIsHorizontal) + stackWidth -= qMax(t.width(), qMax(lc.width(), rc.width())); + + int stackHeight = d->stack->heightForWidth(stackWidth); + QSize s(stackWidth, stackHeight); + + QSize contentSize = basicSize(tabIsHorizontal, lc, rc, s, t); + return (contentSize + padding).expandedTo(QApplication::globalStrut()).height(); +} + + +/*! + \reimp */ void QTabWidget::showEvent(QShowEvent *) { diff --git a/src/gui/widgets/qtabwidget.h b/src/gui/widgets/qtabwidget.h index 696906d..6a026c0 100644 --- a/src/gui/widgets/qtabwidget.h +++ b/src/gui/widgets/qtabwidget.h @@ -129,6 +129,7 @@ public: QSize sizeHint() const; QSize minimumSizeHint() const; + int heightForWidth(int width) const; void setCornerWidget(QWidget * w, Qt::Corner corner = Qt::TopRightCorner); QWidget * cornerWidget(Qt::Corner corner = Qt::TopRightCorner) const; diff --git a/src/gui/widgets/qtextedit.cpp b/src/gui/widgets/qtextedit.cpp index b6661c9..61d4fed 100644 --- a/src/gui/widgets/qtextedit.cpp +++ b/src/gui/widgets/qtextedit.cpp @@ -2616,7 +2616,6 @@ Qt::TextFormat QTextEdit::textFormat() const void QTextEdit::append(const QString &text) { Q_D(QTextEdit); - QTextBlock lastBlock = d->control->document()->lastBlock(); const bool atBottom = isReadOnly() ? d->verticalOffset() >= d->vbar->maximum() : d->control->textCursor().atEnd(); d->control->append(text); diff --git a/src/gui/widgets/qtoolbarlayout.cpp b/src/gui/widgets/qtoolbarlayout.cpp index 98b1fc5..971fdda 100644 --- a/src/gui/widgets/qtoolbarlayout.cpp +++ b/src/gui/widgets/qtoolbarlayout.cpp @@ -647,15 +647,15 @@ QSize QToolBarLayout::expandedSize(const QSize &size) const void QToolBarLayout::setExpanded(bool exp) { - if (exp == expanded) + QWidget *tb = qobject_cast<QToolBar*>(parentWidget()); + if (!tb) + return; + if (exp == expanded && !tb->isWindow()) return; expanded = exp; extension->setChecked(expanded); - QToolBar *tb = qobject_cast<QToolBar*>(parentWidget()); - if (!tb) - return; if (QMainWindow *win = qobject_cast<QMainWindow*>(tb->parentWidget())) { #ifdef QT_NO_DOCKWIDGET animating = false; diff --git a/src/gui/widgets/qtoolbutton.cpp b/src/gui/widgets/qtoolbutton.cpp index 6c49c2b..0d765d2 100644 --- a/src/gui/widgets/qtoolbutton.cpp +++ b/src/gui/widgets/qtoolbutton.cpp @@ -394,9 +394,6 @@ void QToolButton::initStyleOption(QStyleOptionToolButton *option) const option->toolButtonStyle = Qt::ToolButtonTextOnly; else if (option->toolButtonStyle != Qt::ToolButtonTextOnly) option->toolButtonStyle = Qt::ToolButtonIconOnly; - } else { - if (d->text.isEmpty() && option->toolButtonStyle != Qt::ToolButtonIconOnly) - option->toolButtonStyle = Qt::ToolButtonIconOnly; } option->pos = pos(); diff --git a/src/gui/widgets/qvalidator.h b/src/gui/widgets/qvalidator.h index 3f4579f..70f656e 100644 --- a/src/gui/widgets/qvalidator.h +++ b/src/gui/widgets/qvalidator.h @@ -101,7 +101,7 @@ class Q_GUI_EXPORT QIntValidator : public QValidator public: explicit QIntValidator(QObject * parent = 0); - QIntValidator(int bottom, int top, QObject * parent); + QIntValidator(int bottom, int top, QObject *parent = 0); ~QIntValidator(); QValidator::State validate(QString &, int &) const; @@ -142,7 +142,7 @@ class Q_GUI_EXPORT QDoubleValidator : public QValidator public: explicit QDoubleValidator(QObject * parent = 0); - QDoubleValidator(double bottom, double top, int decimals, QObject * parent); + QDoubleValidator(double bottom, double top, int decimals, QObject *parent = 0); ~QDoubleValidator(); enum Notation { @@ -186,7 +186,7 @@ class Q_GUI_EXPORT QRegExpValidator : public QValidator public: explicit QRegExpValidator(QObject *parent = 0); - QRegExpValidator(const QRegExp& rx, QObject *parent); + QRegExpValidator(const QRegExp& rx, QObject *parent = 0); ~QRegExpValidator(); virtual QValidator::State validate(QString& input, int& pos) const; diff --git a/src/gui/widgets/qworkspace.cpp b/src/gui/widgets/qworkspace.cpp index b34a649..9516bca 100644 --- a/src/gui/widgets/qworkspace.cpp +++ b/src/gui/widgets/qworkspace.cpp @@ -1239,7 +1239,6 @@ QWidget * QWorkspace::addWindow(QWidget *w, Qt::WindowFlags flags) int x = w->x(); int y = w->y(); bool hasPos = w->testAttribute(Qt::WA_Moved); - QSize s = w->size().expandedTo(qSmartMinSize(w)); if (!hasSize && w->sizeHint().isValid()) w->adjustSize(); @@ -2923,7 +2922,7 @@ void QWorkspaceChild::setActive(bool b) iconw->setActive(act); update(); - QList<QWidget*> wl = qFindChildren<QWidget*>(childWidget); + QList<QWidget*> wl = childWidget->findChildren<QWidget*>(); if (act) { for (int i = 0; i < wl.size(); ++i) { QWidget *w = wl.at(i); diff --git a/src/gui/widgets/widgets.pri b/src/gui/widgets/widgets.pri index 937b8d6..6b3d6a9 100644 --- a/src/gui/widgets/widgets.pri +++ b/src/gui/widgets/widgets.pri @@ -144,7 +144,15 @@ SOURCES += \ widgets/qplaintextedit.cpp \ widgets/qprintpreviewwidget.cpp -!embedded:mac { +x11: { + HEADERS += \ + widgets/qabstractplatformmenubar_p.h + + SOURCES += \ + widgets/qmenubar_x11.cpp +} + +!embedded:!qpa:mac { HEADERS += widgets/qmacnativewidget_mac.h \ widgets/qmaccocoaviewcontainer_mac.h OBJECTIVE_HEADERS += widgets/qcocoatoolbardelegate_mac_p.h \ |