diff options
Diffstat (limited to 'src/plugins')
113 files changed, 3838 insertions, 568 deletions
diff --git a/src/plugins/accessible/widgets/itemviews.cpp b/src/plugins/accessible/widgets/itemviews.cpp index a3fcc22fc4..17764a3c2e 100644 --- a/src/plugins/accessible/widgets/itemviews.cpp +++ b/src/plugins/accessible/widgets/itemviews.cpp @@ -299,9 +299,28 @@ bool QAccessibleTable::selectRow(int row) if (!view()->model() || !view()->selectionModel()) return false; QModelIndex index = view()->model()->index(row, 0, view()->rootIndex()); - if (!index.isValid() || view()->selectionMode() & QAbstractItemView::NoSelection) + + if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectColumns) + return false; + + switch (view()->selectionMode()) { + case QAbstractItemView::NoSelection: return false; - view()->selectionModel()->select(index, QItemSelectionModel::Select); + case QAbstractItemView::SingleSelection: + if (view()->selectionBehavior() != QAbstractItemView::SelectRows && columnCount() > 1 ) + return false; + view()->clearSelection(); + break; + case QAbstractItemView::ContiguousSelection: + if ((!row || !view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) + && !view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) + view()->clearSelection(); + break; + default: + break; + } + + view()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); return true; } @@ -310,9 +329,26 @@ bool QAccessibleTable::selectColumn(int column) if (!view()->model() || !view()->selectionModel()) return false; QModelIndex index = view()->model()->index(0, column, view()->rootIndex()); - if (!index.isValid() || view()->selectionMode() & QAbstractItemView::NoSelection) + + if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectRows) + return false; + + switch (view()->selectionMode()) { + case QAbstractItemView::NoSelection: return false; - view()->selectionModel()->select(index, QItemSelectionModel::Select); + case QAbstractItemView::SingleSelection: + if (view()->selectionBehavior() != QAbstractItemView::SelectColumns && rowCount() > 1) + return false; + case QAbstractItemView::ContiguousSelection: + if ((!column || !view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) + && !view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) + view()->clearSelection(); + break; + default: + break; + } + + view()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Columns); return true; } @@ -320,10 +356,35 @@ bool QAccessibleTable::unselectRow(int row) { if (!view()->model() || !view()->selectionModel()) return false; + QModelIndex index = view()->model()->index(row, 0, view()->rootIndex()); - if (!index.isValid() || view()->selectionMode() & QAbstractItemView::NoSelection) + if (!index.isValid()) return false; - view()->selectionModel()->select(index, QItemSelectionModel::Deselect); + + QItemSelection selection(index, index); + + switch (view()->selectionMode()) { + case QAbstractItemView::SingleSelection: + //In SingleSelection and ContiguousSelection once an item + //is selected, there's no way for the user to unselect all items + if (selectedRowCount() == 1) + return false; + break; + case QAbstractItemView::ContiguousSelection: + if (selectedRowCount() == 1) + return false; + + if ((!row || view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) + && view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) { + //If there are rows selected both up the current row and down the current rown, + //the ones which are down the current row will be deselected + selection = QItemSelection(index, view()->model()->index(rowCount() - 1, 0, view()->rootIndex())); + } + default: + break; + } + + view()->selectionModel()->select(selection, QItemSelectionModel::Deselect | QItemSelectionModel::Rows); return true; } @@ -331,10 +392,35 @@ bool QAccessibleTable::unselectColumn(int column) { if (!view()->model() || !view()->selectionModel()) return false; + QModelIndex index = view()->model()->index(0, column, view()->rootIndex()); - if (!index.isValid() || view()->selectionMode() & QAbstractItemView::NoSelection) + if (!index.isValid()) return false; - view()->selectionModel()->select(index, QItemSelectionModel::Columns & QItemSelectionModel::Deselect); + + QItemSelection selection(index, index); + + switch (view()->selectionMode()) { + case QAbstractItemView::SingleSelection: + //In SingleSelection and ContiguousSelection once an item + //is selected, there's no way for the user to unselect all items + if (selectedColumnCount() == 1) + return false; + break; + case QAbstractItemView::ContiguousSelection: + if (selectedColumnCount() == 1) + return false; + + if ((!column || view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) + && view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) { + //If there are columns selected both at the left of the current row and at the right + //of the current rown, the ones which are at the right will be deselected + selection = QItemSelection(index, view()->model()->index(0, columnCount() - 1, view()->rootIndex())); + } + default: + break; + } + + view()->selectionModel()->select(selection, QItemSelectionModel::Deselect | QItemSelectionModel::Columns); return true; } @@ -576,9 +662,28 @@ bool QAccessibleTree::selectRow(int row) if (!view()->selectionModel()) return false; QModelIndex index = indexFromLogical(row); - if (!index.isValid() || view()->selectionMode() & QAbstractItemView::NoSelection) + + if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectColumns) return false; - view()->selectionModel()->select(index, QItemSelectionModel::Select); + + switch (view()->selectionMode()) { + case QAbstractItemView::NoSelection: + return false; + case QAbstractItemView::SingleSelection: + if ((view()->selectionBehavior() != QAbstractItemView::SelectRows) && (columnCount() > 1)) + return false; + view()->clearSelection(); + break; + case QAbstractItemView::ContiguousSelection: + if ((!row || !view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) + && !view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) + view()->clearSelection(); + break; + default: + break; + } + + view()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); return true; } @@ -595,6 +700,8 @@ void *QAccessibleTableCell::interface_cast(QAccessible::InterfaceType t) { if (t == QAccessible::TableCellInterface) return static_cast<QAccessibleTableCellInterface*>(this); + if (t == QAccessible::ActionInterface) + return static_cast<QAccessibleActionInterface*>(this); return 0; } @@ -668,6 +775,89 @@ bool QAccessibleTableCell::isSelected() const return view->selectionModel()->isSelected(m_index); } +QStringList QAccessibleTableCell::actionNames() const +{ + QStringList names; + names << toggleAction(); + return names; +} + +void QAccessibleTableCell::doAction(const QString& actionName) +{ + if (actionName == toggleAction()) { + if (isSelected()) + unselectCell(); + else + selectCell(); + } +} + +QStringList QAccessibleTableCell::keyBindingsForAction(const QString& actionName) const +{ + return QStringList(); +} + + +void QAccessibleTableCell::selectCell() +{ + QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); + if (!m_index.isValid() || (selectionMode == QAbstractItemView::NoSelection)) + return; + + QSharedPointer<QAccessibleTableInterface> cellTable(table()->tableInterface()); + + switch (view->selectionBehavior()) { + case QAbstractItemView::SelectItems: + break; + case QAbstractItemView::SelectColumns: + if (cellTable.data()) + cellTable->selectColumn(m_index.column()); + return; + case QAbstractItemView::SelectRows: + if (cellTable.data()) + cellTable->selectRow(m_index.row()); + return; + } + + if (selectionMode == QAbstractItemView::SingleSelection) { + view->clearSelection(); + } + + view->selectionModel()->select(m_index, QItemSelectionModel::Select); +} + +void QAccessibleTableCell::unselectCell() +{ + + QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); + if (!m_index.isValid() || (selectionMode & QAbstractItemView::NoSelection)) + return; + + QSharedPointer<QAccessibleTableInterface> cellTable(table()->tableInterface()); + + switch (view->selectionBehavior()) { + case QAbstractItemView::SelectItems: + break; + case QAbstractItemView::SelectColumns: + if (cellTable.data()) + cellTable->unselectColumn(m_index.column()); + return; + case QAbstractItemView::SelectRows: + if (cellTable.data()) + cellTable->unselectRow(m_index.row()); + return; + } + + //If the mode is not MultiSelection or ExtendedSelection and only + //one cell is selected it cannot be unselected by the user + if ((selectionMode != QAbstractItemView::MultiSelection) + && (selectionMode != QAbstractItemView::ExtendedSelection) + && (view->selectionModel()->selectedIndexes().count() <= 1)) + return; + + view->selectionModel()->select(m_index, QItemSelectionModel::Deselect); +} + void QAccessibleTableCell::rowColumnExtents(int *row, int *column, int *rowExtents, int *columnExtents, bool *selected) const { *row = m_index.row(); @@ -856,14 +1046,11 @@ bool QAccessibleTableHeaderCell::isValid() const QAccessibleInterface *QAccessibleTableHeaderCell::parent() const { - if (false) { #ifndef QT_NO_TREEVIEW - } else if (qobject_cast<const QTreeView*>(view)) { + if (qobject_cast<const QTreeView*>(view)) return new QAccessibleTree(view); #endif - } else { - return new QAccessibleTable(view); - } + return new QAccessibleTable(view); } QAccessibleInterface *QAccessibleTableHeaderCell::child(int) const diff --git a/src/plugins/accessible/widgets/itemviews.h b/src/plugins/accessible/widgets/itemviews.h index d58d504690..baf7fd8587 100644 --- a/src/plugins/accessible/widgets/itemviews.h +++ b/src/plugins/accessible/widgets/itemviews.h @@ -159,7 +159,7 @@ private: QModelIndex indexFromLogical(int row, int column = 0) const; }; -class QAccessibleTableCell: public QAccessibleInterface, public QAccessibleTableCellInterface +class QAccessibleTableCell: public QAccessibleInterface, public QAccessibleTableCellInterface, public QAccessibleActionInterface { public: QAccessibleTableCell(QAbstractItemView *view, const QModelIndex &m_index, QAccessible::Role role); @@ -192,6 +192,11 @@ public: virtual void rowColumnExtents(int *row, int *column, int *rowExtents, int *columnExtents, bool *selected) const; virtual QAccessibleInterface* table() const; + //action interface + virtual QStringList actionNames() const; + virtual void doAction(const QString &actionName); + virtual QStringList keyBindingsForAction(const QString &actionName) const; + private: QHeaderView *verticalHeader() const; QHeaderView *horizontalHeader() const; @@ -199,6 +204,9 @@ private: QModelIndex m_index; QAccessible::Role m_role; + void selectCell(); + void unselectCell(); + friend class QAccessibleTable; friend class QAccessibleTree; }; diff --git a/src/plugins/accessible/widgets/main.cpp b/src/plugins/accessible/widgets/main.cpp index 9a42474910..92cda9f3ca 100644 --- a/src/plugins/accessible/widgets/main.cpp +++ b/src/plugins/accessible/widgets/main.cpp @@ -251,6 +251,9 @@ QAccessibleInterface *AccessibleFactory::create(const QString &classname, QObjec } else if (classname == QLatin1String("QDockWidget")) { iface = new QAccessibleDockWidget(widget); #endif + + } else if (classname == QLatin1String("QDesktopScreenWidget")) { + iface = 0; } else { iface = new QAccessibleWidget(widget); } diff --git a/src/plugins/accessible/widgets/widgets.json b/src/plugins/accessible/widgets/widgets.json index 69584b9bc8..094987daf5 100644 --- a/src/plugins/accessible/widgets/widgets.json +++ b/src/plugins/accessible/widgets/widgets.json @@ -47,5 +47,7 @@ "QScrollArea", "QCalendarWidget", "QDockWidget", - "QAccessibleWidget" ] + "QAccessibleWidget", + "QDesktopScreenWidget" + ] } diff --git a/src/plugins/bearer/corewlan/corewlan.pro b/src/plugins/bearer/corewlan/corewlan.pro index db8651de6e..674af0cbbe 100644 --- a/src/plugins/bearer/corewlan/corewlan.pro +++ b/src/plugins/bearer/corewlan/corewlan.pro @@ -8,9 +8,7 @@ QT = core-private network-private LIBS += -framework Foundation -framework SystemConfiguration contains(QT_CONFIG, corewlan) { - !contains(QMAKE_MAC_SDK, ".*MacOSX10\\.[345]\\.sdk") { - LIBS += -framework CoreWLAN -framework Security - } + LIBS += -framework CoreWLAN -framework Security } HEADERS += qcorewlanengine.h \ diff --git a/src/plugins/generic/meego/qmeegointegration.h b/src/plugins/generic/meego/qmeegointegration.h index 88e2aae721..998bbbf8d3 100644 --- a/src/plugins/generic/meego/qmeegointegration.h +++ b/src/plugins/generic/meego/qmeegointegration.h @@ -47,8 +47,6 @@ #include "contextkitproperty.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QMeeGoIntegration : public QObject @@ -67,6 +65,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QMEEGOINTEGRATION_H diff --git a/src/plugins/generic/tslib/qtslib.h b/src/plugins/generic/tslib/qtslib.h index 0c5c74f672..9342fdfea5 100644 --- a/src/plugins/generic/tslib/qtslib.h +++ b/src/plugins/generic/tslib/qtslib.h @@ -45,8 +45,6 @@ #include <qobject.h> //#include <Qt> -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSocketNotifier; @@ -72,6 +70,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QTSLIB_H diff --git a/src/plugins/imageformats/gif/gif.json b/src/plugins/imageformats/gif/gif.json index b599b40ffe..1d6cb126c4 100644 --- a/src/plugins/imageformats/gif/gif.json +++ b/src/plugins/imageformats/gif/gif.json @@ -1,3 +1,4 @@ { - "Keys": [ "gif" ] + "Keys": [ "gif" ], + "MimeTypes": [ "image/gif" ] } diff --git a/src/plugins/imageformats/ico/ico.json b/src/plugins/imageformats/ico/ico.json index d22cb739a1..bd46e07e54 100644 --- a/src/plugins/imageformats/ico/ico.json +++ b/src/plugins/imageformats/ico/ico.json @@ -1,3 +1,4 @@ { - "Keys": [ "ico" ] + "Keys": [ "ico" ], + "MimeTypes": [ "image/vnd.microsoft.icon" ] } diff --git a/src/plugins/imageformats/jpeg/jpeg.json b/src/plugins/imageformats/jpeg/jpeg.json index 132c642c05..5e26a97206 100644 --- a/src/plugins/imageformats/jpeg/jpeg.json +++ b/src/plugins/imageformats/jpeg/jpeg.json @@ -1,3 +1,4 @@ { - "Keys": [ "jpg", "jpeg" ] + "Keys": [ "jpg", "jpeg" ], + "MimeTypes": [ "image/jpeg", "image/jpeg" ] } diff --git a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp index f4e7666a85..53e9b171d5 100644 --- a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp +++ b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp @@ -226,26 +226,6 @@ void QIBusPlatformInputContext::updatePreeditText(const QDBusVariant &text, uint } -/* Kernel keycode -> X keycode table */ -static const unsigned int keycode_table[256] = { - 0, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 76, 79, 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 111, 221, 94, 95, 96, 211, 128, 127, 129, 208, 131, 126, - 108, 109, 112, 111, 113, 181, 97, 98, 99, 100, 102, 103, 104, 105, 106, 107, - 239, 160, 174, 176, 222, 157, 123, 110, 139, 134, 209, 210, 133, 115, 116, 117, - 232, 133, 134, 135, 140, 248, 191, 192, 122, 188, 245, 158, 161, 193, 223, 227, - 198, 199, 200, 147, 159, 151, 178, 201, 146, 203, 166, 236, 230, 235, 234, 233, - 163, 204, 253, 153, 162, 144, 164, 177, 152, 190, 208, 129, 130, 231, 209, 210, - 136, 220, 143, 246, 251, 137, 138, 182, 183, 184, 93, 184, 247, 132, 170, 219, - 249, 205, 207, 149, 150, 154, 155, 167, 168, 169, 171, 172, 173, 165, 175, 179, - 180, 0, 185, 186, 187, 118, 119, 120, 121, 229, 194, 195, 196, 197, 148, 202, - 101, 212, 237, 214, 215, 216, 217, 218, 228, 142, 213, 240, 241, 242, 243, 244, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - bool QIBusPlatformInputContext::x11FilterEvent(uint keyval, uint keycode, uint state, bool press) { diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h index 6dd7ea2fb3..e44b2d1b6d 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h @@ -93,19 +93,6 @@ @class QT_MANGLE_NAMESPACE(QCocoaMenuLoader); -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 - -@protocol NSApplicationDelegate <NSObject> -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification; -- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames; -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender; -- (void)applicationDidBecomeActive:(NSNotification *)notification; -- (void)applicationDidResignActive:(NSNotification *)notification; -@end - -#endif - @interface QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) : NSObject <NSApplicationDelegate> { bool startedQuit; NSMenu *dockMenu; diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm index a36a855bcd..12808b7041 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.mm +++ b/src/plugins/platforms/cocoa/qcocoacursor.mm @@ -65,8 +65,9 @@ void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window) { Q_UNUSED(window); + const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; // Check for a suitable built-in NSCursor first: - switch (cursor->shape()) { + switch (newShape) { case Qt::ArrowCursor: [[NSCursor arrowCursor] set]; break; @@ -104,7 +105,7 @@ void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window) default : { // No suitable OS cursor exist, use cursors provided // by Qt for the rest. Check for a cached cursor: - NSCursor *cocoaCursor = m_cursors.value(cursor->shape()); + NSCursor *cocoaCursor = m_cursors.value(newShape); if (cocoaCursor && cursor->shape() == Qt::BitmapCursor) { [cocoaCursor release]; cocoaCursor = 0; @@ -115,7 +116,7 @@ void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window) [[NSCursor arrowCursor] set]; return; } - m_cursors.insert(cursor->shape(), cocoaCursor); + m_cursors.insert(newShape, cocoaCursor); } [cocoaCursor set]; diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index a0734530c5..987600c6b4 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -149,10 +149,10 @@ void QCocoaEventDispatcherPrivate::maybeStartCFRunLoopTimer() CFTimeInterval oneyear = CFTimeInterval(3600. * 24. * 365.); // Q: when should the CFRunLoopTimer fire for the first time? - struct timeval tv; + struct timespec tv; if (timerInfoList.timerWait(tv)) { // A: when we have timers to fire, of course - interval = qMax(tv.tv_sec + tv.tv_usec / 1000000., 0.0000001); + interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); } else { // this shouldn't really happen, but in case it does, set the timer to fire a some point in the distant future interval = oneyear; @@ -172,10 +172,10 @@ void QCocoaEventDispatcherPrivate::maybeStartCFRunLoopTimer() CFTimeInterval interval; // Q: when should the timer first next? - struct timeval tv; + struct timespec tv; if (timerInfoList.timerWait(tv)) { // A: when we have timers to fire, of course - interval = qMax(tv.tv_sec + tv.tv_usec / 1000000., 0.0000001); + interval = qMax(tv.tv_sec + tv.tv_nsec / 1000000000., 0.0000001); } else { // no timers can fire, but we cannot stop the CFRunLoopTimer, set the timer to fire at some // point in the distant future (the timer interval is one year) @@ -490,14 +490,12 @@ static bool IsMouseOrKeyEvent( NSEvent* event ) case NSOtherMouseUp: case NSOtherMouseDragged: #ifndef QT_NO_GESTURES -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 case NSEventTypeGesture: // touch events case NSEventTypeMagnify: case NSEventTypeSwipe: case NSEventTypeRotate: case NSEventTypeBeginGesture: case NSEventTypeEndGesture: -#endif #endif // QT_NO_GESTURES result = true; break; diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index 61646041fb..9cc0353dc6 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -77,11 +77,7 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions; @class QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate); @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; @@ -216,7 +212,7 @@ static QString strippedText(QString s) - (void)closePanel { *mCurrentSelection = QT_PREPEND_NAMESPACE(QCFString::toQString)([[mSavePanel URL] path]); - if ([mSavePanel respondsToSelector:@selector(closePanel:)]) + if ([mSavePanel respondsToSelector:@selector(close)]) [mSavePanel close]; if ([mSavePanel isSheet]) [NSApp endSheet: mSavePanel]; @@ -230,6 +226,7 @@ static QString strippedText(QString s) bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave) || [self panel:nil shouldShowFilename:filepath]; + [self updateProperties]; [mOpenPanel setAllowedFileTypes:nil]; [mOpenPanel setDirectoryURL:selectable ? [NSURL fileURLWithPath:QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.filePath())] : [NSURL fileURLWithPath:QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.path())]]; @@ -274,6 +271,7 @@ static QString strippedText(QString s) bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave) || [self panel:nil shouldShowFilename:filepath]; + [self updateProperties]; [mSavePanel setDirectoryURL:selectable ? [NSURL fileURLWithPath:QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.filePath())] : [NSURL fileURLWithPath:QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.path())]]; [mSavePanel setNameFieldStringValue:selectable ? QT_PREPEND_NAMESPACE(QCFString::toNSString)(info.fileName()) : nil]; @@ -405,6 +403,9 @@ static QString strippedText(QString s) [mSavePanel setCanCreateDirectories:!(mOptions->testOption(QT_PREPEND_NAMESPACE(QFileDialogOptions::ReadOnly)))]; [mOpenPanel setAllowsMultipleSelection:(fileMode == QT_PREPEND_NAMESPACE(QFileDialogOptions::ExistingFiles))]; [mOpenPanel setResolvesAliases:!(mOptions->testOption(QT_PREPEND_NAMESPACE(QFileDialogOptions::DontResolveSymlinks)))]; + [mOpenPanel setTitle:QCFString::toNSString(mOptions->windowTitle())]; + [mSavePanel setTitle:QCFString::toNSString(mOptions->windowTitle())]; + [mPopUpButton setHidden:chooseDirsOnly]; // TODO hide the whole sunken pane instead? QStringList ext = [self acceptableExtensionsForSave]; const QString defaultSuffix = mOptions->defaultSuffix(); @@ -577,13 +578,16 @@ extern void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding void QCocoaFileDialogHelper::setDirectory(const QString &directory) { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); - [delegate->mSavePanel setDirectoryURL:[NSURL fileURLWithPath:QCFString::toNSString(directory)]]; + if (delegate) + [delegate->mSavePanel setDirectoryURL:[NSURL fileURLWithPath:QCFString::toNSString(directory)]]; } QString QCocoaFileDialogHelper::directory() const { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); - return QCFString::toQString([delegate->mSavePanel directory]); + if (delegate) + return QCFString::toQString([delegate->mSavePanel directory]); + return QString(); } void QCocoaFileDialogHelper::selectFile(const QString &filename) @@ -600,12 +604,16 @@ void QCocoaFileDialogHelper::selectFile(const QString &filename) QStringList QCocoaFileDialogHelper::selectedFiles() const { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); - return [delegate selectedFiles]; + if (delegate) + return [delegate selectedFiles]; + return QStringList(); } void QCocoaFileDialogHelper::setFilter() { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + if (!delegate) + return; const SharedPointerFileDialogOptions &opts = options(); [delegate->mSavePanel setTitle:QCFString::toNSString(opts->windowTitle())]; if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) @@ -618,9 +626,13 @@ void QCocoaFileDialogHelper::setFilter() void QCocoaFileDialogHelper::selectNameFilter(const QString &filter) { + if (!options()) + return; const int index = options()->nameFilters().indexOf(filter); if (index != -1) { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + if (!delegate) + return; [delegate->mPopUpButton selectItemAtIndex:index]; [delegate filterChanged:nil]; } @@ -629,7 +641,11 @@ void QCocoaFileDialogHelper::selectNameFilter(const QString &filter) QString QCocoaFileDialogHelper::selectedNameFilter() const { QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + if (!delegate) + return QString(); int index = [delegate->mPopUpButton indexOfSelectedItem]; + if (index >= options()->nameFilters().count()) + return QString(); return index != -1 ? options()->nameFilters().at(index) : QString(); } @@ -675,6 +691,8 @@ bool QCocoaFileDialogHelper::showCocoaFilePanel(Qt::WindowModality windowModalit { createNSOpenSavePanelDelegate(); QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + if (!delegate) + return false; if (windowModality == Qt::NonModal) [delegate showModelessPanel]; else if (windowModality == Qt::WindowModal && parent) diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 831ab579f5..c801d9d926 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -84,8 +84,6 @@ HIMutableShapeRef qt_mac_QRegionToHIMutableShape(const QRegion ®ion); OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage); -CGFloat qt_mac_get_scalefactor(); - QChar qt_mac_qtKey2CocoaKey(Qt::Key key); Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 91a6f5a4c7..29a8c49511 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -475,11 +475,9 @@ CGColorSpaceRef qt_mac_genericColorSpace() { #if 0 if (!m_genericColorSpace) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) { m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); } else -#endif { m_genericColorSpace = CGColorSpaceCreateDeviceRGB(); } @@ -616,11 +614,6 @@ InvalidContext: return err; } -CGFloat qt_mac_get_scalefactor() -{ - return [[NSScreen mainScreen] userSpaceScaleFactor]; -} - Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum) { if (buttonNum == 0) diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 7fcdab4d97..f33d4cb68b 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -105,7 +105,9 @@ void QCocoaScreen::updateGeometry() m_physicalSize = QSizeF(size.width, size.height); m_logicalDpi.first = 72; m_logicalDpi.second = 72; - m_refreshRate = CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(dpy)); + float refresh = CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(dpy)); + if (refresh > 0) + m_refreshRate = refresh; // Get m_name (brand/model of the monitor) NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(dpy), kIODisplayOnlyPreferredName); diff --git a/src/plugins/platforms/cocoa/qcocoaintrospection.mm b/src/plugins/platforms/cocoa/qcocoaintrospection.mm index ffb6ae4294..806effc929 100644 --- a/src/plugins/platforms/cocoa/qcocoaintrospection.mm +++ b/src/plugins/platforms/cocoa/qcocoaintrospection.mm @@ -83,7 +83,6 @@ void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class prox 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, @@ -104,7 +103,6 @@ void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class prox Method backupMethod = class_getInstanceMethod(proxyClass, backupSel); class_addMethod(baseClass, backupSel, originalImp, method_getTypeEncoding(backupMethod)); } -#endif } } @@ -114,11 +112,9 @@ void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL b 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 } } diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h index 09b62a9bcd..439b7f1a75 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.h +++ b/src/plugins/platforms/cocoa/qcocoamenu.h @@ -51,8 +51,6 @@ @class NSMenu; @class NSObject; -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QCocoaMenu : public QPlatformMenu @@ -71,11 +69,17 @@ public: void syncMenuItem(QPlatformMenuItem *menuItem); void setEnabled(bool enabled); void setVisible(bool visible); + void showPopup(const QWindow *parentWindow, QPoint pos, const QPlatformMenuItem *item); + void syncSeparatorsCollapsible(bool enable); void syncModalState(bool modal); - virtual void setText(const QString &text); + virtual void setIcon(const QIcon &icon) { Q_UNUSED(icon) } + + void setText(const QString &text); + void setMinimumWidth(int width); + void setFont(const QFont &font); void setParentItem(QCocoaMenuItem* item); @@ -102,6 +106,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index 676f0683fa..0fe4c48510 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -47,6 +47,8 @@ #include <QtCore/QtDebug> #include "qcocoaapplication.h" #include "qcocoamenuloader.h" +#include "qcocoawindow.h" +#import "qnsview.h" static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader() { @@ -133,6 +135,20 @@ void QCocoaMenu::setText(const QString &text) [m_nativeItem setTitle:QCFString::toNSString(stripped)]; } +void QCocoaMenu::setMinimumWidth(int width) +{ + m_nativeMenu.minimumWidth = width; +} + +void QCocoaMenu::setFont(const QFont &font) +{ + if (font.resolve()) { + NSFont *customMenuFont = [NSFont fontWithName:QCFString::toNSString(font.family()) + size:font.pointSize()]; + m_nativeMenu.font = customMenuFont; + } +} + void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) { QCocoaAutoReleasePool pool; @@ -290,9 +306,26 @@ void QCocoaMenu::setVisible(bool visible) [m_nativeItem setSubmenu:(visible ? m_nativeMenu : nil)]; } +void QCocoaMenu::showPopup(const QWindow *parentWindow, QPoint pos, const QPlatformMenuItem *item) +{ + QCocoaWindow *cocoaWindow = parentWindow ? static_cast<QCocoaWindow *>(parentWindow->handle()) : 0; + NSView *view = cocoaWindow ? cocoaWindow->contentView() : nil; + NSMenuItem *nsItem = item ? ((QCocoaMenuItem *)item)->nsItem() : nil; + NSPoint nsPos = NSMakePoint(pos.x(), pos.y()); + [m_nativeMenu popUpMenuPositioningItem:nsItem atLocation:nsPos inView:view]; + + // The call above blocks, and also swallows any mouse release event, + // so we need to clear any mouse button that triggered the menu popup. + if ([view isKindOfClass:[QNSView class]]) + [(QNSView *)view resetMouseButtons]; +} + QPlatformMenuItem *QCocoaMenu::menuItemAt(int position) const { - return m_menuItems.at(position); + if (0 <= position && position < m_menuItems.count()) + return m_menuItems.at(position); + + return 0; } QPlatformMenuItem *QCocoaMenu::menuItemForTag(quintptr tag) const diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.h b/src/plugins/platforms/cocoa/qcocoamenuitem.h index fe5193a50c..0e6d17343d 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.h +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.h @@ -51,8 +51,6 @@ @class NSMenuItem; @class NSMenu; -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QCocoaMenu; @@ -76,6 +74,7 @@ public: void setFont(const QFont &font); void setRole(MenuRole role); void setShortcut(const QKeySequence& shortcut); + void setCheckable(bool checkable) { Q_UNUSED(checkable) } void setChecked(bool isChecked); void setEnabled(bool isEnabled); @@ -110,8 +109,6 @@ private: quintptr m_tag; }; -QT_END_HEADER - QT_END_NAMESPACE #endif diff --git a/src/plugins/platforms/cocoa/qcocoaprintersupport.h b/src/plugins/platforms/cocoa/qcocoaprintersupport.h index 040b687c4e..83cf1ffada 100644 --- a/src/plugins/platforms/cocoa/qcocoaprintersupport.h +++ b/src/plugins/platforms/cocoa/qcocoaprintersupport.h @@ -55,6 +55,7 @@ public: QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode) Q_DECL_OVERRIDE; QPaintEngine *createPaintEngine(QPrintEngine *, QPrinter::PrinterMode printerMode) Q_DECL_OVERRIDE; QList<QPrinter::PaperSize> supportedPaperSizes(const QPrinterInfo &) const Q_DECL_OVERRIDE; + QList<QPair<QString, QSizeF> > supportedSizesWithNames(const QPrinterInfo &) const Q_DECL_OVERRIDE; QList<QPrinterInfo> availablePrinters() Q_DECL_OVERRIDE; QPrinterInfo printerInfo(const QString &printerName) Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/cocoa/qcocoaprintersupport.mm b/src/plugins/platforms/cocoa/qcocoaprintersupport.mm index a48db02949..cfa23b7a30 100644 --- a/src/plugins/platforms/cocoa/qcocoaprintersupport.mm +++ b/src/plugins/platforms/cocoa/qcocoaprintersupport.mm @@ -138,3 +138,34 @@ QPrinterInfo QCocoaPrinterSupport::printerInfoFromPMPrinter(const PMPrinter &pri return createPrinterInfo(name, description, location, makeAndModel, isDefault, 0); } + +QList<QPair<QString, QSizeF> > QCocoaPrinterSupport::supportedSizesWithNames(const QPrinterInfo &printerInfo) const +{ + QList<QPair<QString, QSizeF> > returnValue; + if (printerInfo.isNull()) + return returnValue; + + PMPrinter printer = PMPrinterCreateFromPrinterID(QCFString::toCFStringRef(printerInfo.printerName())); + if (!printer) + return returnValue; + + CFArrayRef array; + if (PMPrinterGetPaperList(printer, &array) != noErr) { + PMRelease(printer); + return returnValue; + } + + int count = CFArrayGetCount(array); + for (int i = 0; i < count; ++i) { + PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(array, i))); + double width, height; + if (PMPaperGetWidth(paper, &width) == noErr && PMPaperGetHeight(paper, &height) == noErr) { + static const double OnePointInMillimeters = 1.0 / 72.0 * 25.4; + QCFString paperName; + if (PMPaperCreateLocalizedName(paper, printer, &paperName) == noErr) + returnValue.append(qMakePair(QString(paperName), QSizeF(width * OnePointInMillimeters, height * OnePointInMillimeters))); + } + } + PMRelease(printer); + return returnValue; +} diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm index e092db8172..99f533b33a 100755 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm @@ -93,7 +93,11 @@ QT_USE_NAMESPACE @class QT_MANGLE_NAMESPACE(QNSMenu); @class QT_MANGLE_NAMESPACE(QNSImageView); -@interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject { +@interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + <NSUserNotificationCenterDelegate> +#endif + { @public QCocoaSystemTrayIcon *systray; NSStatusItem *item; @@ -108,6 +112,11 @@ QT_USE_NAMESPACE -(QRectF)geometry; - (void)triggerSelector:(id)sender button:(Qt::MouseButton)mouseButton; - (void)doubleClickSelector:(id)sender; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification; +- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification; +#endif @end @interface QT_MANGLE_NAMESPACE(QNSImageView) : NSImageView { @@ -132,9 +141,19 @@ class QSystemTrayIconSys public: QSystemTrayIconSys(QCocoaSystemTrayIcon *sys) { item = [[QT_MANGLE_NAMESPACE(QNSStatusItem) alloc] initWithSysTray:sys]; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) { + [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:item]; + } +#endif } ~QSystemTrayIconSys() { [[[item item] view] setHidden: YES]; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) { + [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil]; + } +#endif [item release]; } QT_MANGLE_NAMESPACE(QNSStatusItem) *item; @@ -223,6 +242,18 @@ void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &mess if (!m_sys) return; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) { + NSUserNotification *notification = [[NSUserNotification alloc] init]; + notification.title = [NSString stringWithUTF8String:title.toUtf8().data()]; + notification.informativeText = [NSString stringWithUTF8String:message.toUtf8().data()]; + + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; + + return; + } +#endif + #ifdef QT_MAC_SYSTEMTRAY_USE_GROWL // Make sure that we have Growl installed on the machine we are running on. QCFType<CFURLRef> cfurl; @@ -439,6 +470,20 @@ QT_END_NAMESPACE emit systray->activated(QPlatformSystemTrayIcon::DoubleClick); } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification { + Q_UNUSED(center); + Q_UNUSED(notification); + return YES; +} + +- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification { + Q_UNUSED(center); + Q_UNUSED(notification); + emit systray->messageClicked(); +} +#endif + @end class QSystemTrayIconQMenu : public QPlatformMenu diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 324a43c8ae..291c688915 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -115,6 +115,8 @@ public: bool setMouseGrabEnabled(bool grab); QMargins frameMargins() const; + void requestActivateWindow(); + WId winId() const; void setParent(const QPlatformWindow *window); diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 45100f9906..56ca2e0b14 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -658,6 +658,13 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) setOpacity(opacity); } +void QCocoaWindow::requestActivateWindow() +{ + NSWindow *window = [m_contentView window]; + [ window makeFirstResponder : m_contentView ]; + [ window makeKeyWindow ]; +} + NSWindow * QCocoaWindow::createNSWindow() { QCocoaAutoReleasePool pool; diff --git a/src/plugins/platforms/cocoa/qmacdefines_mac.h b/src/plugins/platforms/cocoa/qmacdefines_mac.h index 18c1ac84de..c03b398836 100644 --- a/src/plugins/platforms/cocoa/qmacdefines_mac.h +++ b/src/plugins/platforms/cocoa/qmacdefines_mac.h @@ -79,15 +79,11 @@ */ /* This is just many defines. Therefore it doesn't need things like: -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE QT_END_NAMESPACE -QT_END_HEADER - Yes, it is an informative comment ;-) */ diff --git a/src/plugins/platforms/cocoa/qmultitouch_mac.mm b/src/plugins/platforms/cocoa/qmultitouch_mac.mm index 337d962c7f..fc0048f998 100644 --- a/src/plugins/platforms/cocoa/qmultitouch_mac.mm +++ b/src/plugins/platforms/cocoa/qmultitouch_mac.mm @@ -42,8 +42,6 @@ #include "qmultitouch_mac_p.h" #include "qcocoahelpers.h" -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - QT_BEGIN_NAMESPACE QHash<qint64, QCocoaTouch*> QCocoaTouch::_currentTouches; @@ -214,6 +212,3 @@ QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch) } QT_END_NAMESPACE - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - diff --git a/src/plugins/platforms/cocoa/qmultitouch_mac_p.h b/src/plugins/platforms/cocoa/qmultitouch_mac_p.h index 1244b8223f..736eb3f878 100644 --- a/src/plugins/platforms/cocoa/qmultitouch_mac_p.h +++ b/src/plugins/platforms/cocoa/qmultitouch_mac_p.h @@ -61,8 +61,6 @@ #include <qhash.h> #include <QtCore> -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - QT_BEGIN_NAMESPACE class QCocoaTouch @@ -92,7 +90,5 @@ class QCocoaTouch QT_END_NAMESPACE -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - #endif // QMULTITOUCH_MAC_P_H diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h index b4b3379a82..eec0cfe5f6 100644 --- a/src/plugins/platforms/cocoa/qnsview.h +++ b/src/plugins/platforms/cocoa/qnsview.h @@ -82,6 +82,9 @@ QT_END_NAMESPACE - (BOOL)isFlipped; - (BOOL)acceptsFirstResponder; +- (BOOL)becomeFirstResponder; + +- (void)resetMouseButtons; - (void)handleMouseEvent:(NSEvent *)theEvent; - (void)mouseDown:(NSEvent *)theEvent; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 1d59592e7d..cdbaa235e4 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -331,8 +331,25 @@ static QTouchDevice *touchDevice = 0; return YES; } +- (BOOL)becomeFirstResponder +{ + QWindow *focusWindow = m_window; + + // For widgets we need to do a bit of trickery as the window + // to activate is the window of the top-level widget. + if (m_window->metaObject()->className() == QStringLiteral("QWidgetWindow")) { + while (focusWindow->parent()) { + focusWindow = focusWindow->parent(); + } + } + QWindowSystemInterface::handleWindowActivated(focusWindow); + return YES; +} + - (BOOL)acceptsFirstResponder { + if ((m_window->flags() & Qt::ToolTip) == Qt::ToolTip) + return NO; return YES; } @@ -381,6 +398,11 @@ static QTouchDevice *touchDevice = 0; } } +- (void)resetMouseButtons +{ + m_buttons = Qt::NoButton; +} + - (void)handleMouseEvent:(NSEvent *)theEvent { QPoint qtWindowPoint, qtScreenPoint; diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.h b/src/plugins/platforms/cocoa/qnswindowdelegate.h index 53bbeb1318..98ad7b8c9d 100644 --- a/src/plugins/platforms/cocoa/qnswindowdelegate.h +++ b/src/plugins/platforms/cocoa/qnswindowdelegate.h @@ -46,26 +46,6 @@ #include "qcocoawindow.h" -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 -@protocol NSWindowDelegate <NSObject> -//- (NSSize)windowWillResize:(NSWindow *)window toSize:(NSSize)proposedFrameSize; -//- (void)windowDidMiniaturize:(NSNotification*)notification; -- (void)windowDidResize:(NSNotification *)notification; -- (void)windowWillClose:(NSNotification *)notification; -//- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame; -- (void)windowDidMove:(NSNotification *)notification; -//- (BOOL)windowShouldClose:(id)window; -//- (void)windowDidDeminiaturize:(NSNotification *)notification; -//- (void)windowDidBecomeMain:(NSNotification*)notification; -//- (void)windowDidResignMain:(NSNotification*)notification; -//- (void)windowDidBecomeKey:(NSNotification*)notification; -//- (void)windowDidResignKey:(NSNotification*)notification; -//- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu; -//- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard; -//- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame; -@end -#endif - @interface QNSWindowDelegate : NSObject <NSWindowDelegate> { QCocoaWindow *m_cocoaWindow; diff --git a/src/plugins/platforms/cocoa/qpaintengine_mac.mm b/src/plugins/platforms/cocoa/qpaintengine_mac.mm index 0b6392fb65..56ab732a32 100644 --- a/src/plugins/platforms/cocoa/qpaintengine_mac.mm +++ b/src/plugins/platforms/cocoa/qpaintengine_mac.mm @@ -90,14 +90,11 @@ void qt_mac_clip_cg(CGContextRef hd, const QRegion &rgn, CGAffineTransform *orig if (rgn.isEmpty()) { CGContextAddRect(hd, CGRectMake(0, 0, 0, 0)); } else { -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { QCFType<HIMutableShapeRef> shape = qt_mac_QRegionToHIMutableShape(rgn); Q_ASSERT(!HIShapeIsEmpty(shape)); HIShapeReplacePathInCGContext(shape, hd); - } else -#endif - { + } else { QVector<QRect> rects = rgn.rects(); const int count = rects.size(); for (int i = 0; i < count; i++) { @@ -338,11 +335,9 @@ CGColorSpaceRef QCoreGraphicsPaintEngine::macGenericColorSpace() { #if 0 if (!m_genericColorSpace) { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) { m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); } else -#endif { m_genericColorSpace = CGColorSpaceCreateDeviceRGB(); } @@ -1185,7 +1180,6 @@ extern "C" { void QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode) { -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { int cg_mode = kCGBlendModeNormal; switch (mode) { @@ -1267,11 +1261,9 @@ QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode) if (cg_mode > -1) { CGContextSetBlendMode(d_func()->hd, CGBlendMode(cg_mode)); } - } else -#endif - // The standard porter duff ops. - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_3 + } else if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_3 && mode <= QPainter::CompositionMode_Xor) { + // The standard porter duff ops. int cg_mode = kCGCompositeModeCopy; switch (mode) { case QPainter::CompositionMode_SourceOver: @@ -1317,7 +1309,6 @@ QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode) if (cg_mode > -1) CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode)); } else { -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) bool needPrivateAPI = false; if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) { int cg_mode = kCGBlendModeNormal; @@ -1367,7 +1358,6 @@ QCoreGraphicsPaintEngine::updateCompositionMode(QPainter::CompositionMode mode) else CGContextSetCompositeOperation(d_func()->hd, CGCompositeMode(cg_mode)); } -#endif } } @@ -1376,13 +1366,8 @@ QCoreGraphicsPaintEngine::updateRenderHints(QPainter::RenderHints hints) { Q_D(QCoreGraphicsPaintEngine); CGContextSetShouldAntialias(d->hd, hints & QPainter::Antialiasing); - static const CGFloat ScaleFactor = qt_mac_get_scalefactor(); - if (ScaleFactor > 1.) { - CGContextSetInterpolationQuality(d->hd, kCGInterpolationHigh); - } else { - CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ? - kCGInterpolationHigh : kCGInterpolationNone); - } + CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ? + kCGInterpolationHigh : kCGInterpolationNone); bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing; if (!textAntialiasing || d->disabledSmoothFonts) { d->disabledSmoothFonts = !textAntialiasing; diff --git a/src/plugins/platforms/cocoa/qpaintengine_mac_p.h b/src/plugins/platforms/cocoa/qpaintengine_mac_p.h index 2763a14690..fa000b9f92 100644 --- a/src/plugins/platforms/cocoa/qpaintengine_mac_p.h +++ b/src/plugins/platforms/cocoa/qpaintengine_mac_p.h @@ -122,8 +122,6 @@ 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/plugins/platforms/cocoa/qprintengine_mac.mm b/src/plugins/platforms/cocoa/qprintengine_mac.mm index 54019372bc..4570a663f6 100644 --- a/src/plugins/platforms/cocoa/qprintengine_mac.mm +++ b/src/plugins/platforms/cocoa/qprintengine_mac.mm @@ -190,23 +190,7 @@ QList<QVariant> QMacPrintEnginePrivate::supportedResolutions() const if (PMSessionGetCurrentPrinter(session(), &printer) == noErr) { PMResolution res; OSStatus status = PMPrinterGetPrinterResolutionCount(printer, &resCount); - if (status == kPMNotImplemented) { -#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5) - // *Sigh* we have to use the non-indexed version. - if (PMPrinterGetPrinterResolution(printer, kPMMinSquareResolution, &res) == noErr) - resolutions.append(int(res.hRes)); - if (PMPrinterGetPrinterResolution(printer, kPMMaxSquareResolution, &res) == noErr) { - QVariant var(int(res.hRes)); - if (!resolutions.contains(var)) - resolutions.append(var); - } - if (PMPrinterGetPrinterResolution(printer, kPMDefaultResolution, &res) == noErr) { - QVariant var(int(res.hRes)); - if (!resolutions.contains(var)) - resolutions.append(var); - } -#endif - } else if (status == noErr) { + if (status == noErr) { // According to the docs, index start at 1. for (UInt32 i = 1; i <= resCount; ++i) { if (PMPrinterGetIndexedPrinterResolution(printer, i, &res) == noErr) diff --git a/src/plugins/platforms/directfb/qdirectfbcursor.cpp b/src/plugins/platforms/directfb/qdirectfbcursor.cpp index afddba3482..b03af51b1a 100644 --- a/src/plugins/platforms/directfb/qdirectfbcursor.cpp +++ b/src/plugins/platforms/directfb/qdirectfbcursor.cpp @@ -59,8 +59,9 @@ void QDirectFBCursor::changeCursor(QCursor *cursor, QWindow *) int ySpot; QPixmap map; - if (cursor->shape() != Qt::BitmapCursor) { - m_image->set(cursor->shape()); + const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; + if (newShape != Qt::BitmapCursor) { + m_image->set(newShape); xSpot = m_image->hotspot().x(); ySpot = m_image->hotspot().y(); QImage *i = m_image->image(); diff --git a/src/plugins/platforms/eglfs/qeglfscontext.cpp b/src/plugins/platforms/eglfs/qeglfscontext.cpp index 66b42d64e7..06db4e02db 100644 --- a/src/plugins/platforms/eglfs/qeglfscontext.cpp +++ b/src/plugins/platforms/eglfs/qeglfscontext.cpp @@ -43,14 +43,17 @@ #include "qeglfswindow.h" #include "qeglfscursor.h" #include "qeglfshooks.h" +#include "qeglfsintegration.h" +#include <QtPlatformSupport/private/qeglpbuffer_p.h> +#include <QtGui/QSurface> #include <QtDebug> QT_BEGIN_NAMESPACE QEglFSContext::QEglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLenum eglApi) - : QEGLPlatformContext(hooks->surfaceFormatFor(format), share, display, eglApi) + : QEGLPlatformContext(format, share, display, QEglFSIntegration::chooseConfig(display, format), eglApi) { } @@ -61,16 +64,20 @@ bool QEglFSContext::makeCurrent(QPlatformSurface *surface) EGLSurface QEglFSContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface) { - QEglFSWindow *window = static_cast<QEglFSWindow *>(surface); - return window->surface(); + if (surface->surface()->surfaceClass() == QSurface::Window) + return static_cast<QEglFSWindow *>(surface)->surface(); + else + return static_cast<QEGLPbuffer *>(surface)->pbuffer(); } void QEglFSContext::swapBuffers(QPlatformSurface *surface) { - QEglFSWindow *window = static_cast<QEglFSWindow *>(surface); - // draw the cursor - if (QEglFSCursor *cursor = static_cast<QEglFSCursor *>(window->screen()->cursor())) - cursor->paintOnScreen(); + if (surface->surface()->surfaceClass() == QSurface::Window) { + QEglFSWindow *window = static_cast<QEglFSWindow *>(surface); + // draw the cursor + if (QEglFSCursor *cursor = static_cast<QEglFSCursor *>(window->screen()->cursor())) + cursor->paintOnScreen(); + } QEGLPlatformContext::swapBuffers(surface); } diff --git a/src/plugins/platforms/eglfs/qeglfscursor.cpp b/src/plugins/platforms/eglfs/qeglfscursor.cpp index c245b1b5bb..9dc836b8b1 100644 --- a/src/plugins/platforms/eglfs/qeglfscursor.cpp +++ b/src/plugins/platforms/eglfs/qeglfscursor.cpp @@ -196,15 +196,16 @@ void QEglFSCursor::changeCursor(QCursor *cursor, QWindow *window) bool QEglFSCursor::setCurrentCursor(QCursor *cursor) { - if (m_cursor.shape == cursor->shape() && cursor->shape() != Qt::BitmapCursor) + const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; + if (m_cursor.shape == newShape && newShape != Qt::BitmapCursor) return false; if (m_cursor.shape == Qt::BitmapCursor) { m_cursor.customCursorImage = QImage(); // in case render() never uploaded it } - m_cursor.shape = cursor->shape(); - if (cursor->shape() != Qt::BitmapCursor) { // standard cursor + m_cursor.shape = newShape; + if (newShape != Qt::BitmapCursor) { // standard cursor const float ws = (float)m_cursorAtlas.cursorWidth / m_cursorAtlas.width, hs = (float)m_cursorAtlas.cursorHeight / m_cursorAtlas.height; m_cursor.textureRect = QRectF(ws * (m_cursor.shape % m_cursorAtlas.cursorsPerRow), diff --git a/src/plugins/platforms/eglfs/qeglfshooks.h b/src/plugins/platforms/eglfs/qeglfshooks.h index 2d095ba934..f3b6a28e70 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks.h +++ b/src/plugins/platforms/eglfs/qeglfshooks.h @@ -55,10 +55,11 @@ class QEglFSScreen; class QEglFSHooks { public: - virtual ~QEglFSHooks() {}; + virtual ~QEglFSHooks() {} virtual void platformInit(); virtual void platformDestroy(); virtual EGLNativeDisplayType platformDisplay() const; + virtual QSizeF physicalScreenSize() const; virtual QSize screenSize() const; virtual int screenDepth() const; virtual QImage::Format screenFormat() const; @@ -67,6 +68,9 @@ public: virtual void destroyNativeWindow(EGLNativeWindowType window); virtual bool hasCapability(QPlatformIntegration::Capability cap) const; virtual QEglFSCursor *createCursor(QEglFSScreen *screen) const; + virtual bool filterConfig(EGLDisplay display, EGLConfig config) const; + + virtual const char *fbDeviceName() const; }; #ifdef EGLFS_PLATFORM_HOOKS diff --git a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp index e9a22ea605..a8fa81a6ec 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp +++ b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp @@ -46,8 +46,15 @@ #include <linux/fb.h> #include <sys/ioctl.h> +#include <private/qmath_p.h> + QT_BEGIN_NAMESPACE +const char *QEglFSHooks::fbDeviceName() const +{ + return "/dev/fb0"; +} + void QEglFSHooks::platformInit() { Q_UNUSED(hooks); @@ -62,6 +69,61 @@ EGLNativeDisplayType QEglFSHooks::platformDisplay() const return EGL_DEFAULT_DISPLAY; } +QSizeF QEglFSHooks::physicalScreenSize() const +{ + static QSizeF size; + if (size.isEmpty()) { + + // Note: in millimeters + int width = qgetenv("QT_QPA_EGLFS_PHYSICAL_WIDTH").toInt(); + int height = qgetenv("QT_QPA_EGLFS_PHYSICAL_HEIGHT").toInt(); + + if (width && height) { + // no need to read fb0 + size.setWidth(width); + size.setHeight(height); + return size; + } + + struct fb_var_screeninfo vinfo; + int fd = open(fbDeviceName(), O_RDONLY); + + int w = -1; + int h = -1; + + if (fd != -1) { + if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) { + qWarning("EGLFS: Could not query variable screen info."); + } else { + w = vinfo.width; + h = vinfo.height; + } + + close(fd); + } else { + qWarning("EGLFS: Failed to open %s to detect physical screen size.", fbDeviceName()); + } + + const int defaultPhysicalDpi = 100; + size.setWidth(w <= 0 ? vinfo.xres * Q_MM_PER_INCH / defaultPhysicalDpi : qreal(w)); + size.setHeight(h <= 0 ? vinfo.yres * Q_MM_PER_INCH / defaultPhysicalDpi : qreal(h)); + + if (w <= 0 || h <= 0) { + qWarning("EGLFS: Unable to query physical screen size, defaulting to %d dpi.\n" + "EGLFS: To override, set QT_QPA_EGLFS_PHYSICAL_WIDTH " + "and QT_QPA_EGLFS_PHYSICAL_HEIGHT (in millimeters).", + defaultPhysicalDpi); + } + + // override fb0 from environment var setting + if (width) + size.setWidth(width); + if (height) + size.setWidth(height); + } + return size; +} + QSize QEglFSHooks::screenSize() const { static QSize size; @@ -78,17 +140,33 @@ QSize QEglFSHooks::screenSize() const } struct fb_var_screeninfo vinfo; - int fd = open("/dev/fb0", O_RDONLY); + int fd = open(fbDeviceName(), O_RDONLY); + + int xres = -1; + int yres = -1; if (fd != -1) { - if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) - qWarning("Could not query variable screen info."); - else - size = QSize(vinfo.xres, vinfo.yres); + if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) { + qWarning("EGLFS: Could not query variable screen info."); + } else { + xres = vinfo.xres; + yres = vinfo.yres; + } close(fd); } else { - qWarning("Failed to open /dev/fb0 to detect screen resolution."); + qWarning("EGLFS: Failed to open %s to detect screen resolution.", fbDeviceName()); + } + + const int defaultWidth = 800; + const int defaultHeight = 600; + size.setWidth(xres <= 0 ? defaultWidth : xres); + size.setHeight(yres <= 0 ? defaultHeight : yres); + + if (xres <= 0 || yres <= 0) { + qWarning("EGLFS: Unable to query screen resolution, defaulting to %dx%d.\n" + "EGLFS: To override, set QT_QPA_EGLFS_WIDTH and QT_QPA_EGLFS_HEIGHT.", + defaultWidth, defaultHeight); } // override fb0 from environment var setting @@ -107,21 +185,30 @@ int QEglFSHooks::screenDepth() const if (depth == 0) { struct fb_var_screeninfo vinfo; - int fd = open("/dev/fb0", O_RDONLY); + int fd = open(fbDeviceName(), O_RDONLY); if (fd != -1) { if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) - qWarning("Could not query variable screen info."); + qWarning("EGLFS: Could not query variable screen info."); else depth = vinfo.bits_per_pixel; close(fd); } else { - qWarning("Failed to open /dev/fb0 to detect screen depth."); + qWarning("EGLFS: Failed to open %s to detect screen depth.", fbDeviceName()); + } + + const int defaultDepth = 32; + + if (depth <= 0) { + depth = defaultDepth; + + qWarning("EGLFS: Unable to query screen depth, defaulting to %d.\n" + "EGLFS: To override, set QT_QPA_EGLFS_DEPTH.", defaultDepth); } } - return depth == 0 ? 32 : depth; + return depth; } QImage::Format QEglFSHooks::screenFormat() const @@ -134,6 +221,11 @@ QSurfaceFormat QEglFSHooks::surfaceFormatFor(const QSurfaceFormat &inputFormat) return inputFormat; } +bool QEglFSHooks::filterConfig(EGLDisplay, EGLConfig) const +{ + return true; +} + EGLNativeWindowType QEglFSHooks::createNativeWindow(const QSize &size, const QSurfaceFormat &format) { Q_UNUSED(size); diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/qeglfsintegration.cpp index 0dcde9ca36..dd212c80a0 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsintegration.cpp @@ -49,7 +49,9 @@ #include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> #include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> +#include <QtPlatformSupport/private/qeglconvenience_p.h> #include <QtPlatformSupport/private/qeglplatformcontext_p.h> +#include <QtPlatformSupport/private/qeglpbuffer_p.h> #if !defined(QT_NO_EVDEV) #include <QtPlatformSupport/private/qevdevmousemanager_p.h> @@ -61,6 +63,7 @@ #include <QtGui/QSurfaceFormat> #include <QtGui/QOpenGLContext> #include <QtGui/QScreen> +#include <QtGui/QOffscreenSurface> #include <qpa/qplatformcursor.h> #include "qeglfscontext.h" @@ -150,7 +153,13 @@ QPlatformBackingStore *QEglFSIntegration::createPlatformBackingStore(QWindow *wi QPlatformOpenGLContext *QEglFSIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { - return new QEglFSContext(context->format(), 0 /*share*/, mDisplay); + return new QEglFSContext(hooks->surfaceFormatFor(context->format()), context->shareHandle(), mDisplay); +} + +QPlatformOffscreenSurface *QEglFSIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const +{ + QEglFSScreen *screen = static_cast<QEglFSScreen *>(surface->screen()->handle()); + return new QEGLPbuffer(screen->display(), hooks->surfaceFormatFor(surface->requestedFormat()), surface); } QPlatformFontDatabase *QEglFSIntegration::fontDatabase() const @@ -201,4 +210,29 @@ void *QEglFSIntegration::nativeResourceForContext(const QByteArray &resource, QO return 0; } +EGLConfig QEglFSIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format) +{ + class Chooser : public QEglConfigChooser { + public: + Chooser(EGLDisplay display, QEglFSHooks *hooks) + : QEglConfigChooser(display) + , m_hooks(hooks) + { + } + + protected: + bool filterConfig(EGLConfig config) const + { + return m_hooks->filterConfig(display(), config) && QEglConfigChooser::filterConfig(config); + } + + private: + QEglFSHooks *m_hooks; + }; + + Chooser chooser(display, hooks); + chooser.setSurfaceFormat(format); + return chooser.chooseConfig(); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.h b/src/plugins/platforms/eglfs/qeglfsintegration.h index 42b132b73a..e048c5d310 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.h +++ b/src/plugins/platforms/eglfs/qeglfsintegration.h @@ -48,8 +48,6 @@ #include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformscreen.h> -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QEglFSIntegration : public QPlatformIntegration, public QPlatformNativeInterface @@ -63,6 +61,7 @@ public: QPlatformWindow *createPlatformWindow(QWindow *window) const; QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; + QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const; QPlatformNativeInterface *nativeInterface() const; QPlatformFontDatabase *fontDatabase() const; @@ -75,6 +74,8 @@ public: void *nativeResourceForIntegration(const QByteArray &resource); void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context); + static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format); + private: EGLDisplay mDisplay; QAbstractEventDispatcher *mEventDispatcher; @@ -83,6 +84,5 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // QEGLFSINTEGRATION_H diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.cpp b/src/plugins/platforms/eglfs/qeglfsscreen.cpp index 1bc1e944de..43d7cb3b1f 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.cpp +++ b/src/plugins/platforms/eglfs/qeglfsscreen.cpp @@ -83,6 +83,11 @@ QImage::Format QEglFSScreen::format() const return hooks->screenFormat(); } +QSizeF QEglFSScreen::physicalSize() const +{ + return hooks->physicalScreenSize(); +} + QPlatformCursor *QEglFSScreen::cursor() const { return m_cursor; diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.h b/src/plugins/platforms/eglfs/qeglfsscreen.h index 309791d6c2..8d3c5dbaec 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.h +++ b/src/plugins/platforms/eglfs/qeglfsscreen.h @@ -63,6 +63,8 @@ public: int depth() const; QImage::Format format() const; + QSizeF physicalSize() const; + QPlatformCursor *cursor() const; EGLDisplay display() const { return m_dpy; } diff --git a/src/plugins/platforms/eglfs/qeglfswindow.cpp b/src/plugins/platforms/eglfs/qeglfswindow.cpp index ceb04901c4..26f701d7ba 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.cpp +++ b/src/plugins/platforms/eglfs/qeglfswindow.cpp @@ -84,7 +84,8 @@ void QEglFSWindow::create() EGLDisplay display = (static_cast<QEglFSScreen *>(window()->screen()->handle()))->display(); QSurfaceFormat platformFormat = hooks->surfaceFormatFor(window()->requestedFormat()); - EGLConfig config = q_configFromGLFormat(display, platformFormat); + EGLConfig config = QEglFSIntegration::chooseConfig(display, platformFormat); + m_format = q_glFormatFromConfig(display, config); m_window = hooks->createNativeWindow(hooks->screenSize(), m_format); m_surface = eglCreateWindowSurface(display, config, m_window, NULL); diff --git a/src/plugins/platforms/kms/kms.pro b/src/plugins/platforms/kms/kms.pro index c0300e0960..711cf9e5c7 100644 --- a/src/plugins/platforms/kms/kms.pro +++ b/src/plugins/platforms/kms/kms.pro @@ -4,9 +4,10 @@ PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QKmsIntegrationPlugin load(qt_plugin) -QT += core-private gui-private platformsupport-private opengl-private +QT += core-private gui-private platformsupport-private +qtHaveModule(opengl):QT += opengl-private -DEFINES += MESA_EGL_NO_X11_HEADERS +DEFINES += MESA_EGL_NO_X11_HEADERS __GBM__ CONFIG += link_pkgconfig egl qpa/genericunixfontdatabase diff --git a/src/plugins/platforms/kms/qkmscursor.cpp b/src/plugins/platforms/kms/qkmscursor.cpp index d9345ce89b..da5dc9bb06 100644 --- a/src/plugins/platforms/kms/qkmscursor.cpp +++ b/src/plugins/platforms/kms/qkmscursor.cpp @@ -48,22 +48,17 @@ QT_BEGIN_NAMESPACE QKmsCursor::QKmsCursor(QKmsScreen *screen) : m_screen(screen), m_graphicsBufferManager(screen->device()->gbmDevice()), + m_cursorBufferObject(gbm_bo_create(m_graphicsBufferManager, 64, 64, GBM_FORMAT_ARGB8888, + GBM_BO_USE_CURSOR_64X64|GBM_BO_USE_WRITE)), + m_cursorImage(new QPlatformCursorImage(0, 0, 0, 0, 0, 0)), m_moved(false) { - gbm_bo *bo = gbm_bo_create(m_graphicsBufferManager, 64, 64, - GBM_BO_FORMAT_ARGB8888, - GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_RENDERING); - - m_eglImage = eglCreateImageKHR(m_screen->device()->eglDisplay(), 0, EGL_NATIVE_PIXMAP_KHR, - bo, 0); - gbm_bo_destroy(bo); - m_cursorImage = new QPlatformCursorImage(0, 0, 0, 0, 0, 0); } QKmsCursor::~QKmsCursor() { - drmModeSetCursor(m_screen->device()->fd(), m_screen->crtcId(), - 0, 0, 0); + drmModeSetCursor(m_screen->device()->fd(), m_screen->crtcId(), 0, 0, 0); + gbm_bo_destroy(m_cursorBufferObject); } void QKmsCursor::pointerEvent(const QMouseEvent &event) @@ -78,57 +73,30 @@ void QKmsCursor::pointerEvent(const QMouseEvent &event) } } -void QKmsCursor::changeCursor(QCursor *widgetCursor, QWindow *window) +void QKmsCursor::changeCursor(QCursor *windowCursor, QWindow *window) { Q_UNUSED(window) if (!m_moved) drmModeMoveCursor(m_screen->device()->fd(), m_screen->crtcId(), 0, 0); - if (widgetCursor->shape() != Qt::BitmapCursor) { - m_cursorImage->set(widgetCursor->shape()); - } else { - m_cursorImage->set(widgetCursor->pixmap().toImage(), - widgetCursor->hotSpot().x(), - widgetCursor->hotSpot().y()); - } - - if ((m_cursorImage->image()->width() > 64) || (m_cursorImage->image()->width() > 64)) { - qWarning("failed to set hardware cursor: larger than 64x64."); - return; - } - - QImage cursorImage = m_cursorImage->image()->convertToFormat(QImage::Format_RGB32); - - //Load cursor image into EGLImage - GLuint cursorTexture; - glGenTextures(1, &cursorTexture); - glBindTexture(GL_TEXTURE_2D, cursorTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - - //TODO: Format may be wrong here, need a color icon to test. - if (m_eglImage != EGL_NO_IMAGE_KHR) { - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, m_eglImage); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, cursorImage.width(), - cursorImage.height(), GL_RGBA, - GL_UNSIGNED_BYTE, cursorImage.constBits()); + const Qt::CursorShape newShape = windowCursor ? windowCursor->shape() : Qt::ArrowCursor; + if (newShape != Qt::BitmapCursor) { + m_cursorImage->set(newShape); } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cursorImage.width(), - cursorImage.height(), 0, GL_RGBA, - GL_UNSIGNED_BYTE, cursorImage.constBits()); + m_cursorImage->set(windowCursor->pixmap().toImage(), + windowCursor->hotSpot().x(), + windowCursor->hotSpot().y()); } - //EGLImage needs to contain sprite before calling this: - gbm_bo *bufferObject = gbm_bo_import(m_graphicsBufferManager, GBM_BO_IMPORT_EGL_IMAGE, - m_eglImage, GBM_BO_USE_CURSOR_64X64); - quint32 handle = gbm_bo_get_handle(bufferObject).u32; + if ((m_cursorImage->image()->width() > 64) || (m_cursorImage->image()->width() > 64)) + qWarning("Warning: cursor larger than 64x64; only 64x64 pixels will be shown."); - gbm_bo_destroy(bufferObject); + QImage cursorImage = m_cursorImage->image()-> + convertToFormat(QImage::Format_ARGB32).copy(0, 0, 64, 64); + gbm_bo_write(m_cursorBufferObject, cursorImage.constBits(), cursorImage.byteCount()); + quint32 handle = gbm_bo_get_handle(m_cursorBufferObject).u32; int status = drmModeSetCursor(m_screen->device()->fd(), m_screen->crtcId(), handle, 64, 64); diff --git a/src/plugins/platforms/kms/qkmscursor.h b/src/plugins/platforms/kms/qkmscursor.h index fda47ebedc..ee65b01e36 100644 --- a/src/plugins/platforms/kms/qkmscursor.h +++ b/src/plugins/platforms/kms/qkmscursor.h @@ -44,15 +44,11 @@ #include <qpa/qplatformcursor.h> -#define EGL_EGLEXT_PROTOTYPES 1 - -#include <EGL/egl.h> -#include <EGL/eglext.h> - QT_BEGIN_NAMESPACE class QKmsScreen; class gbm_device; +class gbm_bo; class QKmsCursor : public QPlatformCursor { @@ -61,12 +57,12 @@ public: ~QKmsCursor(); void pointerEvent(const QMouseEvent &event); - void changeCursor(QCursor *widgetCursor, QWindow *window); + void changeCursor(QCursor *windowCursor, QWindow *window); private: QKmsScreen *m_screen; gbm_device *m_graphicsBufferManager; - EGLImageKHR m_eglImage; + gbm_bo *m_cursorBufferObject; QPlatformCursorImage *m_cursorImage; bool m_moved; }; diff --git a/src/plugins/platforms/kms/qkmsscreen.cpp b/src/plugins/platforms/kms/qkmsscreen.cpp index 123dcc283f..892f7bb01f 100644 --- a/src/plugins/platforms/kms/qkmsscreen.cpp +++ b/src/plugins/platforms/kms/qkmsscreen.cpp @@ -124,10 +124,14 @@ void QKmsScreen::initializeScreenMode() drmModeConnector *connector = drmModeGetConnector(m_device->fd(), m_connectorId); drmModeModeInfo *mode = 0; - if (connector->count_modes > 0) - mode = &connector->modes[0]; - else - mode = &builtin_1024x768; + for (int i = 0; i < connector->count_modes; ++i) { + if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) { + mode = &connector->modes[i]; + break; + } + } + if (!mode) + mode = mode = &builtin_1024x768; drmModeEncoder *encoder = drmModeGetEncoder(m_device->fd(), connector->encoders[0]); if (encoder == 0) diff --git a/src/plugins/platforms/minimalegl/qminimaleglbackingstore.cpp b/src/plugins/platforms/minimalegl/qminimaleglbackingstore.cpp index da89100359..cb245f2e5c 100644 --- a/src/plugins/platforms/minimalegl/qminimaleglbackingstore.cpp +++ b/src/plugins/platforms/minimalegl/qminimaleglbackingstore.cpp @@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE QMinimalEglBackingStore::QMinimalEglBackingStore(QWindow *window) : QPlatformBackingStore(window) , m_context(new QOpenGLContext) + , m_device(0) { m_context->setFormat(window->requestedFormat()); m_context->setScreen(window->screen()); diff --git a/src/plugins/platforms/minimalegl/qminimaleglintegration.h b/src/plugins/platforms/minimalegl/qminimaleglintegration.h index fb86c967e1..dba7504033 100644 --- a/src/plugins/platforms/minimalegl/qminimaleglintegration.h +++ b/src/plugins/platforms/minimalegl/qminimaleglintegration.h @@ -47,8 +47,6 @@ #include <qpa/qplatformintegration.h> #include <qpa/qplatformscreen.h> -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QMinimalEglIntegration : public QPlatformIntegration @@ -75,6 +73,5 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // QMINIMALEGLINTEGRATION_H diff --git a/src/plugins/platforms/offscreen/main.cpp b/src/plugins/platforms/offscreen/main.cpp new file mode 100644 index 0000000000..ca7dc1d18b --- /dev/null +++ b/src/plugins/platforms/offscreen/main.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <qpa/qplatformintegrationplugin.h> +#include "qoffscreenintegration.h" + +QT_BEGIN_NAMESPACE + +class QOffscreenIntegrationPlugin : public QPlatformIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.1" FILE "offscreen.json") +public: + QPlatformIntegration *create(const QString&, const QStringList&); +}; + +QPlatformIntegration *QOffscreenIntegrationPlugin::create(const QString& system, const QStringList& paramList) +{ + Q_UNUSED(paramList); + if (system.toLower() == "offscreen") + return QOffscreenIntegration::createOffscreenIntegration(); + + return 0; +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/platforms/offscreen/offscreen.json b/src/plugins/platforms/offscreen/offscreen.json new file mode 100644 index 0000000000..6e87744de0 --- /dev/null +++ b/src/plugins/platforms/offscreen/offscreen.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "offscreen" ] +} diff --git a/src/plugins/platforms/offscreen/offscreen.pro b/src/plugins/platforms/offscreen/offscreen.pro new file mode 100644 index 0000000000..270e3ce228 --- /dev/null +++ b/src/plugins/platforms/offscreen/offscreen.pro @@ -0,0 +1,25 @@ +TARGET = qoffscreen + +PLUGIN_TYPE = platforms +load(qt_plugin) + +QT += core-private gui-private platformsupport-private + +SOURCES = main.cpp \ + qoffscreenintegration.cpp \ + qoffscreenwindow.cpp \ + qoffscreencommon.cpp + +HEADERS = qoffscreenintegration.h \ + qoffscreenwindow.h \ + qoffscreencommon.h + +OTHER_FILES += offscreen.json + +contains(QT_CONFIG, xcb):contains(QT_CONFIG, opengl):!contains(QT_CONFIG, opengles2) { + SOURCES += qoffscreenintegration_x11.cpp + HEADERS += qoffscreenintegration_x11.h + system(echo "Using X11 offscreen integration with GLX") +} else { + SOURCES += qoffscreenintegration_dummy.cpp +} diff --git a/src/plugins/platforms/offscreen/qoffscreencommon.cpp b/src/plugins/platforms/offscreen/qoffscreencommon.cpp new file mode 100644 index 0000000000..069b20693d --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreencommon.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qoffscreencommon.h" +#include "qoffscreenwindow.h" + +#include <QtGui/private/qpixmap_raster_p.h> +#include <QtGui/private/qguiapplication_p.h> + +#include <qpa/qplatformcursor.h> +#include <qpa/qplatformwindow.h> + +QT_BEGIN_NAMESPACE + +QPlatformWindow *QOffscreenScreen::windowContainingCursor = 0; + +class QOffscreenCursor : public QPlatformCursor +{ +public: + QOffscreenCursor() : m_pos(10, 10) {} + + QPoint pos() const { return m_pos; } + void setPos(const QPoint &pos) + { + m_pos = pos; + QWindowList wl = QGuiApplication::topLevelWindows(); + QWindow *containing = 0; + foreach (QWindow *w, wl) { + if (w->type() != Qt::Desktop && w->isExposed() && w->geometry().contains(pos)) { + containing = w; + break; + } + } + + QPoint local = pos; + if (containing) + local -= containing->position(); + + QWindow *previous = QOffscreenScreen::windowContainingCursor ? QOffscreenScreen::windowContainingCursor->window() : 0; + + if (containing != previous) + QWindowSystemInterface::handleEnterLeaveEvent(containing, previous, local, pos); + + QWindowSystemInterface::handleMouseEvent(containing, local, pos, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers()); + + QOffscreenScreen::windowContainingCursor = containing ? containing->handle() : 0; + } + + void changeCursor(QCursor *windowCursor, QWindow *window) + { + Q_UNUSED(windowCursor); + Q_UNUSED(window); + } + +private: + QPoint m_pos; +}; + +QOffscreenScreen::QOffscreenScreen() + : m_geometry(0, 0, 800, 600) + , m_cursor(new QOffscreenCursor) +{ +} + +QPixmap QOffscreenScreen::grabWindow(WId id, int x, int y, int width, int height) const +{ + QRect rect(x, y, width, height); + + QOffscreenWindow *window = QOffscreenWindow::windowForWinId(id); + if (!window || window->window()->type() == Qt::Desktop) { + QWindowList wl = QGuiApplication::topLevelWindows(); + QWindow *containing = 0; + foreach (QWindow *w, wl) { + if (w->type() != Qt::Desktop && w->isExposed() && w->geometry().contains(rect)) { + containing = w; + break; + } + } + + if (!containing) + return QPixmap(); + + id = containing->winId(); + rect = rect.translated(-containing->geometry().topLeft()); + } + + QOffscreenBackingStore *store = QOffscreenBackingStore::backingStoreForWinId(id); + if (store) + return store->grabWindow(id, rect); + return QPixmap(); +} + +QOffscreenBackingStore::QOffscreenBackingStore(QWindow *window) + : QPlatformBackingStore(window) +{ +} + +QOffscreenBackingStore::~QOffscreenBackingStore() +{ + clearHash(); +} + +QPaintDevice *QOffscreenBackingStore::paintDevice() +{ + return &m_image; +} + +void QOffscreenBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(region); + + if (m_image.size().isEmpty()) + return; + + QSize imageSize = m_image.size(); + + QRegion clipped = QRect(0, 0, window->width(), window->height()); + clipped &= QRect(0, 0, imageSize.width(), imageSize.height()).translated(-offset); + + QRect bounds = clipped.boundingRect().translated(offset); + + if (bounds.isNull()) + return; + + WId id = window->winId(); + + m_windowAreaHash[id] = bounds; + m_backingStoreForWinIdHash[id] = this; +} + +void QOffscreenBackingStore::resize(const QSize &size, const QRegion &) +{ + QImage::Format format = QGuiApplication::primaryScreen()->handle()->format(); + if (m_image.size() != size) + m_image = QImage(size, format); + clearHash(); +} + +extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset); + +bool QOffscreenBackingStore::scroll(const QRegion &area, int dx, int dy) +{ + if (m_image.isNull()) + return false; + + const QVector<QRect> rects = area.rects(); + for (int i = 0; i < rects.size(); ++i) + qt_scrollRectInImage(m_image, rects.at(i), QPoint(dx, dy)); + + return true; +} + +QPixmap QOffscreenBackingStore::grabWindow(WId window, const QRect &rect) const +{ + QRect area = m_windowAreaHash.value(window, QRect()); + if (area.isNull()) + return QPixmap(); + + QRect adjusted = rect; + if (adjusted.width() <= 0) + adjusted.setWidth(area.width()); + if (adjusted.height() <= 0) + adjusted.setHeight(area.height()); + + adjusted = adjusted.translated(area.topLeft()) & area; + + if (adjusted.isEmpty()) + return QPixmap(); + + return QPixmap::fromImage(m_image.copy(adjusted)); +} + +QOffscreenBackingStore *QOffscreenBackingStore::backingStoreForWinId(WId id) +{ + return m_backingStoreForWinIdHash.value(id, 0); +} + +void QOffscreenBackingStore::clearHash() +{ + QList<WId> ids = m_windowAreaHash.keys(); + foreach (WId id, ids) { + QHash<WId, QOffscreenBackingStore *>::iterator it = m_backingStoreForWinIdHash.find(id); + if (it.value() == this) + m_backingStoreForWinIdHash.remove(id); + } + m_windowAreaHash.clear(); +} + +QHash<WId, QOffscreenBackingStore *> QOffscreenBackingStore::m_backingStoreForWinIdHash; + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/offscreen/qoffscreencommon.h b/src/plugins/platforms/offscreen/qoffscreencommon.h new file mode 100644 index 0000000000..a5df7d05d3 --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreencommon.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOFFSCREENCOMMON_H +#define QOFFSCREENCOMMON_H + +#include <qpa/qplatformbackingstore.h> +#include <qpa/qplatformdrag.h> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformscreen.h> +#include <qpa/qplatformwindow.h> + +#include <qscopedpointer.h> +#include <qimage.h> +#include <qhash.h> + +QT_BEGIN_NAMESPACE + +class QOffscreenScreen : public QPlatformScreen +{ +public: + QOffscreenScreen(); + + QRect geometry() const { return m_geometry; } + int depth() const { return 32; } + QImage::Format format() const { return QImage::Format_RGB32; } + QPlatformCursor *cursor() const { return m_cursor.data(); } + + QPixmap grabWindow(WId window, int x, int y, int width, int height) const; + + static QPlatformWindow *windowContainingCursor; + +public: + QRect m_geometry; + QScopedPointer<QPlatformCursor> m_cursor; +}; + +class QOffscreenDrag : public QPlatformDrag +{ +public: + QMimeData *platformDropData() { return 0; } + Qt::DropAction drag(QDrag *) { return Qt::IgnoreAction; } +}; + +class QOffscreenBackingStore : public QPlatformBackingStore +{ +public: + QOffscreenBackingStore(QWindow *window); + ~QOffscreenBackingStore(); + + QPaintDevice *paintDevice(); + void flush(QWindow *window, const QRegion ®ion, const QPoint &offset); + void resize(const QSize &size, const QRegion &staticContents); + bool scroll(const QRegion &area, int dx, int dy); + + QPixmap grabWindow(WId window, const QRect &rect) const; + + static QOffscreenBackingStore *backingStoreForWinId(WId id); + +private: + void clearHash(); + + QImage m_image; + QHash<WId, QRect> m_windowAreaHash; + + static QHash<WId, QOffscreenBackingStore *> m_backingStoreForWinIdHash; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp new file mode 100644 index 0000000000..e3fcc7ebb0 --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qoffscreenintegration.h" +#include "qoffscreenwindow.h" +#include "qoffscreencommon.h" + +#if defined(Q_OS_UNIX) +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> +#if defined(Q_OS_MAC) +#include <qpa/qplatformfontdatabase.h> +#else +#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> +#endif +#elif defined(Q_OS_WIN) +#include <QtPlatformSupport/private/qbasicfontdatabase_p.h> +#include <QtCore/private/qeventdispatcher_win_p.h> +#endif + +#include <QtGui/private/qpixmap_raster_p.h> +#include <QtGui/private/qguiapplication_p.h> + +#include <qpa/qplatformservices.h> + +QT_BEGIN_NAMESPACE + +template <typename BaseEventDispatcher> +class QOffscreenEventDispatcher : public BaseEventDispatcher +{ +public: + explicit QOffscreenEventDispatcher(QObject *parent = 0) + : BaseEventDispatcher(parent) + { + } + + bool processEvents(QEventLoop::ProcessEventsFlags flags) + { + bool didSendEvents = QWindowSystemInterface::sendWindowSystemEvents(flags); + + return BaseEventDispatcher::processEvents(flags) || didSendEvents; + } + + bool hasPendingEvents() + { + return BaseEventDispatcher::hasPendingEvents() + || QWindowSystemInterface::windowSystemEventsQueued(); + } + + void flush() + { + if (qApp) + qApp->sendPostedEvents(); + BaseEventDispatcher::flush(); + } +}; + +QOffscreenIntegration::QOffscreenIntegration() +{ +#if defined(Q_OS_UNIX) + m_eventDispatcher = createUnixEventDispatcher(); +#if defined(Q_OS_MAC) + m_fontDatabase.reset(new QPlatformFontDatabase()); +#else + m_fontDatabase.reset(new QGenericUnixFontDatabase()); +#endif +#elif defined(Q_OS_WIN) + m_eventDispatcher = new QOffscreenEventDispatcher<QEventDispatcherWin32>(); + m_fontDatabase.reset(new QBasicFontDatabase()); +#endif + + m_drag.reset(new QOffscreenDrag); + m_services.reset(new QPlatformServices); + + QGuiApplicationPrivate::instance()->setEventDispatcher(m_eventDispatcher); + screenAdded(new QOffscreenScreen); +} + +QOffscreenIntegration::~QOffscreenIntegration() +{ +} + +bool QOffscreenIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case ThreadedPixmaps: return true; + case MultipleWindows: return true; + default: return QPlatformIntegration::hasCapability(cap); + } +} + +QPlatformWindow *QOffscreenIntegration::createPlatformWindow(QWindow *window) const +{ + Q_UNUSED(window); + QPlatformWindow *w = new QOffscreenWindow(window); + w->requestActivateWindow(); + return w; +} + +QPlatformBackingStore *QOffscreenIntegration::createPlatformBackingStore(QWindow *window) const +{ + return new QOffscreenBackingStore(window); +} + +QAbstractEventDispatcher *QOffscreenIntegration::guiThreadEventDispatcher() const +{ + return m_eventDispatcher; +} + +QPlatformFontDatabase *QOffscreenIntegration::fontDatabase() const +{ + return m_fontDatabase.data(); +} + +QPlatformDrag *QOffscreenIntegration::drag() const +{ + return m_drag.data(); +} + +QPlatformServices *QOffscreenIntegration::services() const +{ + return m_services.data(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.h b/src/plugins/platforms/offscreen/qoffscreenintegration.h new file mode 100644 index 0000000000..eb03100ec9 --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenintegration.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOFFSCREENINTEGRATION_H +#define QOFFSCREENINTEGRATION_H + +#include <qpa/qplatformintegration.h> + +#include <qscopedpointer.h> + +QT_BEGIN_NAMESPACE + +class QOffscreenBackendData; + +class QOffscreenIntegration : public QPlatformIntegration +{ +public: + QOffscreenIntegration(); + ~QOffscreenIntegration(); + + bool hasCapability(QPlatformIntegration::Capability cap) const; + + QPlatformWindow *createPlatformWindow(QWindow *window) const; + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + QPlatformDrag *drag() const; + QPlatformServices *services() const; + + QPlatformFontDatabase *fontDatabase() const; + QAbstractEventDispatcher *guiThreadEventDispatcher() const; + + static QOffscreenIntegration *createOffscreenIntegration(); + +private: + QAbstractEventDispatcher *m_eventDispatcher; + QScopedPointer<QPlatformFontDatabase> m_fontDatabase; + QScopedPointer<QPlatformDrag> m_drag; + QScopedPointer<QPlatformServices> m_services; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_dummy.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration_dummy.cpp new file mode 100644 index 0000000000..8bc1b17a56 --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenintegration_dummy.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qoffscreenintegration.h" + +QOffscreenIntegration *QOffscreenIntegration::createOffscreenIntegration() +{ + return new QOffscreenIntegration; +} diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp new file mode 100644 index 0000000000..4b27afd80f --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qoffscreenintegration_x11.h" + +#include <QByteArray> +#include <QOpenGLContext> + +#include <X11/Xlib.h> +#include <GL/glx.h> + +#include <QtPlatformSupport/private/qglxconvenience_p.h> + +#include <qpa/qplatformsurface.h> +#include <qsurface.h> + +QT_BEGIN_NAMESPACE + +QOffscreenIntegration *QOffscreenIntegration::createOffscreenIntegration() +{ + return new QOffscreenX11Integration; +} + +bool QOffscreenX11Integration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case OpenGL: return true; + case ThreadedOpenGL: return true; + default: return QOffscreenIntegration::hasCapability(cap); + } +} + +QPlatformOpenGLContext *QOffscreenX11Integration::createPlatformOpenGLContext(QOpenGLContext *context) const +{ + if (!m_connection) + m_connection.reset(new QOffscreenX11Connection); + + return new QOffscreenX11GLXContext(m_connection->x11Info(), context); +} + +QOffscreenX11Connection::QOffscreenX11Connection() +{ + XInitThreads(); + + QByteArray displayName = qgetenv("DISPLAY"); + Display *display = XOpenDisplay(displayName.constData()); + m_display = display; + m_screenNumber = DefaultScreen(display); +} + +QOffscreenX11Connection::~QOffscreenX11Connection() +{ + XCloseDisplay((Display *)m_display); +} + +class QOffscreenX11Info +{ +public: + QOffscreenX11Info(QOffscreenX11Connection *connection) + : m_connection(connection) + { + } + + Display *display() const { + return (Display *)m_connection->display(); + } + + Window root() const { + return DefaultRootWindow(display()); + } + + int screenNumber() const { + return m_connection->screenNumber(); + } + +private: + QOffscreenX11Connection *m_connection; +}; + +QOffscreenX11Info *QOffscreenX11Connection::x11Info() +{ + if (!m_x11Info) + m_x11Info.reset(new QOffscreenX11Info(this)); + return m_x11Info.data(); +} + +class QOffscreenX11GLXContextData +{ +public: + QOffscreenX11Info *x11; + QSurfaceFormat format; + GLXContext context; + GLXContext shareContext; + Window window; +}; + +static Window createDummyWindow(QOffscreenX11Info *x11, XVisualInfo *visualInfo) +{ + Colormap cmap = XCreateColormap(x11->display(), x11->root(), visualInfo->visual, AllocNone); + XSetWindowAttributes a; + a.background_pixel = WhitePixel(x11->display(), x11->screenNumber()); + a.border_pixel = BlackPixel(x11->display(), x11->screenNumber()); + a.colormap = cmap; + + Window window = XCreateWindow(x11->display(), x11->root(), + 0, 0, 100, 100, + 0, visualInfo->depth, InputOutput, visualInfo->visual, + CWBackPixel|CWBorderPixel|CWColormap, &a); + XFreeColormap(x11->display(), cmap); + return window; +} + +static Window createDummyWindow(QOffscreenX11Info *x11, GLXFBConfig config) +{ + XVisualInfo *visualInfo = glXGetVisualFromFBConfig(x11->display(), config); + if (!visualInfo) + qFatal("Could not initialize GLX"); + Window window = createDummyWindow(x11, visualInfo); + XFree(visualInfo); + return window; +} + +QOffscreenX11GLXContext::QOffscreenX11GLXContext(QOffscreenX11Info *x11, QOpenGLContext *context) + : d(new QOffscreenX11GLXContextData) +{ + d->x11 = x11; + d->format = context->format(); + + d->shareContext = 0; + if (context->shareHandle()) + d->shareContext = static_cast<QOffscreenX11GLXContext *>(context->shareHandle())->d->context; + + GLXFBConfig config = qglx_findConfig(x11->display(), x11->screenNumber(), d->format); + if (config) { + d->context = glXCreateNewContext(x11->display(), config, GLX_RGBA_TYPE, d->shareContext, true); + if (!d->context && d->shareContext) { + d->shareContext = 0; + // re-try without a shared glx context + d->context = glXCreateNewContext(x11->display(), config, GLX_RGBA_TYPE, 0, true); + } + + // Get the basic surface format details + if (d->context) + d->format = qglx_surfaceFormatFromGLXFBConfig(x11->display(), config, d->context); + + // Create a temporary window so that we can make the new context current + d->window = createDummyWindow(x11, config); + } else { + XVisualInfo *visualInfo = qglx_findVisualInfo(x11->display(), 0, &d->format); + if (!visualInfo) + qFatal("Could not initialize GLX"); + d->context = glXCreateContext(x11->display(), visualInfo, d->shareContext, true); + if (!d->context && d->shareContext) { + // re-try without a shared glx context + d->shareContext = 0; + d->context = glXCreateContext(x11->display(), visualInfo, 0, true); + } + + d->window = createDummyWindow(x11, visualInfo); + XFree(visualInfo); + } +} + +QOffscreenX11GLXContext::~QOffscreenX11GLXContext() +{ + glXDestroyContext(d->x11->display(), d->context); + XDestroyWindow(d->x11->display(), d->window); +} + +bool QOffscreenX11GLXContext::makeCurrent(QPlatformSurface *surface) +{ + QSize size = surface->surface()->size(); + + XResizeWindow(d->x11->display(), d->window, size.width(), size.height()); + XSync(d->x11->display(), true); + + if (glXMakeCurrent(d->x11->display(), d->window, d->context)) { + glViewport(0, 0, size.width(), size.height()); + return true; + } + + return false; +} + +void QOffscreenX11GLXContext::doneCurrent() +{ + glXMakeCurrent(d->x11->display(), 0, 0); +} + +void QOffscreenX11GLXContext::swapBuffers(QPlatformSurface *) +{ +} + +void (*QOffscreenX11GLXContext::getProcAddress(const QByteArray &procName)) () +{ + return (void (*)())glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(procName.constData())); +} + +QSurfaceFormat QOffscreenX11GLXContext::format() const +{ + return d->format; +} + +bool QOffscreenX11GLXContext::isSharing() const +{ + return d->shareContext; +} + +bool QOffscreenX11GLXContext::isValid() const +{ + return d->context && d->window; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h new file mode 100644 index 0000000000..9afa302b04 --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOFFSCREENINTEGRATION_X11_H +#define QOFFSCREENINTEGRATION_X11_H + +#include "qoffscreenintegration.h" + +#include <qglobal.h> +#include <qscopedpointer.h> + +#include <qpa/qplatformopenglcontext.h> + +QT_BEGIN_NAMESPACE + +class QOffscreenX11Connection; +class QOffscreenX11Info; + +class QOffscreenX11Integration : public QOffscreenIntegration +{ +public: + bool hasCapability(QPlatformIntegration::Capability cap) const; + + QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; + +private: + mutable QScopedPointer<QOffscreenX11Connection> m_connection; +}; + +class QOffscreenX11Connection { +public: + QOffscreenX11Connection(); + ~QOffscreenX11Connection(); + + QOffscreenX11Info *x11Info(); + + void *display() const { return m_display; } + int screenNumber() const { return m_screenNumber; } + +private: + void *m_display; + int m_screenNumber; + + QScopedPointer<QOffscreenX11Info> m_x11Info; +}; + +class QOffscreenX11GLXContextData; + +class QOffscreenX11GLXContext : public QPlatformOpenGLContext +{ +public: + QOffscreenX11GLXContext(QOffscreenX11Info *x11, QOpenGLContext *context); + ~QOffscreenX11GLXContext(); + + bool makeCurrent(QPlatformSurface *surface); + void doneCurrent(); + void swapBuffers(QPlatformSurface *surface); + void (*getProcAddress(const QByteArray &procName)) (); + + QSurfaceFormat format() const; + bool isSharing() const; + bool isValid() const; + +private: + QScopedPointer<QOffscreenX11GLXContextData> d; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/offscreen/qoffscreenwindow.cpp b/src/plugins/platforms/offscreen/qoffscreenwindow.cpp new file mode 100644 index 0000000000..702ef2300c --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenwindow.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qoffscreenwindow.h" +#include "qoffscreencommon.h" + +#include <qpa/qplatformscreen.h> +#include <qpa/qwindowsysteminterface.h> + +#include <private/qwindow_p.h> + +QT_BEGIN_NAMESPACE + +QOffscreenWindow::QOffscreenWindow(QWindow *window) + : QPlatformWindow(window) + , m_positionIncludesFrame(false) + , m_visible(false) + , m_pendingGeometryChangeOnShow(true) +{ + if (window->windowState() == Qt::WindowNoState) + setGeometry(window->geometry()); + else + setWindowState(window->windowState()); + + QWindowSystemInterface::flushWindowSystemEvents(); + + static WId counter = 0; + m_winId = ++counter; + + m_windowForWinIdHash[m_winId] = this; +} + +QOffscreenWindow::~QOffscreenWindow() +{ + if (QOffscreenScreen::windowContainingCursor == this) + QOffscreenScreen::windowContainingCursor = 0; + m_windowForWinIdHash.remove(m_winId); +} + +void QOffscreenWindow::setGeometry(const QRect &rect) +{ + if (window()->windowState() != Qt::WindowNoState) + return; + + m_positionIncludesFrame = qt_window_private(window())->positionPolicy == QWindowPrivate::WindowFrameInclusive; + + setFrameMarginsEnabled(true); + setGeometryImpl(rect); + + m_normalGeometry = geometry(); +} + +void QOffscreenWindow::setGeometryImpl(const QRect &rect) +{ + QRect adjusted = rect; + if (adjusted.width() <= 0) + adjusted.setWidth(1); + if (adjusted.height() <= 0) + adjusted.setHeight(1); + + if (m_positionIncludesFrame) { + adjusted.translate(m_margins.left(), m_margins.top()); + } else { + // make sure we're not placed off-screen + if (adjusted.left() < m_margins.left()) + adjusted.translate(m_margins.left(), 0); + if (adjusted.top() < m_margins.top()) + adjusted.translate(0, m_margins.top()); + } + + QPlatformWindow::setGeometry(adjusted); + + if (m_visible) { + QWindowSystemInterface::handleGeometryChange(window(), adjusted); + QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), adjusted.size())); + } else { + m_pendingGeometryChangeOnShow = true; + } +} + +void QOffscreenWindow::setVisible(bool visible) +{ + if (visible == m_visible) + return; + + if (visible) { + if (window()->type() != Qt::ToolTip) + QWindowSystemInterface::handleWindowActivated(window()); + + if (m_pendingGeometryChangeOnShow) { + m_pendingGeometryChangeOnShow = false; + QWindowSystemInterface::handleGeometryChange(window(), geometry()); + } + } + + if (visible) { + QRect rect(QPoint(), geometry().size()); + QWindowSystemInterface::handleExposeEvent(window(), rect); + } else { + QWindowSystemInterface::handleExposeEvent(window(), QRegion()); + } + + m_visible = visible; +} + +void QOffscreenWindow::requestActivateWindow() +{ + if (m_visible) + QWindowSystemInterface::handleWindowActivated(window()); +} + +WId QOffscreenWindow::winId() const +{ + return m_winId; +} + +QMargins QOffscreenWindow::frameMargins() const +{ + return m_margins; +} + +void QOffscreenWindow::setFrameMarginsEnabled(bool enabled) +{ + if (enabled && !(window()->flags() & Qt::FramelessWindowHint)) + m_margins = QMargins(2, 2, 2, 2); + else + m_margins = QMargins(0, 0, 0, 0); +} + +void QOffscreenWindow::setWindowState(Qt::WindowState state) +{ + setFrameMarginsEnabled(state != Qt::WindowFullScreen); + m_positionIncludesFrame = false; + + switch (state) { + case Qt::WindowFullScreen: + setGeometryImpl(screen()->geometry()); + break; + case Qt::WindowMaximized: + setGeometryImpl(screen()->availableGeometry().adjusted(m_margins.left(), m_margins.top(), -m_margins.right(), -m_margins.bottom())); + break; + case Qt::WindowMinimized: + break; + case Qt::WindowNoState: + setGeometryImpl(m_normalGeometry); + break; + default: + break; + } + + QWindowSystemInterface::handleWindowStateChanged(window(), state); +} + +QOffscreenWindow *QOffscreenWindow::windowForWinId(WId id) +{ + return m_windowForWinIdHash.value(id, 0); +} + +QHash<WId, QOffscreenWindow *> QOffscreenWindow::m_windowForWinIdHash; + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/offscreen/qoffscreenwindow.h b/src/plugins/platforms/offscreen/qoffscreenwindow.h new file mode 100644 index 0000000000..cd1cf8e0aa --- /dev/null +++ b/src/plugins/platforms/offscreen/qoffscreenwindow.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOFFSCREENWINDOW_H +#define QOFFSCREENWINDOW_H + +#include <qpa/qplatformbackingstore.h> +#include <qpa/qplatformwindow.h> + +#include <qhash.h> + +QT_BEGIN_NAMESPACE + +class QOffscreenWindow : public QPlatformWindow +{ +public: + QOffscreenWindow(QWindow *window); + ~QOffscreenWindow(); + + void setGeometry(const QRect &rect); + void setWindowState(Qt::WindowState state); + + QMargins frameMargins() const; + + void setVisible(bool visible); + void requestActivateWindow(); + + WId winId() const; + + static QOffscreenWindow *windowForWinId(WId id); + +private: + void setFrameMarginsEnabled(bool enabled); + void setGeometryImpl(const QRect &rect); + + QRect m_normalGeometry; + QMargins m_margins; + bool m_positionIncludesFrame; + bool m_visible; + bool m_pendingGeometryChangeOnShow; + WId m_winId; + + static QHash<WId, QOffscreenWindow *> m_windowForWinIdHash; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index 828867c3a7..173757568f 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -1,6 +1,6 @@ TEMPLATE = subdirs -SUBDIRS += minimal +SUBDIRS += minimal offscreen contains(QT_CONFIG, xcb) { SUBDIRS += xcb diff --git a/src/plugins/platforms/qnx/qnx.pro b/src/plugins/platforms/qnx/qnx.pro index 8367513fc5..203cdebda9 100644 --- a/src/plugins/platforms/qnx/qnx.pro +++ b/src/plugins/platforms/qnx/qnx.pro @@ -39,6 +39,7 @@ CONFIG(blackberry) { #DEFINES += QQNXSCREEN_DEBUG #DEFINES += QQNXVIRTUALKEYBOARD_DEBUG #DEFINES += QQNXWINDOW_DEBUG +#DEFINES += QQNXCURSOR_DEBUG SOURCES = main.cpp \ @@ -54,7 +55,8 @@ SOURCES = main.cpp \ qqnxnavigatoreventhandler.cpp \ qqnxabstractnavigator.cpp \ qqnxabstractvirtualkeyboard.cpp \ - qqnxservices.cpp + qqnxservices.cpp \ + qqnxcursor.cpp HEADERS = main.h \ qqnxbuffer.h \ @@ -70,7 +72,8 @@ HEADERS = main.h \ qqnxnavigatoreventhandler.h \ qqnxabstractnavigator.h \ qqnxabstractvirtualkeyboard.h \ - qqnxservices.h + qqnxservices.h \ + qqnxcursor.h LIBS += -lscreen diff --git a/src/plugins/platforms/qnx/qqnxcursor.cpp b/src/plugins/platforms/qnx/qqnxcursor.cpp new file mode 100644 index 0000000000..4fdff666d7 --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxcursor.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqnxcursor.h" + +#include <QtCore/QDebug> + +#ifdef QQNXCURSOR_DEBUG +#define qCursorDebug qDebug +#else +#define qCursorDebug QT_NO_QDEBUG_MACRO +#endif + +QT_BEGIN_NAMESPACE + +QQnxCursor::QQnxCursor() +{ +} + +#ifndef QT_NO_CURSOR +void QQnxCursor::changeCursor(QCursor *windowCursor, QWindow *window) +{ + Q_UNUSED(windowCursor); + Q_UNUSED(window); +} +#endif + +void QQnxCursor::setPos(const QPoint &pos) +{ + qCursorDebug() << "QQnxCursor::setPos -" << pos; + m_pos = pos; +} + +QPoint QQnxCursor::pos() const +{ + qCursorDebug() << "QQnxCursor::pos -" << m_pos; + return m_pos; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxcursor.h b/src/plugins/platforms/qnx/qqnxcursor.h new file mode 100644 index 0000000000..5d6a8b2c30 --- /dev/null +++ b/src/plugins/platforms/qnx/qqnxcursor.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQNXCURSOR_H +#define QQNXCURSOR_H + +#include <qpa/qplatformcursor.h> + +QT_BEGIN_NAMESPACE + +class QQnxCursor : public QPlatformCursor +{ +public: + QQnxCursor(); + +#ifndef QT_NO_CURSOR + void changeCursor(QCursor *windowCursor, QWindow *window); +#endif + void setPos(const QPoint &pos); + + QPoint pos() const; + +private: + QPoint m_pos; +}; + +QT_END_NAMESPACE + +#endif // QQNXCURSOR_H diff --git a/src/plugins/platforms/qnx/qqnxscreen.cpp b/src/plugins/platforms/qnx/qqnxscreen.cpp index 1e58f047ab..fc8b3bb167 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.cpp +++ b/src/plugins/platforms/qnx/qqnxscreen.cpp @@ -41,6 +41,7 @@ #include "qqnxscreen.h" #include "qqnxwindow.h" +#include "qqnxcursor.h" #include <QtCore/QThread> #include <QtCore/QDebug> @@ -110,7 +111,8 @@ QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, m_posted(false), m_keyboardHeight(0), m_nativeOrientation(Qt::PrimaryOrientation), - m_platformContext(0) + m_platformContext(0), + m_cursor(new QQnxCursor()) { qScreenDebug() << Q_FUNC_INFO; // Cache initial orientation of this display @@ -149,6 +151,8 @@ QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, QQnxScreen::~QQnxScreen() { qScreenDebug() << Q_FUNC_INFO; + + delete m_cursor; } static int defaultDepth() @@ -492,6 +496,11 @@ void QQnxScreen::onWindowPost(QQnxWindow *window) } } +QPlatformCursor * QQnxScreen::cursor() const +{ + return m_cursor; +} + void QQnxScreen::keyboardHeightChanged(int height) { if (height == m_keyboardHeight) diff --git a/src/plugins/platforms/qnx/qqnxscreen.h b/src/plugins/platforms/qnx/qqnxscreen.h index 2851c13c52..39cd4159d1 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.h +++ b/src/plugins/platforms/qnx/qqnxscreen.h @@ -95,6 +95,8 @@ public: QSharedPointer<QQnxRootWindow> rootWindow() const; + QPlatformCursor *cursor() const; + public Q_SLOTS: void setRotation(int rotation); void newWindowCreated(void *window); @@ -130,6 +132,8 @@ private: QList<QQnxWindow *> m_childWindows; QList<screen_window_t> m_overlays; + + QPlatformCursor *m_cursor; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp index 4412bb34bd..2d3c7608bf 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp @@ -347,6 +347,8 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType) qFatal("QQNX: failed to query event position, errno=%d", errno); } + QCursor::setPos(pos[0], pos[1]); + // get window coordinates of touch errno = 0; int windowPos[2]; diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp index 3ccc6212a9..8830d3023a 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp +++ b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp @@ -162,21 +162,10 @@ void QWindowsAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) } } - typedef void (WINAPI *PtrNotifyWinEvent)(DWORD, HWND, LONG, LONG); - #if defined(Q_OS_WINCE) // ### TODO: check for NotifyWinEvent in CE 6.0 // There is no user32.lib nor NotifyWinEvent for CE return; #else - static PtrNotifyWinEvent ptrNotifyWinEvent = 0; - static bool resolvedNWE = false; - if (!resolvedNWE) { - resolvedNWE = true; - ptrNotifyWinEvent = (PtrNotifyWinEvent)QSystemLibrary::resolve(QLatin1String("user32"), "NotifyWinEvent"); - } - if (!ptrNotifyWinEvent) - return; - // An event has to be associated with a window, // so find the first parent that is a widget and that has a WId QAccessibleInterface *iface = event->accessibleInterface(); @@ -213,7 +202,7 @@ void QWindowsAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) eventNum %= 50; //[0..49] int eventId = - (eventNum - 1); qAccessibleRecentSentEvents()->insert(eventId, qMakePair(QPointer<QObject>(event->object()), event->child())); - ptrNotifyWinEvent(event->type(), hWnd, OBJID_CLIENT, eventId); + ::NotifyWinEvent(event->type(), hWnd, OBJID_CLIENT, eventId); ++eventNum; } #endif // Q_OS_WINCE @@ -283,7 +272,7 @@ bool QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(HWND hwnd, W { if (static_cast<long>(lParam) == static_cast<long>(UiaRootObjectId)) { /* For UI Automation */ - } else if ((DWORD)lParam == OBJID_CLIENT) { + } else if ((DWORD)lParam == DWORD(OBJID_CLIENT)) { #if 1 // Ignoring all requests while starting up // ### Maybe QPA takes care of this??? diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h index af0bd65c80..5a6dc0c2e5 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h +++ b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h @@ -48,7 +48,6 @@ #include <oleacc.h> -QT_BEGIN_HEADER QT_BEGIN_NAMESPACE class QWindowsAccessibility : public QPlatformAccessibility @@ -69,6 +68,5 @@ public: }; QT_END_NAMESPACE -QT_END_HEADER #endif // QWINDOWSACCESSIBILITY_H diff --git a/src/plugins/platforms/windows/qtwindows_additional.h b/src/plugins/platforms/windows/qtwindows_additional.h index 8d59fbd7c6..3b2e9787a2 100644 --- a/src/plugins/platforms/windows/qtwindows_additional.h +++ b/src/plugins/platforms/windows/qtwindows_additional.h @@ -126,6 +126,10 @@ typedef struct tagUPDATELAYEREDWINDOWINFO { // IME. #define IMR_CONFIRMRECONVERTSTRING 0x0005 +#ifndef MAPVK_VK_TO_CHAR +# define MAPVK_VK_TO_CHAR 2 +#endif + #endif // if defined(Q_CC_MINGW) /* Touch is supported from Windows 7 onwards and data structures diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 99ef3aacf3..a6709dffb3 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -783,7 +783,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, d->m_creationContext->obtainedGeometry.moveTo(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return true; case QtWindows::CalculateSize: - return false; + return QWindowsGeometryHint::handleCalculateSize(d->m_creationContext->customMargins, msg, result); default: break; } @@ -818,12 +818,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, platformWindow->getSizeHints(reinterpret_cast<MINMAXINFO *>(lParam)); return true;// maybe available on some SDKs revisit WM_NCCALCSIZE case QtWindows::CalculateSize: - // NCCALCSIZE_PARAMS structure if wParam==TRUE - if (wParam && QWindowsContext::verboseWindows) { - const NCCALCSIZE_PARAMS *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(lParam); - qDebug() << platformWindow->window() << *ncp; - } - break; + return QWindowsGeometryHint::handleCalculateSize(platformWindow->customMargins(), msg, result); #endif case QtWindows::ExposeEvent: return platformWindow->handleWmPaint(hwnd, message, wParam, lParam); diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp index 4dc9af61d8..5b2a3acbae 100644 --- a/src/plugins/platforms/windows/qwindowscursor.cpp +++ b/src/plugins/platforms/windows/qwindowscursor.cpp @@ -395,6 +395,19 @@ QWindowsWindowCursor QWindowsCursor::standardWindowCursor(Qt::CursorShape shape) } /*! + \brief Return cached pixmap cursor or create new one. +*/ + +QWindowsWindowCursor QWindowsCursor::pixmapWindowCursor(const QCursor &c) +{ + const qint64 cacheKey = c.pixmap().cacheKey(); + PixmapCursorCache::iterator it = m_pixmapCursorCache.find(cacheKey); + if (it == m_pixmapCursorCache.end()) + it = m_pixmapCursorCache.insert(cacheKey, QWindowsWindowCursor(c)); + return it.value(); +} + +/*! \brief Set a cursor on a window. This is called frequently as the mouse moves over widgets in the window @@ -406,11 +419,15 @@ void QWindowsCursor::changeCursor(QCursor *cursorIn, QWindow *window) if (QWindowsContext::verboseWindows > 1) qDebug() << __FUNCTION__ << cursorIn << window; - if (!cursorIn || !window) + if (!window) + return; + if (!cursorIn) { + QWindowsWindow::baseWindowOf(window)->setCursor(QWindowsWindowCursor()); return; + } const QWindowsWindowCursor wcursor = cursorIn->shape() == Qt::BitmapCursor ? - QWindowsWindowCursor(*cursorIn) : standardWindowCursor(cursorIn->shape()); + pixmapWindowCursor(*cursorIn) : standardWindowCursor(cursorIn->shape()); if (wcursor.handle()) { QWindowsWindow::baseWindowOf(window)->setCursor(wcursor); } else { @@ -449,6 +466,7 @@ void QWindowsCursor::setPos(const QPoint &pos) class QWindowsWindowCursorData : public QSharedData { public: + QWindowsWindowCursorData() : m_cursor(Qt::ArrowCursor), m_handle(0) {} explicit QWindowsWindowCursorData(const QCursor &c); ~QWindowsWindowCursorData(); @@ -464,7 +482,13 @@ QWindowsWindowCursorData::QWindowsWindowCursorData(const QCursor &c) : QWindowsWindowCursorData::~QWindowsWindowCursorData() { - DestroyCursor(m_handle); + if (m_handle) + DestroyCursor(m_handle); +} + +QWindowsWindowCursor::QWindowsWindowCursor() : + m_data(new QWindowsWindowCursorData) +{ } QWindowsWindowCursor::QWindowsWindowCursor(const QCursor &c) : @@ -488,6 +512,11 @@ QWindowsWindowCursor & QWindowsWindowCursor::operator =(const QWindowsWindowCurs return *this; } +bool QWindowsWindowCursor::isNull() const +{ + return m_data->m_handle == 0; +} + QCursor QWindowsWindowCursor::cursor() const { return m_data->m_cursor; diff --git a/src/plugins/platforms/windows/qwindowscursor.h b/src/plugins/platforms/windows/qwindowscursor.h index b0256d4203..1e818bc9b8 100644 --- a/src/plugins/platforms/windows/qwindowscursor.h +++ b/src/plugins/platforms/windows/qwindowscursor.h @@ -55,11 +55,13 @@ class QWindowsWindowCursorData; class QWindowsWindowCursor { public: + QWindowsWindowCursor(); explicit QWindowsWindowCursor(const QCursor &c); ~QWindowsWindowCursor(); QWindowsWindowCursor(const QWindowsWindowCursor &c); QWindowsWindowCursor &operator=(const QWindowsWindowCursor &c); + bool isNull() const; QCursor cursor() const; HCURSOR handle() const; @@ -81,11 +83,14 @@ public: static QPoint mousePosition(); QWindowsWindowCursor standardWindowCursor(Qt::CursorShape s = Qt::ArrowCursor); + QWindowsWindowCursor pixmapWindowCursor(const QCursor &c); private: typedef QHash<Qt::CursorShape, QWindowsWindowCursor> StandardCursorCache; + typedef QHash<qint64, QWindowsWindowCursor> PixmapCursorCache; StandardCursorCache m_standardCursorCache; + PixmapCursorCache m_pixmapCursorCache; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index 74193c47a3..e9c0cccc14 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -58,6 +58,10 @@ #include <QtCore/QObject> #include <QtCore/QThread> #include <QtCore/QSysInfo> +#include <QtCore/QSharedData> +#include <QtCore/QExplicitlySharedDataPointer> +#include <QtCore/QMutex> +#include <QtCore/QMutexLocker> #include <QtCore/private/qsystemlibrary_p.h> #include "qtwindows_additional.h" @@ -387,6 +391,9 @@ void eatMouseMove() Vista on) that mimick the behaviour of their QDialog counterparts as close as possible. + Instances of derived classes are controlled by + QWindowsDialogHelperBase-derived classes. + A major difference is that there is only an exec(), which is a modal, blocking call; there is no non-blocking show(). There 2 types of native dialogs: @@ -401,6 +408,7 @@ void eatMouseMove() like close() can be called on them from event handlers. \endlist + \sa QWindowsDialogHelperBase \internal \ingroup qt-lighthouse-win */ @@ -411,7 +419,6 @@ class QWindowsNativeDialogBase : public QObject public: virtual void setWindowTitle(const QString &title) = 0; virtual void exec(HWND owner = 0) = 0; - virtual QPlatformDialogHelper::DialogCode result() const = 0; signals: void accepted(); @@ -432,12 +439,10 @@ protected: The native dialog is created in setVisible_sys() since then modality and the state of DontUseNativeDialog is known. - Modal dialogs are then started via the platformNativeDialogModalHelp(), - platformNativeDialogModalHelp() slots. - Non-modal dialogs are shown using a separate thread should - they support it. + Modal dialogs are then run by exec(). Non-modal dialogs are shown using a + separate thread started in show() should they support it. - \sa QWindowsDialogThread + \sa QWindowsDialogThread, QWindowsNativeDialogBase \internal \ingroup qt-lighthouse-win */ @@ -445,17 +450,12 @@ protected: template <class BaseClass> QWindowsDialogHelperBase<BaseClass>::QWindowsDialogHelperBase() : m_nativeDialog(0), - m_ownerWindow(0) + m_ownerWindow(0), + m_timerId(0) { } template <class BaseClass> -QWindowsDialogHelperBase<BaseClass>::~QWindowsDialogHelperBase() -{ - delete m_nativeDialog; -} - -template <class BaseClass> QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::nativeDialog() const { if (!m_nativeDialog) { @@ -466,6 +466,19 @@ QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::nativeDialog() co } template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::deleteNativeDialog() +{ + delete m_nativeDialog; + m_nativeDialog = 0; +} + +template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::timerEvent(QTimerEvent *) +{ + startDialogThread(); +} + +template <class BaseClass> QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::ensureNativeDialog() { // Create dialog and apply common settings. @@ -486,22 +499,18 @@ QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::ensureNativeDialo class QWindowsDialogThread : public QThread { public: - QWindowsDialogThread(QWindowsNativeDialogBase *dialog, - HWND owner = 0) : - m_dialog(dialog), m_owner(owner) {} - + QWindowsDialogThread(QPlatformDialogHelper *h) : m_helper(h) {} void run(); private: - QWindowsNativeDialogBase *m_dialog; - const HWND m_owner; + QPlatformDialogHelper *m_helper; }; void QWindowsDialogThread::run() { if (QWindowsContext::verboseDialogs) qDebug(">%s" , __FUNCTION__); - m_dialog->exec(m_owner); + m_helper->exec(); deleteLater(); if (QWindowsContext::verboseDialogs) qDebug("<%s" , __FUNCTION__); @@ -512,7 +521,7 @@ bool QWindowsDialogHelperBase<BaseClass>::show(Qt::WindowFlags, Qt::WindowModality windowModality, QWindow *parent) { - const bool modal = (windowModality == Qt::ApplicationModal); + const bool modal = (windowModality != Qt::NonModal); if (parent) { m_ownerWindow = QWindowsWindow::handleOf(parent); } else { @@ -521,18 +530,40 @@ bool QWindowsDialogHelperBase<BaseClass>::show(Qt::WindowFlags, if (QWindowsContext::verboseDialogs) qDebug("%s modal=%d native=%p parent=%p" , __FUNCTION__, modal, m_nativeDialog, m_ownerWindow); - if (!modal && !supportsNonModalDialog()) + if (!modal && !supportsNonModalDialog(parent)) return false; // Was it changed in-between? if (!ensureNativeDialog()) return false; - if (!modal) { // Modal dialogs are shown in separate slot. - QWindowsDialogThread *thread = new QWindowsDialogThread(m_nativeDialog, m_ownerWindow); - thread->start(); + // Start a background thread to show the dialog. For modal dialogs, + // a subsequent call to exec() may follow. So, start an idle timer + // which will start the dialog thread. If exec() is then called, the + // timer is stopped and dialog->exec() is called directly. + if (modal) { + m_timerId = this->startTimer(0); + } else { + startDialogThread(); } return true; } template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::startDialogThread() +{ + QWindowsDialogThread *thread = new QWindowsDialogThread(this); + thread->start(); + stopTimer(); +} + +template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::stopTimer() +{ + if (m_timerId) { + this->killTimer(m_timerId); + m_timerId = 0; + } +} + +template <class BaseClass> void QWindowsDialogHelperBase<BaseClass>::hide() { if (m_nativeDialog) @@ -545,8 +576,11 @@ void QWindowsDialogHelperBase<BaseClass>::exec() { if (QWindowsContext::verboseDialogs) qDebug("%s" , __FUNCTION__); - if (QWindowsNativeDialogBase *nd = nativeDialog()) + stopTimer(); + if (QWindowsNativeDialogBase *nd = nativeDialog()) { nd->exec(m_ownerWindow); + deleteNativeDialog(); + } } static inline bool snapToDefaultButtonHint() @@ -568,6 +602,101 @@ QVariant QWindowsDialogHelperBase<BaseClass>::styleHint(QPlatformDialogHelper::S } /*! + \class QWindowsFileDialogSharedData + \brief Explicitly shared file dialog parameters that are not in QFileDialogOptions. + + Contain Parameters that need to be cached while the native dialog does not + exist yet. In addition, the data are updated by the change notifications of the + IFileDialogEvent, as querying them after the dialog has closed + does not reliably work. Provides thread-safe setters (for the non-modal case). + + \internal + \ingroup qt-lighthouse-win + \sa QFileDialogOptions +*/ + +class QWindowsFileDialogSharedData +{ +public: + QWindowsFileDialogSharedData() : m_data(new Data) {} + void fromOptions(const QSharedPointer<QFileDialogOptions> &o); + + QString directory() const; + void setDirectory(const QString &); + QString selectedNameFilter() const; + void setSelectedNameFilter(const QString &); + QStringList selectedFiles() const; + void setSelectedFiles(const QStringList &); + QString selectedFile() const; + +private: + class Data : public QSharedData { + public: + QString directory; + QString selectedNameFilter; + QStringList selectedFiles; + QMutex mutex; + }; + QExplicitlySharedDataPointer<Data> m_data; +}; + +inline QString QWindowsFileDialogSharedData::directory() const +{ + m_data->mutex.lock(); + const QString result = m_data->directory; + m_data->mutex.unlock(); + return result; +} + +inline void QWindowsFileDialogSharedData::setDirectory(const QString &d) +{ + QMutexLocker (&m_data->mutex); + m_data->directory = d; +} + +inline QString QWindowsFileDialogSharedData::selectedNameFilter() const +{ + m_data->mutex.lock(); + const QString result = m_data->selectedNameFilter; + m_data->mutex.unlock(); + return result; +} + +inline void QWindowsFileDialogSharedData::setSelectedNameFilter(const QString &f) +{ + QMutexLocker (&m_data->mutex); + m_data->selectedNameFilter = f; +} + +inline QStringList QWindowsFileDialogSharedData::selectedFiles() const +{ + m_data->mutex.lock(); + const QStringList result = m_data->selectedFiles; + m_data->mutex.unlock(); + return result; +} + +inline QString QWindowsFileDialogSharedData::selectedFile() const +{ + const QStringList files = selectedFiles(); + return files.isEmpty() ? QString() : files.front(); +} + +inline void QWindowsFileDialogSharedData::setSelectedFiles(const QStringList &f) +{ + QMutexLocker (&m_data->mutex); + m_data->selectedFiles = f; +} + +inline void QWindowsFileDialogSharedData::fromOptions(const QSharedPointer<QFileDialogOptions> &o) +{ + QMutexLocker (&m_data->mutex); + m_data->directory = o->initialDirectory(); + m_data->selectedFiles = o->initiallySelectedFiles(); + m_data->selectedNameFilter = o->initiallySelectedNameFilter(); +} + +/*! \class QWindowsNativeFileDialogEventHandler \brief Listens to IFileDialog events and forwards them to QWindowsNativeFileDialogBase @@ -609,7 +738,7 @@ public: } // IFileDialogEvents methods - IFACEMETHODIMP OnFileOk(IFileDialog *) { return S_OK; } + IFACEMETHODIMP OnFileOk(IFileDialog *); IFACEMETHODIMP OnFolderChange(IFileDialog *) { return S_OK; } IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *); IFACEMETHODIMP OnHelp(IFileDialog *) { return S_OK; } @@ -658,15 +787,17 @@ class QWindowsNativeFileDialogBase : public QWindowsNativeDialogBase public: ~QWindowsNativeFileDialogBase(); - inline static QWindowsNativeFileDialogBase *create(QFileDialogOptions::AcceptMode am); + inline static QWindowsNativeFileDialogBase *create(QFileDialogOptions::AcceptMode am, const QWindowsFileDialogSharedData &data); virtual void setWindowTitle(const QString &title); inline void setMode(QFileDialogOptions::FileMode mode, QFileDialogOptions::FileDialogOptions options); inline void setDirectory(const QString &directory); + inline void updateDirectory() { setDirectory(m_data.directory()); } inline QString directory() const; virtual void exec(HWND owner = 0); inline void setNameFilters(const QStringList &f); inline void selectNameFilter(const QString &filter); + inline void updateSelectedNameFilter() { selectNameFilter(m_data.selectedNameFilter()); } inline QString selectedNameFilter() const; void selectFile(const QString &fileName) const; bool hideFiltersDetails() const { return m_hideFiltersDetails; } @@ -674,14 +805,16 @@ public: void setDefaultSuffix(const QString &s); inline void setLabelText(QFileDialogOptions::DialogLabel l, const QString &text); - virtual QPlatformDialogHelper::DialogCode result() const - { return fileResult(); } - virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const = 0; + // Return the selected files for tracking in OnSelectionChanged(). virtual QStringList selectedFiles() const = 0; + // Return the result for tracking in OnFileOk(). Differs from selection for + // example by appended default suffixes, etc. + virtual QStringList dialogResult() const = 0; inline void onFolderChange(IShellItem *); inline void onSelectionChange(); inline void onTypeChange(); + inline bool onFileOk(); signals: void directoryEntered(const QString& directory); @@ -692,23 +825,28 @@ public slots: virtual void close() { m_fileDialog->Close(S_OK); } protected: - QWindowsNativeFileDialogBase(); + explicit QWindowsNativeFileDialogBase(const QWindowsFileDialogSharedData &data); bool init(const CLSID &clsId, const IID &iid); inline IFileDialog * fileDialog() const { return m_fileDialog; } static QString itemPath(IShellItem *item); static int itemPaths(IShellItemArray *items, QStringList *fileResult = 0); static IShellItem *shellItem(const QString &path); + const QWindowsFileDialogSharedData &data() const { return m_data; } + QWindowsFileDialogSharedData &data() { return m_data; } + private: IFileDialog *m_fileDialog; IFileDialogEvents *m_dialogEvents; DWORD m_cookie; QStringList m_nameFilters; bool m_hideFiltersDetails; + QWindowsFileDialogSharedData m_data; }; -QWindowsNativeFileDialogBase::QWindowsNativeFileDialogBase() : - m_fileDialog(0), m_dialogEvents(0), m_cookie(0), m_hideFiltersDetails(false) +QWindowsNativeFileDialogBase::QWindowsNativeFileDialogBase(const QWindowsFileDialogSharedData &data) : + m_fileDialog(0), m_dialogEvents(0), m_cookie(0), m_hideFiltersDetails(false), + m_data(data) { } @@ -764,15 +902,17 @@ IShellItem *QWindowsNativeFileDialogBase::shellItem(const QString &path) return result; } #endif - qErrnoWarning("%s: SHCreateItemFromParsingName()) failed", __FUNCTION__); + qErrnoWarning("%s: SHCreateItemFromParsingName(%s)) failed", __FUNCTION__, qPrintable(path)); return 0; } void QWindowsNativeFileDialogBase::setDirectory(const QString &directory) { - if (IShellItem *psi = QWindowsNativeFileDialogBase::shellItem(directory)) { - m_fileDialog->SetFolder(psi); - psi->Release(); + if (!directory.isEmpty()) { + if (IShellItem *psi = QWindowsNativeFileDialogBase::shellItem(directory)) { + m_fileDialog->SetFolder(psi); + psi->Release(); + } } } @@ -983,14 +1123,16 @@ static int indexOfNameFilter(const QStringList &filters, const QString &needle) void QWindowsNativeFileDialogBase::selectNameFilter(const QString &filter) { + if (filter.isEmpty()) + return; const int index = indexOfNameFilter(m_nameFilters, filter); - if (index >= 0) { - m_fileDialog->SetFileTypeIndex(index + 1); // one-based. - } else { + if (index < 0) { qWarning("%s: Invalid parameter '%s' not found in '%s'.", __FUNCTION__, qPrintable(filter), qPrintable(m_nameFilters.join(QStringLiteral(", ")))); + return; } + m_fileDialog->SetFileTypeIndex(index + 1); // one-based. } QString QWindowsNativeFileDialogBase::selectedNameFilter() const @@ -1008,6 +1150,7 @@ void QWindowsNativeFileDialogBase::onFolderChange(IShellItem *item) { if (item) { const QString directory = QWindowsNativeFileDialogBase::itemPath(item); + m_data.setDirectory(directory); emit directoryEntered(directory); } } @@ -1015,13 +1158,23 @@ void QWindowsNativeFileDialogBase::onFolderChange(IShellItem *item) void QWindowsNativeFileDialogBase::onSelectionChange() { const QStringList current = selectedFiles(); + m_data.setSelectedFiles(current); if (current.size() == 1) emit currentChanged(current.front()); } void QWindowsNativeFileDialogBase::onTypeChange() { - emit filterSelected(selectedNameFilter()); + const QString filter = selectedNameFilter(); + m_data.setSelectedNameFilter(filter); + emit filterSelected(filter); +} + +bool QWindowsNativeFileDialogBase::onFileOk() +{ + // Store selected files as GetResults() returns invalid data after the dialog closes. + m_data.setSelectedFiles(dialogResult()); + return true; } HRESULT QWindowsNativeFileDialogEventHandler::OnFolderChanging(IFileDialog *, IShellItem *item) @@ -1042,6 +1195,11 @@ HRESULT QWindowsNativeFileDialogEventHandler::OnTypeChange(IFileDialog *) return S_OK; } +HRESULT QWindowsNativeFileDialogEventHandler::OnFileOk(IFileDialog *) +{ + return m_nativeFileDialog->onFileOk() ? S_OK : S_FALSE; +} + /*! \class QWindowsNativeSaveFileDialog \brief Windows native file save dialog wrapper around IFileSaveDialog. @@ -1055,8 +1213,10 @@ HRESULT QWindowsNativeFileDialogEventHandler::OnTypeChange(IFileDialog *) class QWindowsNativeSaveFileDialog : public QWindowsNativeFileDialogBase { public: - virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const; + explicit QWindowsNativeSaveFileDialog(const QWindowsFileDialogSharedData &data) : + QWindowsNativeFileDialogBase(data) {} virtual QStringList selectedFiles() const; + virtual QStringList dialogResult() const; }; // Append a suffix from the name filter "Foo files (*.foo;*.bar)" @@ -1079,17 +1239,13 @@ static inline QString appendSuffix(const QString &fileName, const QString &filte return fileName + QLatin1Char('.') + filter.mid(suffixPos, endPos - suffixPos); } -QPlatformDialogHelper::DialogCode QWindowsNativeSaveFileDialog::fileResult(QStringList *result /* = 0 */) const +QStringList QWindowsNativeSaveFileDialog::dialogResult() const { - if (result) - result->clear(); + QStringList result; IShellItem *item = 0; - const HRESULT hr = fileDialog()->GetResult(&item); - if (FAILED(hr) || !item) - return QPlatformDialogHelper::Rejected; - if (result) - result->push_back(appendSuffix(QWindowsNativeFileDialogBase::itemPath(item), selectedNameFilter())); - return QPlatformDialogHelper::Accepted; + if (SUCCEEDED(fileDialog()->GetResult(&item)) && item) + result.push_back(appendSuffix(QWindowsNativeFileDialogBase::itemPath(item), selectedNameFilter())); + return result; } QStringList QWindowsNativeSaveFileDialog::selectedFiles() const @@ -1115,23 +1271,23 @@ QStringList QWindowsNativeSaveFileDialog::selectedFiles() const class QWindowsNativeOpenFileDialog : public QWindowsNativeFileDialogBase { public: - virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const; + explicit QWindowsNativeOpenFileDialog(const QWindowsFileDialogSharedData &data) : + QWindowsNativeFileDialogBase(data) {} virtual QStringList selectedFiles() const; + virtual QStringList dialogResult() const; private: inline IFileOpenDialog *openFileDialog() const { return static_cast<IFileOpenDialog *>(fileDialog()); } }; -QPlatformDialogHelper::DialogCode QWindowsNativeOpenFileDialog::fileResult(QStringList *result /* = 0 */) const +QStringList QWindowsNativeOpenFileDialog::dialogResult() const { - if (result) - result->clear(); + QStringList result; IShellItemArray *items = 0; - const HRESULT hr = openFileDialog()->GetResults(&items); - if (SUCCEEDED(hr) && items && QWindowsNativeFileDialogBase::itemPaths(items, result) > 0) - return QPlatformDialogHelper::Accepted; - return QPlatformDialogHelper::Rejected; + if (SUCCEEDED(openFileDialog()->GetResults(&items)) && items) + QWindowsNativeFileDialogBase::itemPaths(items, &result); + return result; } QStringList QWindowsNativeOpenFileDialog::selectedFiles() const @@ -1150,17 +1306,18 @@ QStringList QWindowsNativeOpenFileDialog::selectedFiles() const QFileDialog::AcceptMode. */ -QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOptions::AcceptMode am) +QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOptions::AcceptMode am, + const QWindowsFileDialogSharedData &data) { QWindowsNativeFileDialogBase *result = 0; if (am == QFileDialogOptions::AcceptOpen) { - result = new QWindowsNativeOpenFileDialog; + result = new QWindowsNativeOpenFileDialog(data); if (!result->init(CLSID_FileOpenDialog, IID_IFileOpenDialog)) { delete result; return 0; } } else { - result = new QWindowsNativeSaveFileDialog; + result = new QWindowsNativeSaveFileDialog(data); if (!result->init(CLSID_FileSaveDialog, IID_IFileSaveDialog)) { delete result; return 0; @@ -1169,16 +1326,17 @@ QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOp return result; } +static inline bool isQQuickWindow(const QWindow *w = 0) +{ + return w && w->inherits("QQuickWindow"); +} + /*! \class QWindowsFileDialogHelper \brief Helper for native Windows file dialogs - Non-modal dialogs are disabled for now. The functionality is - implemented in principle, however there are failures - when querying the results from a dialog run in another thread. - This could probably be fixed be calling CoInitializeEx() with - the right parameters from each thread. The problem is though - that calls to CoInitialize() occur in several places in Qt. + For Qt 4 compatibility, do not create native non-modal dialogs on widgets, + but only on QQuickWindows, which do not have a fallback. \internal \ingroup qt-lighthouse-win @@ -1188,8 +1346,9 @@ class QWindowsFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDi { public: QWindowsFileDialogHelper() {} - virtual bool supportsNonModalDialog() const { return false; } - + // For Qt 4 compatibility, do not create native non-modal dialogs on widgets, + // but only on QQuickWindows, which do not have a fallback. + virtual bool supportsNonModalDialog(const QWindow *parent = 0) const { return isQQuickWindow(parent); } virtual bool defaultNameFilterDisables() const { return true; } virtual void setDirectory(const QString &directory); @@ -1205,11 +1364,14 @@ private: virtual QWindowsNativeDialogBase *createNativeDialog(); inline QWindowsNativeFileDialogBase *nativeFileDialog() const { return static_cast<QWindowsNativeFileDialogBase *>(nativeDialog()); } + + // Cache for the case no native dialog is created. + QWindowsFileDialogSharedData m_data; }; QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog() { - QWindowsNativeFileDialogBase *result = QWindowsNativeFileDialogBase::create(options()->acceptMode()); + QWindowsNativeFileDialogBase *result = QWindowsNativeFileDialogBase::create(options()->acceptMode(), m_data); if (!result) return 0; QObject::connect(result, SIGNAL(accepted()), this, SIGNAL(accept())); @@ -1223,6 +1385,7 @@ QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog() // Apply settings. const QSharedPointer<QFileDialogOptions> &opts = options(); + m_data.fromOptions(opts); result->setWindowTitle(opts->windowTitle()); result->setMode(opts->fileMode(), opts->options()); result->setHideFiltersDetails(opts->testOption(QFileDialogOptions::HideNameFilterDetails)); @@ -1233,18 +1396,14 @@ QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog() result->setLabelText(QFileDialogOptions::FileName, opts->labelText(QFileDialogOptions::FileName)); if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) result->setLabelText(QFileDialogOptions::Accept, opts->labelText(QFileDialogOptions::Accept)); - const QString initialDirectory = opts->initialDirectory(); - if (!initialDirectory.isEmpty()) - result->setDirectory(initialDirectory); + result->updateDirectory(); + result->updateSelectedNameFilter(); const QStringList initialSelection = opts->initiallySelectedFiles(); if (initialSelection.size() > 0) { QFileInfo info(initialSelection.front()); if (!info.isDir()) result->selectFile(info.fileName()); } - const QString initialNameFilter = opts->initiallySelectedNameFilter(); - if (!initialNameFilter.isEmpty()) - result->selectNameFilter(initialNameFilter); const QString defaultSuffix = opts->defaultSuffix(); if (!defaultSuffix.isEmpty()) result->setDefaultSuffix(defaultSuffix); @@ -1256,15 +1415,14 @@ void QWindowsFileDialogHelper::setDirectory(const QString &directory) if (QWindowsContext::verboseDialogs) qDebug("%s %s" , __FUNCTION__, qPrintable(directory)); - if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - nfd->setDirectory(directory); + m_data.setDirectory(directory); + if (hasNativeDialog()) + nativeFileDialog()->updateDirectory(); } QString QWindowsFileDialogHelper::directory() const { - if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - return nfd->directory(); - return QString(); + return m_data.directory(); } void QWindowsFileDialogHelper::selectFile(const QString &fileName) @@ -1278,13 +1436,7 @@ void QWindowsFileDialogHelper::selectFile(const QString &fileName) QStringList QWindowsFileDialogHelper::selectedFiles() const { - QStringList files; - if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - nfd->fileResult(&files); - if (QWindowsContext::verboseDialogs) - qDebug("%s files='%s'" , __FUNCTION__, - qPrintable(files.join(QStringLiteral(", ")))); - return files; + return m_data.selectedFiles(); } void QWindowsFileDialogHelper::setFilter() @@ -1303,15 +1455,14 @@ void QWindowsFileDialogHelper::setNameFilters(const QStringList &filters) void QWindowsFileDialogHelper::selectNameFilter(const QString &filter) { - if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - nfd->selectNameFilter(filter); + m_data.setSelectedNameFilter(filter); + if (hasNativeDialog()) + nativeFileDialog()->updateSelectedNameFilter(); } QString QWindowsFileDialogHelper::selectedNameFilter() const { - if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - return nfd->selectedNameFilter(); - return QString(); + return m_data.selectedNameFilter(); } #ifndef Q_OS_WINCE @@ -1335,20 +1486,12 @@ class QWindowsXpNativeFileDialog : public QWindowsNativeDialogBase public: typedef QSharedPointer<QFileDialogOptions> OptionsPtr; - static QWindowsXpNativeFileDialog *create(const OptionsPtr &options); + static QWindowsXpNativeFileDialog *create(const OptionsPtr &options, const QWindowsFileDialogSharedData &data); virtual void setWindowTitle(const QString &t) { m_title = t; } virtual void exec(HWND owner = 0); virtual QPlatformDialogHelper::DialogCode result() const { return m_result; } - void setDirectory(const QString &d) { m_directory = d; } - QString directory() const { return m_directory; } - void selectFile(const QString &f) { m_initialFile = f; } - QStringList selectedFiles() const { return m_selectedFiles; } - void setNameFilters(const QStringList &n) { m_nameFilters = n; } - void selectNameFilter(const QString &f); - QString selectedNameFilter() const { return m_selectedNameFilter; } - int existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam); public slots: @@ -1358,19 +1501,15 @@ private: typedef BOOL (APIENTRY *PtrGetOpenFileNameW)(LPOPENFILENAMEW); typedef BOOL (APIENTRY *PtrGetSaveFileNameW)(LPOPENFILENAMEW); - explicit QWindowsXpNativeFileDialog(const OptionsPtr &options); + explicit QWindowsXpNativeFileDialog(const OptionsPtr &options, const QWindowsFileDialogSharedData &data); void populateOpenFileName(OPENFILENAME *ofn, HWND owner) const; QStringList execExistingDir(HWND owner); QStringList execFileNames(HWND owner, int *selectedFilterIndex) const; const OptionsPtr m_options; QString m_title; - QString m_directory; - QString m_initialFile; - QStringList m_selectedFiles; - QString m_selectedNameFilter; - QStringList m_nameFilters; QPlatformDialogHelper::DialogCode m_result; + QWindowsFileDialogSharedData m_data; static PtrGetOpenFileNameW m_getOpenFileNameW; static PtrGetSaveFileNameW m_getSaveFileNameW; @@ -1379,7 +1518,7 @@ private: QWindowsXpNativeFileDialog::PtrGetOpenFileNameW QWindowsXpNativeFileDialog::m_getOpenFileNameW = 0; QWindowsXpNativeFileDialog::PtrGetSaveFileNameW QWindowsXpNativeFileDialog::m_getSaveFileNameW = 0; -QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options) +QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options, const QWindowsFileDialogSharedData &data) { // GetOpenFileNameW() GetSaveFileName() are resolved // dynamically as not to create a dependency on Comdlg32, which @@ -1390,51 +1529,33 @@ QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr m_getSaveFileNameW = (PtrGetSaveFileNameW)(library.resolve("GetSaveFileNameW")); } if (m_getOpenFileNameW && m_getSaveFileNameW) - return new QWindowsXpNativeFileDialog(options); + return new QWindowsXpNativeFileDialog(options, data); return 0; } -QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options) : - m_options(options), m_result(QPlatformDialogHelper::Rejected) +QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options, + const QWindowsFileDialogSharedData &data) : + m_options(options), m_result(QPlatformDialogHelper::Rejected), m_data(data) { - const QStringList nameFilters = m_options->nameFilters(); - if (!nameFilters.isEmpty()) - setNameFilters(nameFilters); - const QString initialDirectory = m_options->initialDirectory(); - if (!initialDirectory.isEmpty()) - setDirectory(initialDirectory); - const QString initialNameFilter = m_options->initiallySelectedNameFilter(); - if (!initialNameFilter.isEmpty()) - selectNameFilter(initialNameFilter); - const QStringList selectedFiles = m_options->initiallySelectedFiles(); - if (!selectedFiles.isEmpty()) - selectFile(selectedFiles.front()); setWindowTitle(m_options->windowTitle()); } -void QWindowsXpNativeFileDialog::selectNameFilter(const QString &f) -{ - const int index = indexOfNameFilter(m_nameFilters, f); - if (index >= 0) - m_selectedNameFilter = m_nameFilters.at(index); -} - void QWindowsXpNativeFileDialog::exec(HWND owner) { int selectedFilterIndex = -1; - m_selectedFiles = m_options->fileMode() == QFileDialogOptions::DirectoryOnly ? + const QStringList selectedFiles = + m_options->fileMode() == QFileDialogOptions::DirectoryOnly ? execExistingDir(owner) : execFileNames(owner, &selectedFilterIndex); + m_data.setSelectedFiles(selectedFiles); QWindowsDialogs::eatMouseMove(); - if (m_selectedFiles.isEmpty()) { + if (selectedFiles.isEmpty()) { m_result = QPlatformDialogHelper::Rejected; emit rejected(); } else { - if (selectedFilterIndex >= 0 && selectedFilterIndex < m_nameFilters.size()) { - m_selectedNameFilter = m_nameFilters.at(selectedFilterIndex); - } else { - m_selectedNameFilter.clear(); - } - m_directory = QFileInfo(m_selectedFiles.front()).absolutePath(); + const QStringList nameFilters = m_options->nameFilters(); + if (selectedFilterIndex >= 0 && selectedFilterIndex < nameFilters.size()) + m_data.setSelectedNameFilter(nameFilters.at(selectedFilterIndex)); + m_data.setDirectory(QFileInfo(selectedFiles.front()).absolutePath()); m_result = QPlatformDialogHelper::Accepted; emit accepted(); } @@ -1458,9 +1579,11 @@ typedef PIDLIST_ABSOLUTE qt_LpItemIdList; int QWindowsXpNativeFileDialog::existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam) { switch (uMsg) { - case BFFM_INITIALIZED: - if (!m_initialFile.isEmpty()) - SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(m_initialFile.utf16())); + case BFFM_INITIALIZED: { + const QString initialFile = m_data.selectedFile(); + if (!initialFile.isEmpty()) + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initialFile.utf16())); + } break; case BFFM_SELCHANGED: { wchar_t path[MAX_PATH]; @@ -1532,7 +1655,7 @@ void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND ow *ptr++ = 0; } *ptr = 0; - const int nameFilterIndex = indexOfNameFilter(m_nameFilters, m_selectedNameFilter); + const int nameFilterIndex = indexOfNameFilter(m_options->nameFilters(), m_data.selectedNameFilter()); if (nameFilterIndex >= 0) ofn->nFilterIndex = nameFilterIndex + 1; // 1..n based. // lpstrFile receives the initial selection and is the buffer @@ -1540,10 +1663,10 @@ void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND ow // will not show. ofn->nMaxFile = 65535; const QString initiallySelectedFile = - QDir::toNativeSeparators(m_initialFile).remove(QLatin1Char('<')). + QDir::toNativeSeparators(m_data.selectedFile()).remove(QLatin1Char('<')). remove(QLatin1Char('>')).remove(QLatin1Char('"')).remove(QLatin1Char('|')); ofn->lpstrFile = qStringToWCharArray(initiallySelectedFile, ofn->nMaxFile); - ofn->lpstrInitialDir = qStringToWCharArray(QDir::toNativeSeparators(m_directory)); + ofn->lpstrInitialDir = qStringToWCharArray(QDir::toNativeSeparators(m_data.directory())); ofn->lpstrTitle = (wchar_t*)m_title.utf16(); // Determine lpstrDefExt. Note that the current MSDN docs document this // member wrong. It should rather be documented as "the default extension @@ -1612,8 +1735,7 @@ class QWindowsXpFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFile { public: QWindowsXpFileDialogHelper() {} - virtual bool supportsNonModalDialog() const { return false; } - + virtual bool supportsNonModalDialog(const QWindow *parent = 0) const { return isQQuickWindow(parent); } virtual bool defaultNameFilterDisables() const { return true; } virtual void setDirectory(const QString &directory); @@ -1629,11 +1751,14 @@ private: virtual QWindowsNativeDialogBase *createNativeDialog(); inline QWindowsXpNativeFileDialog *nativeFileDialog() const { return static_cast<QWindowsXpNativeFileDialog *>(nativeDialog()); } + + QWindowsFileDialogSharedData m_data; }; QWindowsNativeDialogBase *QWindowsXpFileDialogHelper::createNativeDialog() { - if (QWindowsNativeDialogBase *result = QWindowsXpNativeFileDialog::create(options())) { + m_data.fromOptions(options()); + if (QWindowsXpNativeFileDialog *result = QWindowsXpNativeFileDialog::create(options(), m_data)) { QObject::connect(result, SIGNAL(accepted()), this, SIGNAL(accept())); QObject::connect(result, SIGNAL(rejected()), this, SIGNAL(reject())); return result; @@ -1643,47 +1768,37 @@ QWindowsNativeDialogBase *QWindowsXpFileDialogHelper::createNativeDialog() void QWindowsXpFileDialogHelper::setDirectory(const QString &directory) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->setDirectory(directory); + m_data.setDirectory(directory); // Dialog cannot be updated at run-time. } QString QWindowsXpFileDialogHelper::directory() const { - if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - return nfd->directory(); - return QString(); + return m_data.directory(); } void QWindowsXpFileDialogHelper::selectFile(const QString &filename) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->selectFile(filename); + m_data.setSelectedFiles(QStringList(filename)); // Dialog cannot be updated at run-time. } QStringList QWindowsXpFileDialogHelper::selectedFiles() const { - if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - return nfd->selectedFiles(); - return QStringList(); + return m_data.selectedFiles(); } -void QWindowsXpFileDialogHelper::setNameFilters(const QStringList &n) +void QWindowsXpFileDialogHelper::setNameFilters(const QStringList &) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->setNameFilters(n); + // Dialog cannot be updated at run-time. } void QWindowsXpFileDialogHelper::selectNameFilter(const QString &f) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->selectNameFilter(f); + m_data.setSelectedNameFilter(f); // Dialog cannot be updated at run-time. } QString QWindowsXpFileDialogHelper::selectedNameFilter() const { - if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - return nfd->selectedNameFilter(); - return QString(); + return m_data.selectedNameFilter(); } #endif // Q_OS_WINCE diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.h b/src/plugins/platforms/windows/qwindowsdialoghelpers.h index 07f105cb35..7884f398f3 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.h +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.h @@ -65,6 +65,7 @@ template <class BaseClass> class QWindowsDialogHelperBase : public BaseClass { public: + ~QWindowsDialogHelperBase() { deleteNativeDialog(); } virtual void exec(); virtual bool show(Qt::WindowFlags windowFlags, @@ -73,19 +74,24 @@ public: virtual void hide(); virtual QVariant styleHint(QPlatformDialogHelper::StyleHint) const; - virtual bool supportsNonModalDialog() const { return true; } + virtual bool supportsNonModalDialog(const QWindow * /* parent */ = 0) const { return true; } protected: QWindowsDialogHelperBase(); - ~QWindowsDialogHelperBase(); QWindowsNativeDialogBase *nativeDialog() const; + inline bool hasNativeDialog() const { return m_nativeDialog; } + void deleteNativeDialog(); + void timerEvent(QTimerEvent *); private: virtual QWindowsNativeDialogBase *createNativeDialog() = 0; inline QWindowsNativeDialogBase *ensureNativeDialog(); + inline void startDialogThread(); + inline void stopTimer(); QWindowsNativeDialogBase *m_nativeDialog; HWND m_ownerWindow; + int m_timerId; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 1286f5c106..fedda750d9 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -304,10 +304,10 @@ private: class DragCursorHandle { Q_DISABLE_COPY(DragCursorHandle) public: - DragCursorHandle(HCURSOR c, quint64 k) : cursor(c), cacheKey(k) {} + DragCursorHandle(HCURSOR c, qint64 k) : cursor(c), cacheKey(k) {} ~DragCursorHandle() { DestroyCursor(cursor); } HCURSOR cursor; - quint64 cacheKey; + qint64 cacheKey; }; typedef QMap <Qt::DropAction, QSharedPointer<DragCursorHandle> > ActionCursorMap; @@ -490,7 +490,7 @@ QWindowsOleDropSource::GiveFeedback(DWORD dwEffect) qDebug("%s dwEffect=%lu, action=%d", __FUNCTION__, dwEffect, action); QSharedPointer<DragCursorHandle> cursorHandler = m_cursors.value(action); - quint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey(); + qint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey(); if (cursorHandler.isNull() || currentCacheKey != cursorHandler->cacheKey) createCursors(); diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp index b4fb19e8e8..c1c906523f 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp @@ -1160,9 +1160,7 @@ QWindowsFontDatabase::~QWindowsFontDatabase() removeApplicationFonts(); } -QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, - QUnicodeTables::Script script, - void *handle) +QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle) { QFontEngine *fe = QWindowsFontDatabase::createEngine(script, fontDef, 0, QWindowsContext::instance()->defaultDPI(), false, @@ -1215,7 +1213,7 @@ QFontEngine *QWindowsFontDatabase::fontEngine(const QByteArray &fontData, qreal request.styleStrategy = QFont::NoFontMerging | QFont::PreferMatch; request.hintingPreference = hintingPreference; - fontEngine = QWindowsFontDatabase::createEngine(QUnicodeTables::Common, request, 0, + fontEngine = QWindowsFontDatabase::createEngine(QChar::Script_Common, request, 0, QWindowsContext::instance()->defaultDPI(), false, QStringList(), sharedFontData()); @@ -1529,8 +1527,8 @@ HFONT QWindowsFontDatabase::systemFont() static inline bool scriptRequiresOpenType(int script) { - return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala) - || script == QUnicodeTables::Khmer || script == QUnicodeTables::Nko); + return ((script >= QChar::Script_Syriac && script <= QChar::Script_Sinhala) + || script == QChar::Script_Khmer || script == QChar::Script_Nko); } static const char *other_tryFonts[] = { @@ -1715,7 +1713,7 @@ static QStringList extraTryFontsForFamily(const QString& family) return result; } -QStringList QWindowsFontDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const +QStringList QWindowsFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const { QStringList result = QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script); if (!result.isEmpty()) @@ -1917,7 +1915,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(int script, const QFontDef &requ directWriteFont->Release(); #endif - if (script == QUnicodeTables::Common + if (script == QChar::Script_Common && !(request.styleStrategy & QFont::NoFontMerging)) { QStringList extraFonts = extraTryFontsForFamily(request.family); if (extraFonts.size()) { diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.h b/src/plugins/platforms/windows/qwindowsfontdatabase.h index c14d6027c2..b9e6c38eaa 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.h +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.h @@ -78,9 +78,9 @@ public: ~QWindowsFontDatabase(); virtual void populateFontDatabase(); - virtual QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle); + virtual QFontEngine *fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle); virtual QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference); - virtual QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const; + virtual QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const; virtual QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName); virtual void releaseHandle(void *handle); virtual QString fontDir() const; diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp index 406dc636b8..c2ddb912f1 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp @@ -362,7 +362,7 @@ void QWindowsFontDatabaseFT::populate(const QString &family) ReleaseDC(0, dummy); } -QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle) +QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle) { QFontEngine *fe = QBasicFontDatabase::fontEngine(fontDef, script, handle); if (QWindowsContext::verboseFonts) @@ -430,9 +430,9 @@ static const char *kr_tryFonts[] = { static const char **tryFonts = 0; -QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const +QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const { - if(script == QUnicodeTables::Common) { + if (script == QChar::Script_Common) { // && !(request.styleStrategy & QFont::NoFontMerging)) { QFontDatabase db; if (!db.writingSystems(family).contains(QFontDatabase::Symbol)) { @@ -518,8 +518,8 @@ HFONT QWindowsFontDatabaseFT::systemFont() static inline bool scriptRequiresOpenType(int script) { - return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala) - || script == QUnicodeTables::Khmer || script == QUnicodeTables::Nko); + return ((script >= QChar::Script_Syriac && script <= QChar::Script_Sinhala) + || script == QChar::Script_Khmer || script == QChar::Script_Nko); } static inline int verticalDPI() diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h index 2ce429d012..d3cbc0210a 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h @@ -52,10 +52,10 @@ class QWindowsFontDatabaseFT : public QBasicFontDatabase { public: void populateFontDatabase(); - QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle); + QFontEngine *fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle); QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference); - QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const; + QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const; virtual QString fontDir() const; virtual QFont defaultFont() const; diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index 7fcd9814bd..c402f00453 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -66,7 +66,6 @@ #include <QtCore/QThreadStorage> #include <QtCore/private/qsystemlibrary_p.h> -#include <QtCore/private/qunicodetables_p.h> #include <QtCore/QDebug> #include <limits.h> @@ -155,9 +154,20 @@ static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc) return otm; } +bool QWindowsFontEngine::hasCFFTable() const +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + return GetFontData(hdc, MAKE_TAG('C', 'F', 'F', ' '), 0, 0, 0) != GDI_ERROR; +} + void QWindowsFontEngine::getCMap() { ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE); + + // TMPF_TRUETYPE is not set for fonts with CFF tables + cffTable = !ttf && hasCFFTable(); + HDC hdc = m_fontEngineData->hdc; SelectObject(hdc, hfont); bool symb = false; @@ -1041,7 +1051,7 @@ void QWindowsFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gly bool QWindowsFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const { - if (!ttf) + if (!ttf && !cffTable) return false; HDC hdc = m_fontEngineData->hdc; SelectObject(hdc, hfont); @@ -1216,13 +1226,13 @@ QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed, const QTra { HFONT font = hfont; - int contrast; + UINT contrast; SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0); SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) 1000, 0); int margin = glyphMargin(QFontEngineGlyphCache::Raster_RGBMask); QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, margin, t, QImage::Format_RGB32); - SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) contrast, 0); + SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) quintptr(contrast), 0); if (mask == 0) return QImage(); @@ -1258,7 +1268,7 @@ QFontEngine *QWindowsFontEngine::cloneWithSize(qreal pixelSize) const request.styleStrategy |= QFont::NoFontMerging; QFontEngine *fontEngine = - QWindowsFontDatabase::createEngine(QUnicodeTables::Common, request, 0, + QWindowsFontDatabase::createEngine(QChar::Script_Common, request, 0, QWindowsContext::instance()->defaultDPI(), false, QStringList(), m_fontEngineData); diff --git a/src/plugins/platforms/windows/qwindowsfontengine.h b/src/plugins/platforms/windows/qwindowsfontengine.h index a23db2f235..2bf6ead503 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.h +++ b/src/plugins/platforms/windows/qwindowsfontengine.h @@ -145,6 +145,7 @@ public: private: QWindowsNativeImage *drawGDIGlyph(HFONT font, glyph_t, int margin, const QTransform &xform, QImage::Format mask_format); + bool hasCFFTable() const; const QSharedPointer<QWindowsFontEngineData> m_fontEngineData; @@ -155,6 +156,7 @@ private: uint stockFont : 1; uint ttf : 1; uint hasOutline : 1; + uint cffTable : 1; TEXTMETRIC tm; int lw; const unsigned char *cmap; diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp index 183acedfc8..5b6ce695d8 100644 --- a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp @@ -376,10 +376,12 @@ void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEn 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; } + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + for (int i = 0; i < glyphs->numGlyphs; ++i) + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + } } else { qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__); } diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index da4519199f..30e0478e64 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -77,6 +77,7 @@ #include <QtCore/private/qeventdispatcher_win_p.h> #include <QtCore/QDebug> +#include <QtCore/QVariant> QT_BEGIN_NAMESPACE @@ -114,6 +115,11 @@ public: bool asyncExpose() const; void setAsyncExpose(bool value); + + QVariantMap windowProperties(QPlatformWindow *window) const; + QVariant windowProperty(QPlatformWindow *window, const QString &name) const; + QVariant windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const; + void setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value); }; void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resource, QWindow *window) @@ -150,6 +156,37 @@ void *QWindowsNativeInterface::nativeResourceForBackingStore(const QByteArray &r return 0; } +static const char customMarginPropertyC[] = "WindowsCustomMargins"; + +QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name) const +{ + QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window); + if (name == QLatin1String(customMarginPropertyC)) + return qVariantFromValue(platformWindow->customMargins()); + return QVariant(); +} + +QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const +{ + const QVariant result = windowProperty(window, name); + return result.isValid() ? result : defaultValue; +} + +void QWindowsNativeInterface::setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value) +{ + QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window); + if (name == QLatin1String(customMarginPropertyC)) + platformWindow->setCustomMargins(qvariant_cast<QMargins>(value)); +} + +QVariantMap QWindowsNativeInterface::windowProperties(QPlatformWindow *window) const +{ + QVariantMap result; + const QString customMarginProperty = QLatin1String(customMarginPropertyC); + result.insert(customMarginProperty, windowProperty(window, customMarginProperty)); + return result; +} + #ifndef QT_NO_OPENGL void *QWindowsNativeInterface::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) { @@ -372,6 +409,11 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons QWindowsWindow::WindowData requested; requested.flags = window->flags(); requested.geometry = window->geometry(); + // Apply custom margins (see QWindowsWindow::setCustomMargins())). + const QVariant customMarginsV = window->property("_q_windowsCustomMargins"); + if (customMarginsV.isValid()) + requested.customMargins = qvariant_cast<QMargins>(customMarginsV); + const QWindowsWindow::WindowData obtained = QWindowsWindow::WindowData::create(window, requested, window->title()); if (QWindowsContext::verboseIntegration || QWindowsContext::verboseWindows) diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 56deb27442..924d604641 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -856,9 +856,15 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms if (isNumpad && (nModifiers & AltAny)) { code = winceKeyBend(msg.wParam); } else if (!isDeadKey) { - unsigned char kbdBuffer[256]; // Will hold the complete keyboard state - GetKeyboardState(kbdBuffer); - code = toKeyOrUnicode(msg.wParam, scancode, kbdBuffer); + // QTBUG-8764, QTBUG-10032 + // Can't call toKeyOrUnicode because that would call ToUnicode, and, if a dead key + // is pressed at the moment, Windows would NOT use it to compose a character for the next + // WM_CHAR event. + + // Instead, use MapVirtualKey, which will provide adequate values. + code = MapVirtualKey(msg.wParam, MAPVK_VK_TO_CHAR); + if (code < 0x20 || code == 0x7f) // The same logic as in toKeyOrUnicode() + code = winceKeyBend(msg.wParam); } // Invert state logic: @@ -1120,7 +1126,7 @@ QList<int> QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const } result << int(baseKey + keyMods); // The base key is _always_ valid, of course - for (int i = 1; i < NumMods; ++i) { + for (size_t i = 1; i < NumMods; ++i) { Qt::KeyboardModifiers neededMods = ModsTbl[i]; quint32 key = kbItem.qtKey[i]; if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp index 7fb32d3513..a8bacd631d 100644 --- a/src/plugins/platforms/windows/qwindowsmime.cpp +++ b/src/plugins/platforms/windows/qwindowsmime.cpp @@ -703,14 +703,14 @@ QWindowsMimeURI::QWindowsMimeURI() bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const { - if (getCf(formatetc) == CF_HDROP) { + if (mimeData->hasUrls() && getCf(formatetc) == CF_HDROP) { QList<QUrl> urls = mimeData->urls(); for (int i=0; i<urls.size(); i++) { if (!urls.at(i).toLocalFile().isEmpty()) return true; } } - return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasFormat(QStringLiteral("text/uri-list")); + return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasUrls(); } bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index e1f4f4b143..dd16ea1c6f 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -49,6 +49,7 @@ #include <qpa/qwindowsysteminterface.h> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> +#include <QtGui/QWindow> #include <QtCore/QDebug> #include <QtCore/QScopedArrayPointer> @@ -236,6 +237,9 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, platformWindow->setFlag(QWindowsWindow::AutoMouseCapture); if (QWindowsContext::verboseEvents) qDebug() << "Automatic mouse capture " << window; + // Implement "Click to focus" for native child windows. + if (!window->isTopLevel() && QGuiApplication::focusWindow() != window) + window->requestActivate(); } else if (platformWindow->hasMouseCapture() && platformWindow->testFlag(QWindowsWindow::AutoMouseCapture) && (msg.message == WM_LBUTTONUP || msg.message == WM_MBUTTONUP diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp index bcae2e0816..250fea56b1 100644 --- a/src/plugins/platforms/windows/qwindowsservices.cpp +++ b/src/plugins/platforms/windows/qwindowsservices.cpp @@ -44,6 +44,7 @@ #include <QtCore/QUrl> #include <QtCore/QDebug> +#include <QtCore/QDir> #include <shlobj.h> #ifndef Q_OS_WINCE @@ -57,7 +58,8 @@ enum { debug = 0 }; static inline bool shellExecute(const QString &file) { #ifndef Q_OS_WINCE - const quintptr result = (quintptr)ShellExecute(0, 0, (wchar_t*)file.utf16(), 0, 0, SW_SHOWNORMAL); + const QString nativeFilePath = QDir::toNativeSeparators(file); + const quintptr result = (quintptr)ShellExecute(0, 0, (wchar_t*)nativeFilePath.utf16(), 0, 0, SW_SHOWNORMAL); // ShellExecute returns a value greater than 32 if successful if (result <= 32) { qWarning("ShellExecute '%s' failed (error %s).", qPrintable(file), qPrintable(QString::number(result))); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 07dc668b3f..d565420f4f 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -296,7 +296,7 @@ struct WindowCreationData tool(false), embedded(false), hasAlpha(false) {} void fromWindow(const QWindow *w, const Qt::WindowFlags flags, unsigned creationFlags = 0); - inline WindowData create(const QWindow *w, const QRect &geometry, QString title) const; + inline WindowData create(const QWindow *w, const WindowData &data, QString title) const; inline void applyWindowFlags(HWND hwnd) const; void initialize(HWND h, bool frameChange, qreal opacityLevel) const; @@ -448,7 +448,7 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag } QWindowsWindow::WindowData - WindowCreationData::create(const QWindow *w, const QRect &geometry, QString title) const + WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const { typedef QSharedPointer<QWindowCreationContext> QWindowCreationContextPtr; @@ -474,24 +474,25 @@ QWindowsWindow::WindowData const wchar_t *titleUtf16 = reinterpret_cast<const wchar_t *>(title.utf16()); const wchar_t *classNameUtf16 = reinterpret_cast<const wchar_t *>(windowClassName.utf16()); - // Capture events before CreateWindowEx() returns. - const QWindowCreationContextPtr context(new QWindowCreationContext(w, geometry, style, exStyle)); + // Capture events before CreateWindowEx() returns. The context is cleared in + // the QWindowsWindow constructor. + const QWindowCreationContextPtr context(new QWindowCreationContext(w, data.geometry, data.customMargins, style, exStyle)); QWindowsContext::instance()->setWindowCreationContext(context); if (QWindowsContext::verboseWindows) qDebug().nospace() << "CreateWindowEx: " << w << *this << " class=" <<windowClassName << " title=" << title - << "\nrequested: " << geometry << ": " + << "\nrequested: " << data.geometry << ": " << context->frameWidth << 'x' << context->frameHeight - << '+' << context->frameX << '+' << context->frameY; + << '+' << context->frameX << '+' << context->frameY + << " custom margins: " << context->customMargins; result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16, style, context->frameX, context->frameY, context->frameWidth, context->frameHeight, parentHandle, NULL, appinst, NULL); - QWindowsContext::instance()->setWindowCreationContext(QWindowCreationContextPtr()); if (QWindowsContext::verboseWindows) qDebug().nospace() << "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: " @@ -505,6 +506,7 @@ QWindowsWindow::WindowData result.geometry = context->obtainedGeometry; result.frame = context->margins; result.embedded = embedded; + result.customMargins = context->customMargins; return result; } @@ -543,6 +545,8 @@ void WindowCreationData::initialize(HWND hwnd, bool frameChange, qreal opacityLe qWarning() << "QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time"; } else if (flags & Qt::WindowStaysOnBottomHint) { SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, swpFlags); + } else if (frameChange) { // Force WM_NCCALCSIZE with wParam=1 in case of custom margins. + SetWindowPos(hwnd, 0, 0, 0, 0, 0, swpFlags); } if (flags & (Qt::CustomizeWindowHint|Qt::WindowTitleHint)) { HMENU systemMenu = GetSystemMenu(hwnd, FALSE); @@ -603,6 +607,33 @@ QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle) return result; } +bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result) +{ +#ifndef Q_OS_WINCE + // NCCALCSIZE_PARAMS structure if wParam==TRUE + if (!msg.wParam || customMargins.isNull()) + return false; + *result = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + NCCALCSIZE_PARAMS *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg.lParam); + const RECT oldClientArea = ncp->rgrc[0]; + ncp->rgrc[0].left += customMargins.left(); + ncp->rgrc[0].top += customMargins.top(); + ncp->rgrc[0].right -= customMargins.right(); + ncp->rgrc[0].bottom -= customMargins.bottom(); + result = 0; + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->" + << ncp->rgrc[0] << ' ' << ncp->rgrc[1] << ' ' << ncp->rgrc[2] + << ' ' << ncp->lppos->cx << ',' << ncp->lppos->cy; + return true; +#else + Q_UNUSED(customMargins) + Q_UNUSED(msg) + Q_UNUSED(result) + return false; +#endif +} + #ifndef Q_OS_WINCE void QWindowsGeometryHint::applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const { @@ -669,10 +700,11 @@ bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w) QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QRect &geometry, + const QMargins &cm, DWORD style_, DWORD exStyle_) : geometryHint(w), style(style_), exStyle(exStyle_), requestedGeometry(geometry), obtainedGeometry(geometry), - margins(QWindowsGeometryHint::frame(style, exStyle)), + margins(QWindowsGeometryHint::frame(style, exStyle)), customMargins(cm), frameX(CW_USEDEFAULT), frameY(CW_USEDEFAULT), frameWidth(CW_USEDEFAULT), frameHeight(CW_USEDEFAULT) { @@ -683,14 +715,16 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, if (geometry.isValid()) { frameX = geometry.x(); frameY = geometry.y(); - frameWidth = margins.left() + geometry.width() + margins.right(); - frameHeight = margins.top() + geometry.height() + margins.bottom(); + const QMargins effectiveMargins = margins + customMargins; + frameWidth = effectiveMargins.left() + geometry.width() + effectiveMargins.right(); + frameHeight = effectiveMargins.top() + geometry.height() + effectiveMargins.bottom(); const bool isDefaultPosition = !frameX && !frameY && w->isTopLevel(); if (!QWindowsGeometryHint::positionIncludesFrame(w) && !isDefaultPosition) { - frameX -= margins.left(); - frameY -= margins.top(); + frameX -= effectiveMargins.left(); + frameY -= effectiveMargins.top(); } } + if (QWindowsContext::verboseWindows) qDebug().nospace() << __FUNCTION__ << ' ' << w << geometry @@ -698,7 +732,8 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, << " frame: " << frameWidth << 'x' << frameHeight << '+' << frameX << '+' << frameY << " min" << geometryHint.minimumSize - << " max" << geometryHint.maximumSize; + << " max" << geometryHint.maximumSize + << " custom margins " << customMargins; } /*! @@ -730,9 +765,6 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : m_hdc(0), m_windowState(Qt::WindowNoState), m_opacity(1.0), -#ifndef QT_NO_CURSOR - m_cursor(QWindowsScreen::screenOf(aWindow)->windowsCursor()->standardWindowCursor()), -#endif m_dropTarget(0), m_savedStyle(0), m_format(aWindow->format()), @@ -747,6 +779,8 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : { if (aWindow->surfaceType() == QWindow::OpenGLSurface) setFlag(OpenGLSurface); + // Clear the creation context as the window can be found in QWindowsContext's map. + QWindowsContext::instance()->setWindowCreationContext(QSharedPointer<QWindowCreationContext>()); QWindowsContext::instance()->addWindow(m_data.hwnd, this); if (aWindow->isTopLevel()) { switch (aWindow->type()) { @@ -872,8 +906,9 @@ QWindowsWindow::WindowData { WindowCreationData creationData; creationData.fromWindow(w, parameters.flags); - WindowData result = creationData.create(w, parameters.geometry, title); - creationData.initialize(result.hwnd, false, 1); + WindowData result = creationData.create(w, parameters, title); + // Force WM_NCCALCSIZE (with wParam=1) via SWP_FRAMECHANGED for custom margin. + creationData.initialize(result.hwnd, !parameters.customMargins.isNull(), 1); return result; } @@ -1163,7 +1198,7 @@ QRect QWindowsWindow::frameGeometry_sys() const QRect QWindowsWindow::geometry_sys() const { - return frameGeometry_sys() - frameMargins(); + return frameGeometry_sys().marginsRemoved(frameMargins()); } /*! @@ -1490,7 +1525,7 @@ QMargins QWindowsWindow::frameMargins() const m_data.frame = QWindowsGeometryHint::frame(style(), exStyle()); clearFlag(FrameDirty); } - return m_data.frame; + return m_data.frame + m_data.customMargins; } void QWindowsWindow::setOpacity(qreal level) @@ -1664,20 +1699,42 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const void QWindowsWindow::applyCursor() { #ifndef QT_NO_CURSOR - SetCursor(m_cursor.handle()); + if (m_cursor.isNull()) { // Recurse up to parent with non-null cursor. + if (const QWindow *p = window()->parent()) + QWindowsWindow::baseWindowOf(p)->applyCursor(); + } else { + SetCursor(m_cursor.handle()); + } #endif } +// Check whether to apply a new cursor. Either the window in question is +// currently under mouse, or it is the parent of the window under mouse and +// there is no other window with an explicitly set cursor in-between. +static inline bool applyNewCursor(const QWindow *w) +{ + const QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse(); + if (underMouse == w) + return true; + for (const QWindow *p = underMouse; p ; p = p->parent()) { + if (p == w) + return true; + if (!QWindowsWindow::baseWindowOf(p)->cursor().isNull()) + return false; + } + return false; +} + void QWindowsWindow::setCursor(const QWindowsWindowCursor &c) { #ifndef QT_NO_CURSOR if (c.handle() != m_cursor.handle()) { - const bool underMouse = QWindowsContext::instance()->windowUnderMouse() == window(); + const bool apply = applyNewCursor(window()); if (QWindowsContext::verboseWindows) qDebug() << window() << __FUNCTION__ << "Shape=" << c.cursor().shape() - << " isWUM=" << underMouse; + << " doApply=" << apply; m_cursor = c; - if (underMouse) + if (apply) applyCursor(); } #endif @@ -1850,4 +1907,32 @@ void QWindowsWindow::setWindowIcon(const QIcon &icon) } } +/*! + \brief Sets custom margins to be added to the default margins determined by + the windows style in the handling of the WM_NCCALCSIZE message. + + This is currently used to give the Aero-style QWizard a smaller top margin. + The property can be set using QPlatformNativeInterface::setWindowProperty() or, + before platform window creation, by setting a dynamic property + on the QWindow (see QWindowsIntegration::createPlatformWindow()). +*/ + +void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) +{ + if (newCustomMargins != m_data.customMargins) { + const QMargins oldCustomMargins = m_data.customMargins; + m_data.customMargins = newCustomMargins; + // Re-trigger WM_NCALCSIZE with wParam=1 by passing SWP_FRAMECHANGED + const QRect currentFrameGeometry = frameGeometry_sys(); + const QPoint topLeft = currentFrameGeometry.topLeft(); + QRect newFrame = currentFrameGeometry.marginsRemoved(oldCustomMargins) + m_data.customMargins; + newFrame.moveTo(topLeft); + setFlag(FrameDirty); + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins + << currentFrameGeometry << "->" << newFrame; + SetWindowPos(m_data.hwnd, 0, newFrame.x(), newFrame.y(), newFrame.width(), newFrame.height(), SWP_NOZORDER | SWP_FRAMECHANGED); + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index f3b480b0af..8fe5cbe17b 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -69,6 +69,7 @@ struct QWindowsGeometryHint QWindowsGeometryHint() {} explicit QWindowsGeometryHint(const QWindow *w); static QMargins frame(DWORD style, DWORD exStyle); + static bool handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result); #ifndef Q_OS_WINCE //MinMax maybe define struct if not available void applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const; void applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const; @@ -89,6 +90,7 @@ struct QWindowsGeometryHint struct QWindowCreationContext { QWindowCreationContext(const QWindow *w, const QRect &r, + const QMargins &customMargins, DWORD style, DWORD exStyle); #ifndef Q_OS_WINCE //MinMax maybe define struct if not available void applyToMinMaxInfo(MINMAXINFO *mmi) const @@ -101,6 +103,7 @@ struct QWindowCreationContext QRect requestedGeometry; QRect obtainedGeometry; QMargins margins; + QMargins customMargins; // User-defined, additional frame for WM_NCCALCSIZE int frameX; // Passed on to CreateWindowEx(), including frame. int frameY; int frameWidth; @@ -137,6 +140,7 @@ public: Qt::WindowFlags flags; QRect geometry; QMargins frame; // Do not use directly for windows, see FrameDirty. + QMargins customMargins; // User-defined, additional frame for NCCALCSIZE HWND hwnd; bool embedded; @@ -191,6 +195,9 @@ public: void setFrameStrutEventsEnabled(bool enabled); bool frameStrutEventsEnabled() const { return testFlag(FrameStrutEventsEnabled); } + QMargins customMargins() const { return m_data.customMargins; } + void setCustomMargins(const QMargins &m); + #ifdef QT_OPENGL_ES_2 EGLSurface eglSurfaceHandle() const { return m_eglSurface;} EGLSurface ensureEglSurfaceHandle(const QWindowsEGLStaticContextPtr &staticContext, EGLConfig config); @@ -290,17 +297,6 @@ private: HICON m_iconBig; }; -// Conveniences for window frames. -inline QRect operator+(const QRect &r, const QMargins &m) -{ - return r.adjusted(-m.left(), -m.top(), m.right(), m.bottom()); -} - -inline QRect operator-(const QRect &r, const QMargins &m) -{ - return r.adjusted(m.left(), m.top(), -m.right(), -m.bottom()); -} - // Debug QDebug operator<<(QDebug d, const RECT &r); #ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_GETMINMAXINFO/WM_NCCALCSIZE @@ -371,4 +367,6 @@ inline void QWindowsWindow::destroyIcon() QT_END_NAMESPACE +Q_DECLARE_METATYPE(QMargins) + #endif // QWINDOWSWINDOW_H diff --git a/src/plugins/platforms/xcb/qglxintegration.cpp b/src/plugins/platforms/xcb/qglxintegration.cpp index 11c74edc45..854f7bcd17 100644 --- a/src/plugins/platforms/xcb/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/qglxintegration.cpp @@ -50,6 +50,7 @@ #include <GL/glx.h> #include <QtGui/QOpenGLContext> +#include <QtGui/QOffscreenSurface> #include "qglxintegration.h" #include <QtPlatformSupport/private/qglxconvenience_p.h> @@ -270,6 +271,7 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat , m_screen(screen) , m_context(0) , m_format(format) + , m_isPBufferCurrent(false) { m_shareContext = 0; if (share) @@ -391,19 +393,35 @@ bool QGLXContext::makeCurrent(QPlatformSurface *surface) { Q_ASSERT(surface->surface()->surfaceType() == QSurface::OpenGLSurface); - GLXDrawable glxDrawable = static_cast<QXcbWindow *>(surface)->xcb_window(); - - return glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), glxDrawable, m_context); + QSurface::SurfaceClass surfaceClass = surface->surface()->surfaceClass(); + if (surfaceClass == QSurface::Window) { + m_isPBufferCurrent = false; + QXcbWindow *window = static_cast<QXcbWindow *>(surface); + return glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), window->xcb_window(), m_context); + } else if (surfaceClass == QSurface::Offscreen) { + m_isPBufferCurrent = true; + QGLXPbuffer *pbuffer = static_cast<QGLXPbuffer *>(surface); + return glXMakeContextCurrent(DISPLAY_FROM_XCB(m_screen), pbuffer->pbuffer(), pbuffer->pbuffer(), m_context); + } + return false; } void QGLXContext::doneCurrent() { - glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), 0, 0); + if (m_isPBufferCurrent) + glXMakeContextCurrent(DISPLAY_FROM_XCB(m_screen), 0, 0, 0); + else + glXMakeCurrent(DISPLAY_FROM_XCB(m_screen), 0, 0); + m_isPBufferCurrent = false; } void QGLXContext::swapBuffers(QPlatformSurface *surface) { - GLXDrawable glxDrawable = static_cast<QXcbWindow *>(surface)->xcb_window(); + GLXDrawable glxDrawable = 0; + if (surface->surface()->surfaceClass() == QSurface::Offscreen) + glxDrawable = static_cast<QGLXPbuffer *>(surface)->pbuffer(); + else + glxDrawable = static_cast<QXcbWindow *>(surface)->xcb_window(); glXSwapBuffers(DISPLAY_FROM_XCB(m_screen), glxDrawable); } @@ -455,4 +473,36 @@ bool QGLXContext::isValid() const return m_context != 0; } + +QGLXPbuffer::QGLXPbuffer(QOffscreenSurface *offscreenSurface) + : QPlatformOffscreenSurface(offscreenSurface) + , m_format(offscreenSurface->requestedFormat()) + , m_screen(static_cast<QXcbScreen *>(offscreenSurface->screen()->handle())) + , m_pbuffer(0) +{ + GLXFBConfig config = qglx_findConfig(DISPLAY_FROM_XCB(m_screen), m_screen->screenNumber(), m_format); + + if (config) { + const int attributes[] = { + GLX_PBUFFER_WIDTH, offscreenSurface->size().width(), + GLX_PBUFFER_HEIGHT, offscreenSurface->size().height(), + GLX_LARGEST_PBUFFER, False, + GLX_PRESERVED_CONTENTS, False, + GLX_NONE + }; + + m_pbuffer = glXCreatePbuffer(DISPLAY_FROM_XCB(m_screen), config, attributes); + + if (m_pbuffer) + m_format = qglx_surfaceFormatFromGLXFBConfig(DISPLAY_FROM_XCB(m_screen), config); + } +} + +QGLXPbuffer::~QGLXPbuffer() +{ + if (m_pbuffer) + glXDestroyPbuffer(DISPLAY_FROM_XCB(m_screen), m_pbuffer); +} + + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qglxintegration.h b/src/plugins/platforms/xcb/qglxintegration.h index 78e9985334..d10b1d441b 100644 --- a/src/plugins/platforms/xcb/qglxintegration.h +++ b/src/plugins/platforms/xcb/qglxintegration.h @@ -46,6 +46,7 @@ #include "qxcbscreen.h" #include <qpa/qplatformopenglcontext.h> +#include <qpa/qplatformoffscreensurface.h> #include <QtGui/QSurfaceFormat> #include <QtCore/QMutex> @@ -89,6 +90,25 @@ private: GLXContext m_context; GLXContext m_shareContext; QSurfaceFormat m_format; + bool m_isPBufferCurrent; +}; + + +class QGLXPbuffer : public QPlatformOffscreenSurface +{ +public: + explicit QGLXPbuffer(QOffscreenSurface *offscreenSurface); + ~QGLXPbuffer(); + + QSurfaceFormat format() const { return m_format; } + bool isValid() const { return m_pbuffer != 0; } + + GLXPbuffer pbuffer() const { return m_pbuffer; } + +private: + QSurfaceFormat m_format; + QXcbScreen *m_screen; + GLXPbuffer m_pbuffer; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index e29b21c9c8..1b11ef7f95 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -287,8 +287,14 @@ QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) QXcbCursor::~QXcbCursor() { + xcb_connection_t *conn = xcb_connection(); if (!--cursorCount) - xcb_close_font(xcb_connection(), cursorFont); + xcb_close_font(conn, cursorFont); + + foreach (xcb_cursor_t cursor, m_bitmapCursorMap) + xcb_free_cursor(conn, cursor); + foreach (xcb_cursor_t cursor, m_shapeCursorMap) + xcb_free_cursor(conn, cursor); } #ifndef QT_NO_CURSOR @@ -301,17 +307,19 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *widget) // No X11 cursor control when there is no widget under the cursor return; - xcb_cursor_t c; - if (cursor->shape() == Qt::BitmapCursor) { - qint64 id = cursor->pixmap().cacheKey(); - if (!m_bitmapCursorMap.contains(id)) - m_bitmapCursorMap.insert(id, createBitmapCursor(cursor)); - c = m_bitmapCursorMap.value(id); - } else { - int id = cursor->shape(); - if (!m_shapeCursorMap.contains(id)) - m_shapeCursorMap.insert(id, createFontCursor(cursor->shape())); - c = m_shapeCursorMap.value(id); + xcb_cursor_t c = XCB_CURSOR_NONE; + if (cursor) { + if (cursor->shape() == Qt::BitmapCursor) { + qint64 id = cursor->pixmap().cacheKey(); + if (!m_bitmapCursorMap.contains(id)) + m_bitmapCursorMap.insert(id, createBitmapCursor(cursor)); + c = m_bitmapCursorMap.value(id); + } else { + int id = cursor->shape(); + if (!m_shapeCursorMap.contains(id)) + m_shapeCursorMap.insert(id, createFontCursor(cursor->shape())); + c = m_shapeCursorMap.value(id); + } } w->setCursor(c); diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index 1840dd1ce5..d0b0ab8d02 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -78,10 +78,12 @@ #elif defined(XCB_USE_EGL) #include "qxcbeglsurface.h" #include <QtPlatformSupport/private/qeglplatformcontext_p.h> +#include <QtPlatformSupport/private/qeglpbuffer_p.h> #endif #include <QtGui/QOpenGLContext> #include <QtGui/QScreen> +#include <QtGui/QOffscreenSurface> #ifndef QT_NO_ACCESSIBILITY #include <qpa/qplatformaccessibility.h> #ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE @@ -168,7 +170,10 @@ public: EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) { - return static_cast<QXcbWindow *>(surface)->eglSurface()->surface(); + if (surface->surface()->surfaceClass() == QSurface::Window) + return static_cast<QXcbWindow *>(surface)->eglSurface()->surface(); + else + return static_cast<QEGLPbuffer *>(surface)->pbuffer(); } private: @@ -205,6 +210,20 @@ QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *wind return new QXcbBackingStore(window); } +QPlatformOffscreenSurface *QXcbIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const +{ +#if defined(XCB_USE_GLX) + return new QGLXPbuffer(surface); +#elif defined(XCB_USE_EGL) + QXcbScreen *screen = static_cast<QXcbScreen *>(surface->screen()->handle()); + return new QEGLPbuffer(screen->connection()->egl_display(), surface->requestedFormat(), surface); +#else + Q_UNUSED(surface); + qWarning("QXcbIntegration: Cannot create platform offscreen surface, neither GLX nor EGL are enabled"); + return 0; +#endif +} + bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const { switch (cap) { diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h index cd6c2fd73c..13b3b115ca 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -68,6 +68,8 @@ public: #endif QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; + QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const; + bool hasCapability(Capability cap) const; QAbstractEventDispatcher *guiThreadEventDispatcher() const; diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index bda54b4682..4ac60f6077 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -1081,6 +1081,7 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod int qtcode = 0; int count = chars.count(); QString string = translateKeySym(sym, state, qtcode, modifiers, chars, count); + string.truncate(count); bool isAutoRepeat = false; @@ -1102,7 +1103,7 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod bool filtered = false; if (inputContext) { - QKeyEvent event(type, qtcode, modifiers, code, sym, state, string.left(count), isAutoRepeat, count); + QKeyEvent event(type, qtcode, modifiers, code, sym, state, string, isAutoRepeat, count); event.setTimestamp(time); filtered = inputContext->filterEvent(&event); } @@ -1114,7 +1115,7 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod QWindowSystemInterface::handleContextMenuEvent(window, false, pos, globalPos, modifiers); } QWindowSystemInterface::handleExtendedKeyEvent(window, time, type, qtcode, modifiers, - code, sym, state, string.left(count), isAutoRepeat); + code, sym, state, string, isAutoRepeat); } if (isAutoRepeat && type == QEvent::KeyRelease) { @@ -1130,13 +1131,13 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod } if (!filtered && inputContext) { - QKeyEvent event(QEvent::KeyPress, qtcode, modifiers, code, sym, state, string.left(count), isAutoRepeat, count); + QKeyEvent event(QEvent::KeyPress, qtcode, modifiers, code, sym, state, string, isAutoRepeat, count); event.setTimestamp(time); filtered = inputContext->filterEvent(&event); } if (!filtered) QWindowSystemInterface::handleExtendedKeyEvent(window, time, QEvent::KeyPress, qtcode, modifiers, - code, sym, state, string.left(count), isAutoRepeat); + code, sym, state, string, isAutoRepeat); } } diff --git a/src/plugins/platformthemes/gtk2/gtk2.json b/src/plugins/platformthemes/gtk2/gtk2.json new file mode 100644 index 0000000000..86dd8e58fa --- /dev/null +++ b/src/plugins/platformthemes/gtk2/gtk2.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "gtk2" ] +} diff --git a/src/plugins/platformthemes/gtk2/gtk2.pro b/src/plugins/platformthemes/gtk2/gtk2.pro new file mode 100644 index 0000000000..bb02192f91 --- /dev/null +++ b/src/plugins/platformthemes/gtk2/gtk2.pro @@ -0,0 +1,20 @@ +TARGET = qgtk2 + +PLUGIN_TYPE = platformthemes +PLUGIN_CLASS_NAME = QGtk2ThemePlugin +load(qt_plugin) + +QT += core-private gui-private platformsupport-private + +CONFIG += X11 +QMAKE_CXXFLAGS += $$QT_CFLAGS_QGTK2 +LIBS += $$QT_LIBS_QGTK2 + +HEADERS += \ + qgtk2dialoghelpers.h \ + qgtk2theme.h + +SOURCES += \ + main.cpp \ + qgtk2dialoghelpers.cpp \ + qgtk2theme.cpp \ diff --git a/src/plugins/platformthemes/gtk2/main.cpp b/src/plugins/platformthemes/gtk2/main.cpp new file mode 100644 index 0000000000..0c3fe46e29 --- /dev/null +++ b/src/plugins/platformthemes/gtk2/main.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qpa/qplatformthemeplugin.h> +#include "qgtk2theme.h" + +QT_BEGIN_NAMESPACE + +class QGtk2ThemePlugin : public QPlatformThemePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformThemeFactoryInterface.5.1" FILE "gtk2.json") + +public: + QPlatformTheme *create(const QString &key, const QStringList ¶ms); +}; + +QPlatformTheme *QGtk2ThemePlugin::create(const QString &key, const QStringList ¶ms) +{ + Q_UNUSED(params); + if (!key.compare(QStringLiteral("gtk2"), Qt::CaseInsensitive)) + return new QGtk2Theme; + + return 0; +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp b/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp new file mode 100644 index 0000000000..2a8815654f --- /dev/null +++ b/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp @@ -0,0 +1,609 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgtk2dialoghelpers.h" + +#include <qeventloop.h> +#include <qwindow.h> +#include <qcolor.h> +#include <qdebug.h> +#include <qfont.h> + +#include <private/qguiapplication_p.h> + +#undef signals +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <pango/pango.h> + +QT_BEGIN_NAMESPACE + +class QGtk2Dialog : public QWindow +{ + Q_OBJECT + +public: + QGtk2Dialog(GtkWidget *gtkWidget); + ~QGtk2Dialog(); + + GtkDialog* gtkDialog() const; + + void exec(); + bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent); + void hide(); + +Q_SIGNALS: + void accept(); + void reject(); + +protected: + static void onResponse(QGtk2Dialog *dialog, int response); + +private: + GtkWidget *gtkWidget; +}; + +QGtk2Dialog::QGtk2Dialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) +{ + g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), this); + g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); +} + +QGtk2Dialog::~QGtk2Dialog() +{ + gtk_widget_destroy(gtkWidget); +} + +GtkDialog* QGtk2Dialog::gtkDialog() const +{ + return GTK_DIALOG(gtkWidget); +} + +void QGtk2Dialog::exec() +{ + if (modality() == Qt::ApplicationModal) { + // block input to the whole app, including other GTK dialogs + gtk_dialog_run(gtkDialog()); + } else { + // block input to the window, allow input to other GTK dialogs + QEventLoop loop; + connect(this, SIGNAL(accept()), &loop, SLOT(quit())); + connect(this, SIGNAL(reject()), &loop, SLOT(quit())); + loop.exec(); + } +} + +bool QGtk2Dialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +{ + setParent(parent); + setFlags(flags); + setModality(modality); + + gtk_widget_realize(gtkWidget); // creates X window + + if (parent) { + XSetTransientForHint(gdk_x11_drawable_get_xdisplay(gtkWidget->window), + gdk_x11_drawable_get_xid(gtkWidget->window), + parent->winId()); + } + + if (modality != Qt::NonModal) { + gdk_window_set_modal_hint(gtkWidget->window, true); + QGuiApplicationPrivate::showModalWindow(this); + } + + gtk_widget_show(gtkWidget); + return true; +} + +void QGtk2Dialog::hide() +{ + QGuiApplicationPrivate::hideModalWindow(this); + gtk_widget_hide(gtkWidget); +} + +void QGtk2Dialog::onResponse(QGtk2Dialog *dialog, int response) +{ + if (response == GTK_RESPONSE_OK) + emit dialog->accept(); + else + emit dialog->reject(); +} + +QGtk2ColorDialogHelper::QGtk2ColorDialogHelper() +{ + d.reset(new QGtk2Dialog(gtk_color_selection_dialog_new(""))); + connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); + connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); + + GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(d->gtkDialog())); + g_signal_connect_swapped(gtkColorSelection, "color-changed", G_CALLBACK(onColorChanged), this); +} + +QGtk2ColorDialogHelper::~QGtk2ColorDialogHelper() +{ +} + +bool QGtk2ColorDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +{ + applyOptions(); + return d->show(flags, modality, parent); +} + +void QGtk2ColorDialogHelper::exec() +{ + d->exec(); +} + +void QGtk2ColorDialogHelper::hide() +{ + d->hide(); +} + +void QGtk2ColorDialogHelper::setCurrentColor(const QColor &color) +{ + GtkDialog *gtkDialog = d->gtkDialog(); + GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); + GdkColor gdkColor; + gdkColor.red = color.red() << 8; + gdkColor.green = color.green() << 8; + gdkColor.blue = color.blue() << 8; + gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(gtkColorSelection), &gdkColor); + if (color.alpha() < 255) { + gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(gtkColorSelection), true); + gtk_color_selection_set_current_alpha(GTK_COLOR_SELECTION(gtkColorSelection), color.alpha() << 8); + } +} + +QColor QGtk2ColorDialogHelper::currentColor() const +{ + GtkDialog *gtkDialog = d->gtkDialog(); + GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); + GdkColor gdkColor; + gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(gtkColorSelection), &gdkColor); + guint16 alpha = gtk_color_selection_get_current_alpha(GTK_COLOR_SELECTION(gtkColorSelection)); + return QColor(gdkColor.red >> 8, gdkColor.green >> 8, gdkColor.blue >> 8, alpha >> 8); +} + +void QGtk2ColorDialogHelper::onAccepted() +{ + emit accept(); + emit colorSelected(currentColor()); +} + +void QGtk2ColorDialogHelper::onColorChanged(QGtk2ColorDialogHelper *dialog) +{ + emit dialog->currentColorChanged(dialog->currentColor()); +} + +void QGtk2ColorDialogHelper::applyOptions() +{ + GtkDialog* gtkDialog = d->gtkDialog(); + gtk_window_set_title(GTK_WINDOW(gtkDialog), options()->windowTitle().toUtf8()); + + GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); + gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(gtkColorSelection), options()->testOption(QColorDialogOptions::ShowAlphaChannel)); + + GtkWidget *okButton = 0; + GtkWidget *cancelButton = 0; + GtkWidget *helpButton = 0; + g_object_get(G_OBJECT(gtkDialog), "ok-button", &okButton, "cancel-button", &cancelButton, "help-button", &helpButton, NULL); + if (okButton) + g_object_set(G_OBJECT(okButton), "visible", !options()->testOption(QColorDialogOptions::NoButtons), NULL); + if (cancelButton) + g_object_set(G_OBJECT(cancelButton), "visible", !options()->testOption(QColorDialogOptions::NoButtons), NULL); + if (helpButton) + gtk_widget_hide(helpButton); +} + +QGtk2FileDialogHelper::QGtk2FileDialogHelper() +{ + d.reset(new QGtk2Dialog(gtk_file_chooser_dialog_new("", 0, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL))); + connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); + connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); + + g_signal_connect(GTK_FILE_CHOOSER(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this); + g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this); +} + +QGtk2FileDialogHelper::~QGtk2FileDialogHelper() +{ +} + +bool QGtk2FileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +{ + _dir.clear(); + _selection.clear(); + + applyOptions(); + return d->show(flags, modality, parent); +} + +void QGtk2FileDialogHelper::exec() +{ + d->exec(); +} + +void QGtk2FileDialogHelper::hide() +{ + // After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder() + // & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual + // values before hiding the dialog + _dir = directory(); + _selection = selectedFiles(); + + d->hide(); +} + +bool QGtk2FileDialogHelper::defaultNameFilterDisables() const +{ + return false; +} + +void QGtk2FileDialogHelper::setDirectory(const QString &directory) +{ + GtkDialog *gtkDialog = d->gtkDialog(); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), directory.toUtf8()); +} + +QString QGtk2FileDialogHelper::directory() const +{ + // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder() + // returns a bogus value -> return the cached value before hiding + if (!_dir.isEmpty()) + return _dir; + + QString ret; + GtkDialog *gtkDialog = d->gtkDialog(); + gchar *folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(gtkDialog)); + if (folder) { + ret = QString::fromUtf8(folder); + g_free(folder); + } + return ret; +} + +void QGtk2FileDialogHelper::selectFile(const QString &filename) +{ + GtkDialog *gtkDialog = d->gtkDialog(); + gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(gtkDialog), filename.toUtf8()); +} + +QStringList QGtk2FileDialogHelper::selectedFiles() const +{ + // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames() + // returns a bogus value -> return the cached value before hiding + if (!_selection.isEmpty()) + return _selection; + + QStringList selection; + GtkDialog *gtkDialog = d->gtkDialog(); + GSList *filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(gtkDialog)); + for (GSList *it = filenames; it; it = it->next) + selection += QString::fromUtf8((const char*)it->data); + g_slist_free(filenames); + return selection; +} + +void QGtk2FileDialogHelper::setFilter() +{ + applyOptions(); +} + +void QGtk2FileDialogHelper::selectNameFilter(const QString &filter) +{ + GtkFileFilter *gtkFilter = _filters.value(filter); + if (gtkFilter) { + GtkDialog *gtkDialog = d->gtkDialog(); + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter); + } +} + +QString QGtk2FileDialogHelper::selectedNameFilter() const +{ + GtkDialog *gtkDialog = d->gtkDialog(); + GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(gtkDialog)); + return _filterNames.value(gtkFilter); +} + +void QGtk2FileDialogHelper::onAccepted() +{ + emit accept(); + + QString filter = selectedNameFilter(); + if (filter.isEmpty()) + emit filterSelected(filter); + + QStringList files = selectedFiles(); + emit filesSelected(files); + if (files.count() == 1) + emit fileSelected(files.first()); +} + +void QGtk2FileDialogHelper::onSelectionChanged(GtkDialog *gtkDialog, QGtk2FileDialogHelper *helper) +{ + QString selection; + gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog)); + if (filename) { + selection = QString::fromUtf8(filename); + g_free(filename); + } + emit helper->currentChanged(selection); +} + +void QGtk2FileDialogHelper::onCurrentFolderChanged(QGtk2FileDialogHelper *dialog) +{ + emit dialog->directoryEntered(dialog->directory()); +} + +static GtkFileChooserAction gtkFileChooserAction(const QSharedPointer<QFileDialogOptions> &options) +{ + switch (options->fileMode()) { + case QFileDialogOptions::AnyFile: + case QFileDialogOptions::ExistingFile: + case QFileDialogOptions::ExistingFiles: + if (options->acceptMode() == QFileDialogOptions::AcceptOpen) + return GTK_FILE_CHOOSER_ACTION_OPEN; + else + return GTK_FILE_CHOOSER_ACTION_SAVE; + case QFileDialogOptions::Directory: + case QFileDialogOptions::DirectoryOnly: + default: + if (options->acceptMode() == QFileDialogOptions::AcceptOpen) + return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; + else + return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; + } +} + +void QGtk2FileDialogHelper::applyOptions() +{ + GtkDialog *gtkDialog = d->gtkDialog(); + const QSharedPointer<QFileDialogOptions> &opts = options(); + + gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8()); + gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(gtkDialog), true); + + const GtkFileChooserAction action = gtkFileChooserAction(opts); + gtk_file_chooser_set_action(GTK_FILE_CHOOSER(gtkDialog), action); + + const bool selectMultiple = opts->fileMode() == QFileDialogOptions::ExistingFiles; + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(gtkDialog), selectMultiple); + + const bool confirmOverwrite = !opts->testOption(QFileDialogOptions::DontConfirmOverwrite); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(gtkDialog), confirmOverwrite); + + const QStringList nameFilters = opts->nameFilters(); + if (!nameFilters.isEmpty()) + setNameFilters(nameFilters); + + const QString initialDirectory = opts->initialDirectory(); + if (!initialDirectory.isEmpty()) + setDirectory(initialDirectory); + + foreach (const QString &filename, opts->initiallySelectedFiles()) + selectFile(filename); + + const QString initialNameFilter = opts->initiallySelectedNameFilter(); + if (!initialNameFilter.isEmpty()) + selectNameFilter(initialNameFilter); + + GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK); + if (acceptButton) { + if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) + gtk_button_set_label(GTK_BUTTON(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8()); + else if (opts->acceptMode() == QFileDialogOptions::AcceptOpen) + gtk_button_set_label(GTK_BUTTON(acceptButton), GTK_STOCK_OPEN); + else + gtk_button_set_label(GTK_BUTTON(acceptButton), GTK_STOCK_SAVE); + } + + GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL); + if (rejectButton) { + if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject)) + gtk_button_set_label(GTK_BUTTON(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8()); + else + gtk_button_set_label(GTK_BUTTON(rejectButton), GTK_STOCK_CANCEL); + } +} + +void QGtk2FileDialogHelper::setNameFilters(const QStringList& filters) +{ + GtkDialog *gtkDialog = d->gtkDialog(); + foreach (GtkFileFilter *filter, _filters) + gtk_file_chooser_remove_filter(GTK_FILE_CHOOSER(gtkDialog), filter); + + _filters.clear(); + _filterNames.clear(); + + foreach (const QString &filter, filters) { + GtkFileFilter *gtkFilter = gtk_file_filter_new(); + const QString name = filter.left(filter.indexOf(QLatin1Char('('))); + const QStringList extensions = cleanFilterList(filter); + + gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8() : name.toUtf8()); + foreach (const QString &ext, extensions) + gtk_file_filter_add_pattern(gtkFilter, ext.toUtf8()); + + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter); + + _filters.insert(filter, gtkFilter); + _filterNames.insert(gtkFilter, filter); + } +} + +QGtk2FontDialogHelper::QGtk2FontDialogHelper() +{ + d.reset(new QGtk2Dialog(gtk_font_selection_dialog_new(""))); + connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); + connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); +} + +QGtk2FontDialogHelper::~QGtk2FontDialogHelper() +{ +} + +bool QGtk2FontDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +{ + applyOptions(); + return d->show(flags, modality, parent); +} + +void QGtk2FontDialogHelper::exec() +{ + d->exec(); +} + +void QGtk2FontDialogHelper::hide() +{ + d->hide(); +} + +static QString qt_fontToString(const QFont& font) +{ + PangoFontDescription *desc = pango_font_description_new(); + pango_font_description_set_size(desc, font.pointSizeF() * PANGO_SCALE); + pango_font_description_set_family(desc, font.family().toUtf8()); + + int weight = font.weight(); + if (weight >= QFont::Black) + pango_font_description_set_weight(desc, PANGO_WEIGHT_HEAVY); + else if (weight >= QFont::Bold) + pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); + else if (weight >= QFont::DemiBold) + pango_font_description_set_weight(desc, PANGO_WEIGHT_SEMIBOLD); + else if (weight >= QFont::Normal) + pango_font_description_set_weight(desc, PANGO_WEIGHT_NORMAL); + else + pango_font_description_set_weight(desc, PANGO_WEIGHT_LIGHT); + + int style = font.style(); + if (style == QFont::StyleItalic) + pango_font_description_set_style(desc, PANGO_STYLE_ITALIC); + else if (style == QFont::StyleOblique) + pango_font_description_set_style(desc, PANGO_STYLE_OBLIQUE); + else + pango_font_description_set_style(desc, PANGO_STYLE_NORMAL); + + char *str = pango_font_description_to_string(desc); + QString name = QString::fromUtf8(str); + pango_font_description_free(desc); + g_free(str); + return name; +} + +static QFont qt_fontFromString(const QString &name) +{ + QFont font; + PangoFontDescription* desc = pango_font_description_from_string(name.toUtf8()); + font.setPointSizeF(static_cast<float>(pango_font_description_get_size(desc)) / PANGO_SCALE); + + QString family = QString::fromUtf8(pango_font_description_get_family(desc)); + if (!family.isEmpty()) + font.setFamily(family); + + int weight = pango_font_description_get_weight(desc); + if (weight >= PANGO_WEIGHT_HEAVY) + font.setWeight(QFont::Black); + else if (weight >= PANGO_WEIGHT_BOLD) + font.setWeight(QFont::Bold); + else if (weight >= PANGO_WEIGHT_SEMIBOLD) + font.setWeight(QFont::DemiBold); + else if (weight >= PANGO_WEIGHT_NORMAL) + font.setWeight(QFont::Normal); + else + font.setWeight(QFont::Light); + + PangoStyle style = pango_font_description_get_style(desc); + if (style == PANGO_STYLE_ITALIC) + font.setStyle(QFont::StyleItalic); + else if (style == PANGO_STYLE_OBLIQUE) + font.setStyle(QFont::StyleOblique); + else + font.setStyle(QFont::StyleNormal); + + pango_font_description_free(desc); + return font; +} + +void QGtk2FontDialogHelper::setCurrentFont(const QFont &font) +{ + GtkFontSelectionDialog *gtkDialog = GTK_FONT_SELECTION_DIALOG(d->gtkDialog()); + gtk_font_selection_dialog_set_font_name(gtkDialog, qt_fontToString(font).toUtf8()); +} + +QFont QGtk2FontDialogHelper::currentFont() const +{ + GtkFontSelectionDialog *gtkDialog = GTK_FONT_SELECTION_DIALOG(d->gtkDialog()); + gchar *name = gtk_font_selection_dialog_get_font_name(gtkDialog); + QFont font = qt_fontFromString(QString::fromUtf8(name)); + g_free(name); + return font; +} + +void QGtk2FontDialogHelper::onAccepted() +{ + emit accept(); + emit fontSelected(currentFont()); +} + +void QGtk2FontDialogHelper::applyOptions() +{ + GtkDialog *gtkDialog = d->gtkDialog(); + const QSharedPointer<QFontDialogOptions> &opts = options(); + + gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8()); + + GtkWidget *okButton = gtk_font_selection_dialog_get_ok_button(GTK_FONT_SELECTION_DIALOG(gtkDialog)); + GtkWidget *cancelButton = gtk_font_selection_dialog_get_cancel_button(GTK_FONT_SELECTION_DIALOG(gtkDialog)); + if (okButton) + gtk_widget_set_visible(okButton, !options()->testOption(QFontDialogOptions::NoButtons)); + if (cancelButton) + gtk_widget_set_visible(cancelButton, !options()->testOption(QFontDialogOptions::NoButtons)); +} + +QT_END_NAMESPACE + +#include "qgtk2dialoghelpers.moc" diff --git a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.h b/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.h new file mode 100644 index 0000000000..2c5381aec8 --- /dev/null +++ b/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGTK2DIALOGHELPERS_P_H +#define QGTK2DIALOGHELPERS_P_H + +#include <QtCore/qscopedpointer.h> +#include <qpa/qplatformdialoghelper.h> + +typedef struct _GtkDialog GtkDialog; +typedef struct _GtkFileFilter GtkFileFilter; + +QT_BEGIN_NAMESPACE + +class QGtk2Dialog; + +class QGtk2ColorDialogHelper : public QPlatformColorDialogHelper +{ + Q_OBJECT + +public: + QGtk2ColorDialogHelper(); + ~QGtk2ColorDialogHelper(); + + virtual bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent); + virtual void exec(); + virtual void hide(); + + virtual void setCurrentColor(const QColor &color); + virtual QColor currentColor() const; + +private Q_SLOTS: + void onAccepted(); + +private: + static void onColorChanged(QGtk2ColorDialogHelper *helper); + void applyOptions(); + + mutable QScopedPointer<QGtk2Dialog> d; +}; + +class QGtk2FileDialogHelper : public QPlatformFileDialogHelper +{ + Q_OBJECT + +public: + QGtk2FileDialogHelper(); + ~QGtk2FileDialogHelper(); + + virtual bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent); + virtual void exec(); + virtual void hide(); + + virtual bool defaultNameFilterDisables() const; + virtual void setDirectory(const QString &directory); + virtual QString directory() const; + virtual void selectFile(const QString &filename); + virtual QStringList selectedFiles() const; + virtual void setFilter(); + virtual void selectNameFilter(const QString &filter); + virtual QString selectedNameFilter() const; + +private Q_SLOTS: + void onAccepted(); + +private: + static void onSelectionChanged(GtkDialog *dialog, QGtk2FileDialogHelper *helper); + static void onCurrentFolderChanged(QGtk2FileDialogHelper *helper); + void applyOptions(); + void setNameFilters(const QStringList &filters); + + QString _dir; + QStringList _selection; + QHash<QString, GtkFileFilter*> _filters; + QHash<GtkFileFilter*, QString> _filterNames; + mutable QScopedPointer<QGtk2Dialog> d; +}; + +class QGtk2FontDialogHelper : public QPlatformFontDialogHelper +{ + Q_OBJECT + +public: + QGtk2FontDialogHelper(); + ~QGtk2FontDialogHelper(); + + virtual bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent); + virtual void exec(); + virtual void hide(); + + virtual void setCurrentFont(const QFont &font); + virtual QFont currentFont() const; + +private Q_SLOTS: + void onAccepted(); + +private: + void applyOptions(); + + mutable QScopedPointer<QGtk2Dialog> d; +}; + +QT_END_NAMESPACE + +#endif // QGTK2DIALOGHELPERS_P_H diff --git a/src/plugins/platformthemes/gtk2/qgtk2theme.cpp b/src/plugins/platformthemes/gtk2/qgtk2theme.cpp new file mode 100644 index 0000000000..7bb538b888 --- /dev/null +++ b/src/plugins/platformthemes/gtk2/qgtk2theme.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgtk2theme.h" +#include "qgtk2dialoghelpers.h" + +#undef signals +#include <gtk/gtk.h> + +QT_BEGIN_NAMESPACE + +const char *QGtk2Theme::name = "gtk2"; + +QGtk2Theme::QGtk2Theme() +{ + gtk_init(0, 0); +} + +bool QGtk2Theme::usePlatformNativeDialog(DialogType type) const +{ + Q_UNUSED(type); + return true; +} + +QPlatformDialogHelper *QGtk2Theme::createPlatformDialogHelper(DialogType type) const +{ + switch (type) { + case ColorDialog: + return new QGtk2ColorDialogHelper; + case FileDialog: + return new QGtk2FileDialogHelper; + case FontDialog: + return new QGtk2FontDialogHelper; + default: + return 0; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platformthemes/gtk2/qgtk2theme.h b/src/plugins/platformthemes/gtk2/qgtk2theme.h new file mode 100644 index 0000000000..6d8768dd8e --- /dev/null +++ b/src/plugins/platformthemes/gtk2/qgtk2theme.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGTK2THEME_H +#define QGTK2THEME_H + +#include <private/qgenericunixthemes_p.h> + +QT_BEGIN_NAMESPACE + +class QGtk2Theme : public QGnomeTheme +{ +public: + QGtk2Theme(); + + virtual bool usePlatformNativeDialog(DialogType type) const; + virtual QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const; + + static const char *name; +}; + +QT_END_NAMESPACE + +#endif // QGTK2THEME_H diff --git a/src/plugins/platformthemes/platformthemes.pro b/src/plugins/platformthemes/platformthemes.pro new file mode 100644 index 0000000000..23dcda2c82 --- /dev/null +++ b/src/plugins/platformthemes/platformthemes.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +contains(QT_CONFIG, gtk2): SUBDIRS += gtk2 diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 3b0ff3f6c8..516105401e 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,7 +1,8 @@ TEMPLATE = subdirs -SUBDIRS *= sqldrivers bearer -qtHaveModule(gui): SUBDIRS *= imageformats platforms platforminputcontexts generic +SUBDIRS *= sqldrivers +qtHaveModule(network): SUBDIRS += bearer +qtHaveModule(gui): SUBDIRS *= imageformats platforms platforminputcontexts platformthemes generic qtHaveModule(widgets): SUBDIRS += accessible !wince*:qtHaveModule(widgets): SUBDIRS += printsupport diff --git a/src/plugins/printsupport/cups/qcupsprintersupport.cpp b/src/plugins/printsupport/cups/qcupsprintersupport.cpp index c2e9bd445f..f41d4f5047 100644 --- a/src/plugins/printsupport/cups/qcupsprintersupport.cpp +++ b/src/plugins/printsupport/cups/qcupsprintersupport.cpp @@ -86,6 +86,11 @@ QList<QPrinter::PaperSize> QCupsPrinterSupport::supportedPaperSizes(const QPrint return QCUPSSupport::getCupsPrinterPaperSizes(printerIndex(printerInfo)); } +QList<QPair<QString, QSizeF> > QCupsPrinterSupport::supportedSizesWithNames(const QPrinterInfo &printerInfo) const +{ + return QCUPSSupport::getCupsPrinterPaperSizesWithNames(printerIndex(printerInfo)); +} + void QCupsPrinterSupport::loadCups() { cupsGetDests = (CupsGetDests) m_cups.resolve("cupsGetDests"); diff --git a/src/plugins/printsupport/cups/qcupsprintersupport_p.h b/src/plugins/printsupport/cups/qcupsprintersupport_p.h index 43fe871021..e9fe24203e 100644 --- a/src/plugins/printsupport/cups/qcupsprintersupport_p.h +++ b/src/plugins/printsupport/cups/qcupsprintersupport_p.h @@ -52,7 +52,6 @@ #include <cups/cups.h> -QT_BEGIN_HEADER QT_BEGIN_NAMESPACE typedef int (*CupsGetDests)(cups_dest_t **dests); @@ -68,6 +67,7 @@ public: virtual QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode); virtual QPaintEngine *createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode); virtual QList<QPrinter::PaperSize> supportedPaperSizes(const QPrinterInfo &) const; + virtual QList<QPair<QString, QSizeF> > supportedSizesWithNames(const QPrinterInfo &) const; virtual QString printerOption(const QPrinterInfo &printer, const QString &key) const; virtual PrinterOptions printerOptions(const QPrinterInfo &printer) const; @@ -86,7 +86,6 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // QT_NO_PRINTER #endif // QCUPSPRINTERSUPPORT_H diff --git a/src/plugins/printsupport/windows/qwindowsprintersupport.cpp b/src/plugins/printsupport/windows/qwindowsprintersupport.cpp index 469dbdb34e..36e7a3fb8e 100644 --- a/src/plugins/printsupport/windows/qwindowsprintersupport.cpp +++ b/src/plugins/printsupport/windows/qwindowsprintersupport.cpp @@ -93,4 +93,9 @@ QList<QPrinter::PaperSize> QWindowsPrinterSupport::supportedPaperSizes(const QPr return QWin32PrintEngine::supportedPaperSizes(printerInfo); } +QList<QPair<QString, QSizeF> >QWindowsPrinterSupport::supportedSizesWithNames(const QPrinterInfo &printerInfo) const +{ + return QWin32PrintEngine::supportedSizesWithNames(printerInfo); +} + QT_END_NAMESPACE diff --git a/src/plugins/printsupport/windows/qwindowsprintersupport.h b/src/plugins/printsupport/windows/qwindowsprintersupport.h index afa341a82f..1d5a4f3da4 100644 --- a/src/plugins/printsupport/windows/qwindowsprintersupport.h +++ b/src/plugins/printsupport/windows/qwindowsprintersupport.h @@ -44,7 +44,6 @@ #include <qpa/qplatformprintersupport.h> -QT_BEGIN_HEADER QT_BEGIN_NAMESPACE class QWin32PrintEngine; @@ -58,9 +57,9 @@ public: virtual QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode); virtual QPaintEngine *createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode); virtual QList<QPrinter::PaperSize> supportedPaperSizes(const QPrinterInfo &) const; + virtual QList<QPair<QString, QSizeF> >supportedSizesWithNames(const QPrinterInfo &printerInfo) const; }; QT_END_NAMESPACE -QT_END_HEADER #endif // WINDOWSPRINTERSUPPORT_H |