diff options
author | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2014-03-24 16:10:15 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2014-03-24 16:10:15 +0100 |
commit | 3b5c0bc0780f1749fed7c07bd8b691400a0282b7 (patch) | |
tree | 1022f5553ad5a0aca9b5f3b49ca38a01c2329d20 /src/plugins | |
parent | c79918733a194ebbe5a2fe1617c884659f3e4b9f (diff) | |
parent | 21f1738a94fc8544ece04b3b1ee03a11986fe59b (diff) |
Merge remote-tracking branch 'origin/stable' into dev
Conflicts:
src/gui/image/qjpeghandler.cpp
Change-Id: I9db3acea7d5c82f5da679c8eaeb29431136665f0
Diffstat (limited to 'src/plugins')
84 files changed, 3272 insertions, 1120 deletions
diff --git a/src/plugins/bearer/corewlan/qcorewlanengine.mm b/src/plugins/bearer/corewlan/qcorewlanengine.mm index dc2920360d..2d8b9be092 100644 --- a/src/plugins/bearer/corewlan/qcorewlanengine.mm +++ b/src/plugins/bearer/corewlan/qcorewlanengine.mm @@ -573,6 +573,8 @@ void QCoreWlanEngine::doRequestUpdate() scanThread->start(); } locker.unlock(); + if ([wifiInterfaces count] == 0) + networksChanged(); [autoreleasepool release]; } diff --git a/src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm b/src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm index 7044e9696b..057aec5487 100644 --- a/src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm +++ b/src/plugins/bearer/corewlan/qcorewlanengine_10_6.mm @@ -631,6 +631,8 @@ void QCoreWlanEngine::doRequestUpdate() scanThread->start(); } locker.unlock(); + if ([wifiInterfaces count] == 0) + networksChanged(); [autoreleasepool release]; } diff --git a/src/plugins/platforminputcontexts/compose/compose.pro b/src/plugins/platforminputcontexts/compose/compose.pro index 546a0a2af7..7182c458fc 100644 --- a/src/plugins/platforminputcontexts/compose/compose.pro +++ b/src/plugins/platforminputcontexts/compose/compose.pro @@ -6,8 +6,6 @@ load(qt_plugin) QT += gui-private -LIBS += $$QMAKE_LIBS_XKBCOMMON -QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XKBCOMMON DEFINES += X11_PREFIX='\\"$$QMAKE_X11_PREFIX\\"' SOURCES += $$PWD/main.cpp \ @@ -19,14 +17,12 @@ HEADERS += $$PWD/qcomposeplatforminputcontext.h \ # libxkbcommon contains(QT_CONFIG, xkbcommon-qt): { + # dont't need x11 dependency for compose key plugin + QT_CONFIG -= use-xkbcommon-x11support include(../../../3rdparty/xkbcommon.pri) } else { LIBS += $$QMAKE_LIBS_XKBCOMMON QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XKBCOMMON - equals(QMAKE_VERSION_XKBCOMMON, "0.2.0") { - DEFINES += XKBCOMMON_0_2_0 - INCLUDEPATH += ../../../3rdparty/xkbcommon/xkbcommon/ - } } OTHER_FILES += $$PWD/compose.json diff --git a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp index ca61b0e495..8bbb490022 100644 --- a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp +++ b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp @@ -51,10 +51,6 @@ #include <xkbcommon/xkbcommon.h> -#ifdef XKBCOMMON_0_2_0 -#include <xkbcommon_workaround.h> -#endif - #include <locale.h> // LC_CTYPE #include <string.h> // strchr, strncmp, etc. #include <strings.h> // strncasecmp @@ -326,23 +322,7 @@ ushort TableGenerator::keysymToUtf8(quint32 sym) QByteArray chars; int bytes; chars.resize(8); - -#ifdef XKBCOMMON_0_2_0 - if (needWorkaround(sym)) { - quint32 codepoint; - if (sym == XKB_KEY_KP_Space) - codepoint = XKB_KEY_space & 0x7f; - else - codepoint = sym & 0x7f; - - bytes = utf32_to_utf8(codepoint, chars.data()); - } else { - bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size()); - } -#else bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size()); -#endif - if (bytes == -1) qWarning("TableGenerator::keysymToUtf8 - buffer too small"); diff --git a/src/plugins/platforminputcontexts/platforminputcontexts.pro b/src/plugins/platforminputcontexts/platforminputcontexts.pro index 60b66bfb35..faea54b874 100644 --- a/src/plugins/platforminputcontexts/platforminputcontexts.pro +++ b/src/plugins/platforminputcontexts/platforminputcontexts.pro @@ -4,7 +4,6 @@ qtHaveModule(dbus) { !mac:!win32:SUBDIRS += ibus } -unix:!macx:!contains(DEFINES, QT_NO_XKBCOMMON): { - SUBDIRS += compose -} +contains(QT_CONFIG, xcb-plugin): SUBDIRS += compose + diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp index 55d44b7377..9efdcad158 100644 --- a/src/plugins/platforms/android/androidjniinput.cpp +++ b/src/plugins/platforms/android/androidjniinput.cpp @@ -67,8 +67,6 @@ namespace QtAndroidInput static QPointer<QWindow> m_mouseGrabber; - static int m_lastCursorPos = -1; - void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd) { AttachedJNIEnv env; @@ -78,21 +76,6 @@ namespace QtAndroidInput #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << ">>> UPDATESELECTION" << selStart << selEnd << candidatesStart << candidatesEnd; #endif - if (candidatesStart == -1 && candidatesEnd == -1 && selStart == selEnd) { - // Qt only gives us position inside the block, so if we move to the - // same position in another block, the Android keyboard will believe - // we have not changed position, and be terribly confused. - if (selStart == m_lastCursorPos) { -#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL - qDebug() << ">>> FAKEUPDATESELECTION" << selStart+1; -#endif - env.jniEnv->CallStaticVoidMethod(applicationClass(), m_updateSelectionMethodID, - selStart+1, selEnd+1, candidatesStart, candidatesEnd); - } - m_lastCursorPos = selStart; - } else { - m_lastCursorPos = -1; - } env.jniEnv->CallStaticVoidMethod(applicationClass(), m_updateSelectionMethodID, selStart, selEnd, candidatesStart, candidatesEnd); } @@ -517,6 +500,12 @@ namespace QtAndroidInput } } + // maps 0 to the empty string, and anything else to a single-character string + static inline QString toString(jint unicode) + { + return unicode ? QString(QChar(unicode)) : QString(); + } + static void keyDown(JNIEnv */*env*/, jobject /*thiz*/, jint key, jint unicode, jint modifier) { Qt::KeyboardModifiers modifiers; @@ -533,7 +522,7 @@ namespace QtAndroidInput QEvent::KeyPress, mapAndroidKey(key), modifiers, - QChar(unicode), + toString(unicode), false); } @@ -553,7 +542,7 @@ namespace QtAndroidInput QEvent::KeyRelease, mapAndroidKey(key), modifiers, - QChar(unicode), + toString(unicode), false); } diff --git a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp index b112e265a5..224a8ca9f7 100644 --- a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp +++ b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp @@ -262,18 +262,20 @@ private: AndroidAssetsFileEngineHandler::AndroidAssetsFileEngineHandler() : m_assetsCache(std::max(5, qgetenv("QT_ANDROID_MAX_ASSETS_CACHE_SIZE").toInt())) , m_hasPrepopulatedCache(false) + , m_hasTriedPrepopulatingCache(false) { m_assetManager = QtAndroid::assetManager(); - prepopulateCache(); } AndroidAssetsFileEngineHandler::~AndroidAssetsFileEngineHandler() { } -void AndroidAssetsFileEngineHandler::prepopulateCache() +void AndroidAssetsFileEngineHandler::prepopulateCache() const { - QMutexLocker locker(&m_assetsCacheMutext); + Q_ASSERT(!m_hasTriedPrepopulatingCache); + m_hasTriedPrepopulatingCache = true; + Q_ASSERT(m_assetsCache.isEmpty()); // Failsafe: Don't read cache files that are larger than 1MB @@ -364,7 +366,11 @@ QAbstractFileEngine * AndroidAssetsFileEngineHandler::create(const QString &file if (!path.size()) path = fileName.left(fileName.length() - 1).toUtf8(); + m_assetsCacheMutext.lock(); + if (!m_hasTriedPrepopulatingCache) + prepopulateCache(); + QSharedPointer<AndroidAssetDir> *aad = m_assetsCache.object(path); m_assetsCacheMutext.unlock(); if (!aad) { diff --git a/src/plugins/platforms/android/qandroidassetsfileenginehandler.h b/src/plugins/platforms/android/qandroidassetsfileenginehandler.h index d56367d4d8..ac16ad7b79 100644 --- a/src/plugins/platforms/android/qandroidassetsfileenginehandler.h +++ b/src/plugins/platforms/android/qandroidassetsfileenginehandler.h @@ -58,12 +58,13 @@ public: QAbstractFileEngine *create(const QString &fileName) const; private: - void prepopulateCache(); + void prepopulateCache() const; AAssetManager *m_assetManager; mutable QCache<QByteArray, QSharedPointer<AndroidAssetDir>> m_assetsCache; mutable QMutex m_assetsCacheMutext; - bool m_hasPrepopulatedCache; + mutable bool m_hasPrepopulatedCache; + mutable bool m_hasTriedPrepopulatingCache; }; #endif // QANDROIDASSETSFILEENGINEHANDLER_H diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index 326972e71e..bfb13811e3 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -81,7 +81,7 @@ static jboolean commitText(JNIEnv *env, jobject /*thiz*/, jstring text, jint new env->ReleaseStringChars(text, jstr); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL - qDebug() << "@@@ COMMIT" << str; + qDebug() << "@@@ COMMIT" << str << newCursorPosition; #endif return m_androidInputContext->commitText(str, newCursorPosition); } @@ -160,7 +160,7 @@ static jstring getTextAfterCursor(JNIEnv *env, jobject /*thiz*/, jint length, ji const QString &text = m_androidInputContext->getTextAfterCursor(length, flags); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL - qDebug() << "@@@ GET" << length << text; + qDebug() << "@@@ GETA" << length << text; #endif return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length())); } @@ -172,7 +172,7 @@ static jstring getTextBeforeCursor(JNIEnv *env, jobject /*thiz*/, jint length, j const QString &text = m_androidInputContext->getTextBeforeCursor(length, flags); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL - qDebug() << "@@@ GET" << length << text; + qDebug() << "@@@ GETB" << length << text; #endif return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length())); } @@ -188,7 +188,7 @@ static jboolean setComposingText(JNIEnv *env, jobject /*thiz*/, jstring text, ji env->ReleaseStringChars(text, jstr); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL - qDebug() << "@@@ SET" << str; + qDebug() << "@@@ SET" << str << newCursorPosition; #endif return m_androidInputContext->setComposingText(str, newCursorPosition); } @@ -271,6 +271,18 @@ static jboolean paste(JNIEnv */*env*/, jobject /*thiz*/) return m_androidInputContext->paste(); } +static jboolean updateCursorPosition(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL + qDebug() << "@@@ UPDATECURSORPOS"; +#endif + m_androidInputContext->updateCursorPosition(); + return true; +} + static JNINativeMethod methods[] = { {"commitText", "(Ljava/lang/String;I)Z", (void *)commitText}, @@ -288,7 +300,8 @@ static JNINativeMethod methods[] = { {"cut", "()Z", (void *)cut}, {"copy", "()Z", (void *)copy}, {"copyURL", "()Z", (void *)copyURL}, - {"paste", "()Z", (void *)paste} + {"paste", "()Z", (void *)paste}, + {"updateCursorPosition", "()Z", (void *)updateCursorPosition} }; @@ -404,7 +417,9 @@ void QAndroidInputContext::updateCursorPosition() { QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (!query.isNull() && !m_blockUpdateSelection) { - const int cursorPos = query->value(Qt::ImCursorPosition).toInt(); + // make sure it also works with editors that have not been updated to the new API + QVariant absolutePos = query->value(Qt::ImAbsolutePosition); + const int cursorPos = absolutePos.isValid() ? absolutePos.toInt() : query->value(Qt::ImCursorPosition).toInt(); QtAndroidInput::updateSelection(cursorPos, cursorPos, -1, -1); //selection empty and no pre-edit text } } @@ -422,9 +437,9 @@ void QAndroidInputContext::invokeAction(QInputMethod::Action action, int cursorP #warning TODO Handle at least QInputMethod::ContextMenu action Q_UNUSED(action) Q_UNUSED(cursorPosition) - - if (action == QInputMethod::Click) - commit(); + //### click should be passed to the IM, but in the meantime it's better to ignore it than to do something wrong + // if (action == QInputMethod::Click) + // commit(); } QRectF QAndroidInputContext::keyboardRect() const @@ -573,6 +588,12 @@ QString QAndroidInputContext::getSelectedText(jint /*flags*/) QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/) { + QVariant textAfter = queryFocusObjectThreadSafe(Qt::ImTextAfterCursor, QVariant(length)); + if (textAfter.isValid()) { + return textAfter.toString().left(length); + } + + //compatibility code for old controls that do not implement the new API QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return QString(); @@ -587,15 +608,21 @@ QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/) QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) { + QVariant textBefore = queryFocusObjectThreadSafe(Qt::ImTextBeforeCursor, QVariant(length)); + if (textBefore.isValid()) { + return textBefore.toString().left(length); + } + + //compatibility code for old controls that do not implement the new API QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return QString(); + int cursorPos = query->value(Qt::ImCursorPosition).toInt(); QString text = query->value(Qt::ImSurroundingText).toString(); if (!text.length()) return text; - int cursorPos = query->value(Qt::ImCursorPosition).toInt(); const int wordLeftPos = cursorPos - length; return text.mid(wordLeftPos > 0 ? wordLeftPos : 0, cursorPos); } @@ -621,8 +648,9 @@ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCur QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (!query.isNull() && !m_blockUpdateSelection) { - int cursorPos = query->value(Qt::ImCursorPosition).toInt(); - int preeditLength = text.length(); + QVariant absolutePos = query->value(Qt::ImAbsolutePosition); + const int cursorPos = absolutePos.isValid() ? absolutePos.toInt() : query->value(Qt::ImCursorPosition).toInt(); + const int preeditLength = text.length(); QtAndroidInput::updateSelection(cursorPos+preeditLength, cursorPos+preeditLength, cursorPos, cursorPos+preeditLength); } @@ -650,16 +678,19 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) Therefore, the length of the region is end - start */ int length = end - start; + int localPos = query->value(Qt::ImCursorPosition).toInt(); + QVariant absolutePos = query->value(Qt::ImAbsolutePosition); + int blockPosition = absolutePos.isValid() ? absolutePos.toInt() - localPos : 0; + int localStart = start - blockPosition; // Qt uses position inside block bool updateSelectionWasBlocked = m_blockUpdateSelection; m_blockUpdateSelection = true; QString text = query->value(Qt::ImSurroundingText).toString(); - m_composingText = text.mid(start, length); + m_composingText = text.mid(localStart, length); - //in the Qt text controls, cursor pos is the start of the preedit - int cursorPos = query->value(Qt::ImCursorPosition).toInt(); - int relativeStart = start - cursorPos; + //in the Qt text controls, the cursor position is the start of the preedit + int relativeStart = localStart - localPos; QList<QInputMethodEvent::Attribute> attributes; @@ -669,6 +700,9 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,0, length, QVariant(underlined))); + // Keep the cursor position unchanged (don't move to end of preedit) + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, localPos - localStart, length, QVariant())); + QInputMethodEvent event(m_composingText, attributes); event.setCommitString(QString(), relativeStart, length); sendInputMethodEvent(&event); @@ -720,6 +754,26 @@ jboolean QAndroidInputContext::paste() return JNI_FALSE; } + +Q_INVOKABLE QVariant QAndroidInputContext::queryFocusObjectUnsafe(Qt::InputMethodQuery query, QVariant argument) +{ + return QInputMethod::queryFocusObject(query, argument); +} + +QVariant QAndroidInputContext::queryFocusObjectThreadSafe(Qt::InputMethodQuery query, QVariant argument) +{ + bool inMainThread = qGuiApp->thread() == QThread::currentThread(); + QVariant retval; + + QMetaObject::invokeMethod(this, "queryFocusObjectUnsafe", + inMainThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QVariant, retval), + Q_ARG(Qt::InputMethodQuery, query), + Q_ARG(QVariant, argument)); + + return retval; +} + QSharedPointer<QInputMethodQueryEvent> QAndroidInputContext::focusObjectInputMethodQuery(Qt::InputMethodQueries queries) { #warning TODO make qGuiApp->focusObject() thread safe !!! diff --git a/src/plugins/platforms/android/qandroidinputcontext.h b/src/plugins/platforms/android/qandroidinputcontext.h index 041bd0dc49..2fb54a97c4 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.h +++ b/src/plugins/platforms/android/qandroidinputcontext.h @@ -114,14 +114,19 @@ public: jboolean copyURL(); jboolean paste(); +public slots: + void updateCursorPosition(); + private: QSharedPointer<QInputMethodQueryEvent> focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll); void sendInputMethodEvent(QInputMethodEvent *event); + Q_INVOKABLE QVariant queryFocusObjectUnsafe(Qt::InputMethodQuery query, QVariant argument); + QVariant queryFocusObjectThreadSafe(Qt::InputMethodQuery query, QVariant argument); + private slots: virtual void sendEvent(QObject *receiver, QInputMethodEvent *event); virtual void sendEvent(QObject *receiver, QInputMethodQueryEvent *event); - void updateCursorPosition(); private: ExtractedText m_extractedText; diff --git a/src/plugins/platforms/android/qandroidplatformbackingstore.cpp b/src/plugins/platforms/android/qandroidplatformbackingstore.cpp index b85b1157a8..ff49f59076 100644 --- a/src/plugins/platforms/android/qandroidplatformbackingstore.cpp +++ b/src/plugins/platforms/android/qandroidplatformbackingstore.cpp @@ -50,11 +50,8 @@ QT_BEGIN_NAMESPACE QAndroidPlatformBackingStore::QAndroidPlatformBackingStore(QWindow *window) : QPlatformBackingStore(window) { - Q_ASSERT(window->handle()); - if (window->surfaceType() == QSurface::RasterSurface) - (static_cast<QAndroidPlatformRasterWindow *>(window->handle()))->setBackingStore(this); - else - qWarning("QAndroidPlatformBackingStore does not support GL windows."); + if (window->handle()) + setBackingStore(window); } QPaintDevice *QAndroidPlatformBackingStore::paintDevice() @@ -64,9 +61,11 @@ QPaintDevice *QAndroidPlatformBackingStore::paintDevice() void QAndroidPlatformBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) { - Q_UNUSED(window); Q_UNUSED(offset); + if (!m_backingStoreSet) + setBackingStore(window); + (static_cast<QAndroidPlatformRasterWindow *>(window->handle()))->repaint(region); } @@ -78,4 +77,14 @@ void QAndroidPlatformBackingStore::resize(const QSize &size, const QRegion &stat m_image = QImage(size, window()->screen()->handle()->format()); } +void QAndroidPlatformBackingStore::setBackingStore(QWindow *window) +{ + if (window->surfaceType() == QSurface::RasterSurface) { + (static_cast<QAndroidPlatformRasterWindow *>(window->handle()))->setBackingStore(this); + m_backingStoreSet = true; + } else { + qWarning("QAndroidPlatformBackingStore does not support GL windows."); + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/qandroidplatformbackingstore.h b/src/plugins/platforms/android/qandroidplatformbackingstore.h index e6ea3dcce0..4c24e129c3 100644 --- a/src/plugins/platforms/android/qandroidplatformbackingstore.h +++ b/src/plugins/platforms/android/qandroidplatformbackingstore.h @@ -56,9 +56,10 @@ public: virtual void flush(QWindow *window, const QRegion ®ion, const QPoint &offset); virtual void resize(const QSize &size, const QRegion &staticContents); const QImage image() { return m_image; } - + void setBackingStore(QWindow *window); protected: QImage m_image; + bool m_backingStoreSet = false; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp index 2cca974b41..9adefd5b2c 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp @@ -152,7 +152,8 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const case ThreadedOpenGL: if (needsWorkaround()) return false; - // fall through + else + return true; default: return QPlatformIntegration::hasCapability(cap); } diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index d8cd32255c..a60f4adc28 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -81,7 +81,7 @@ HEADERS += qcocoaintegration.h \ RESOURCES += qcocoaresources.qrc -LIBS += -framework Cocoa -framework Carbon -framework IOKit +LIBS += -framework Cocoa -framework Carbon -framework IOKit -lcups QT += core-private gui-private platformsupport-private @@ -90,11 +90,13 @@ qtHaveModule(widgets) { qpaintengine_mac.mm \ qprintengine_mac.mm \ qcocoaprintersupport.mm \ + qcocoaprintdevice.mm \ HEADERS += \ qpaintengine_mac_p.h \ qprintengine_mac_p.h \ qcocoaprintersupport.h \ + qcocoaprintdevice.h \ QT += widgets-private printsupport-private } diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index 1371eb3658..0f99a414a0 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -130,7 +130,7 @@ static void populateRoleMap() roleMap[QAccessible::Cell] = NSAccessibilityTextFieldRole; roleMap[QAccessible::PushButton] = NSAccessibilityButtonRole; roleMap[QAccessible::EditableText] = NSAccessibilityTextFieldRole; - roleMap[QAccessible::Link] = NSAccessibilityTextFieldRole; + roleMap[QAccessible::Link] = NSAccessibilityLinkRole; roleMap[QAccessible::Indicator] = NSAccessibilityValueIndicatorRole; roleMap[QAccessible::Splitter] = NSAccessibilitySplitGroupRole; roleMap[QAccessible::List] = NSAccessibilityListRole; diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index 66c7727f15..bc98d002f0 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -91,6 +91,13 @@ // attributes ++ (id) lineNumberForIndex: (int)index forText:(const QString &)text +{ + QStringRef textBefore = QStringRef(&text, 0, index); + int newlines = textBefore.count(QLatin1Char('\n')); + return [NSNumber numberWithInt: newlines]; +} + - (NSArray *)accessibilityAttributeNames { static NSArray *defaultAttributes = nil; @@ -121,6 +128,19 @@ [attributes addObject : NSAccessibilityValueAttribute]; } + if (iface->textInterface()) { + [attributes addObjectsFromArray: [[NSArray alloc] initWithObjects: + NSAccessibilityNumberOfCharactersAttribute, + NSAccessibilitySelectedTextAttribute, + NSAccessibilitySelectedTextRangeAttribute, + NSAccessibilityVisibleCharacterRangeAttribute, + NSAccessibilityInsertionPointLineNumberAttribute, + nil + ]]; + +// TODO: multi-selection: NSAccessibilitySelectedTextRangesAttribute, + } + return [attributes autorelease]; } @@ -167,23 +187,150 @@ return nil; return QCocoaAccessible::getValueAttribute(iface); + + } else if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute]) { + if (QAccessibleTextInterface *text = iface->textInterface()) + return [NSNumber numberWithInt: text->characterCount()]; + return nil; + } else if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) { + if (QAccessibleTextInterface *text = iface->textInterface()) { + int start = 0; + int end = 0; + text->selection(0, &start, &end); + return text->text(start, end).toNSString(); + } + return nil; + } else if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { + if (QAccessibleTextInterface *text = iface->textInterface()) { + int start = 0; + int end = 0; + if (text->selectionCount() > 0) { + text->selection(0, &start, &end); + } else { + start = text->cursorPosition(); + end = start; + } + return [NSValue valueWithRange:NSMakeRange(quint32(start), quint32(end - start))]; + } + return [NSValue valueWithRange: NSMakeRange(0, 0)]; + } else if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) { + // FIXME This is not correct and may impact performance for big texts + return [NSValue valueWithRange: NSMakeRange(0, iface->textInterface()->characterCount())]; + + } else if ([attribute isEqualToString:NSAccessibilityInsertionPointLineNumberAttribute]) { + if (QAccessibleTextInterface *text = iface->textInterface()) { + QString textBeforeCursor = text->text(0, text->cursorPosition()); + return [NSNumber numberWithInt: textBeforeCursor.count(QLatin1Char('\n'))]; + } + return nil; + } + + return nil; +} + +- (NSArray *)accessibilityParameterizedAttributeNames { + + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface) { + qWarning() << "Called attribute on invalid object: " << axid; + return nil; + } + + if (iface->textInterface()) { + return [[NSArray alloc] initWithObjects: + NSAccessibilityStringForRangeParameterizedAttribute, + NSAccessibilityLineForIndexParameterizedAttribute, + NSAccessibilityRangeForLineParameterizedAttribute, + NSAccessibilityRangeForPositionParameterizedAttribute, +// NSAccessibilityRangeForIndexParameterizedAttribute, + NSAccessibilityBoundsForRangeParameterizedAttribute, +// NSAccessibilityRTFForRangeParameterizedAttribute, +// NSAccessibilityStyleRangeForIndexParameterizedAttribute, + NSAccessibilityAttributedStringForRangeParameterizedAttribute, + nil + ]; + } + + return nil; +} + +- (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface) { + qWarning() << "Called attribute on invalid object: " << axid; + return nil; } + if (!iface->textInterface()) + return nil; + + if ([attribute isEqualToString: NSAccessibilityStringForRangeParameterizedAttribute]) { + NSRange range = [parameter rangeValue]; + QString text = iface->textInterface()->text(range.location, range.location + range.length); + return text.toNSString(); + } + if ([attribute isEqualToString: NSAccessibilityLineForIndexParameterizedAttribute]) { + int index = [parameter intValue]; + NSNumber *ln = [QCocoaAccessibleElement lineNumberForIndex: index forText: iface->text(QAccessible::Value)]; + return ln; + } + if ([attribute isEqualToString: NSAccessibilityRangeForLineParameterizedAttribute]) { + int lineNumber = [parameter intValue]; + QString text = iface->text(QAccessible::Value); + int startOffset = 0; + // skip newlines until we have the one we look for + for (int i = 0; i < lineNumber; ++i) + startOffset = text.indexOf(QLatin1Char('\n'), startOffset) + 1; + if (startOffset < 0) // invalid line number, return the first line + startOffset = 0; + int endOffset = text.indexOf(QLatin1Char('\n'), startOffset + 1); + if (endOffset == -1) + endOffset = text.length(); + return [NSValue valueWithRange:NSMakeRange(quint32(startOffset), quint32(endOffset - startOffset))]; + } + if ([attribute isEqualToString: NSAccessibilityBoundsForRangeParameterizedAttribute]) { + NSRange range = [parameter rangeValue]; + QRect firstRect = iface->textInterface()->characterRect(range.location); + QRect lastRect = iface->textInterface()->characterRect(range.location + range.length); + QRect rect = firstRect.united(lastRect); // This is off quite often, but at least a rough approximation + return [NSValue valueWithRect: NSMakeRect((CGFloat) rect.x(),(CGFloat) qt_mac_flipYCoordinate(rect.y() + rect.height()), rect.width(), rect.height())]; + } + if ([attribute isEqualToString: NSAccessibilityAttributedStringForRangeParameterizedAttribute]) { + NSRange range = [parameter rangeValue]; + QString text = iface->textInterface()->text(range.location, range.location + range.length); + return [[NSAttributedString alloc] initWithString: text.toNSString()]; + } return nil; } - (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute { + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface) + return nil; + if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { - return NO; // YES to handle keyboard input - } else { - return NO; + return iface->state().focusable ? YES : NO; + } else if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { + return iface->textInterface() ? YES : NO; } + return NO; } - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute { - Q_UNUSED(value); + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + if (!iface) + return; if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { - + if (QAccessibleActionInterface *action = iface->actionInterface()) + action->doAction(QAccessibleActionInterface::setFocusAction()); + } else if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) { + if (QAccessibleTextInterface *text = iface->textInterface()) { + NSRange range = [value rangeValue]; + if (range.length > 0) + text->setSelection(0, range.location, range.location + range.length); + else + text->setCursorPosition(range.location); + } } } diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index 327ca00ad6..f8411845dc 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -223,8 +223,9 @@ static void cleanupCocoaApplicationDelegate() // events while the event loop is still running. const QWindowList topLevels = QGuiApplication::topLevelWindows(); for (int i = 0; i < topLevels.size(); ++i) { - topLevels.at(i)->close(); + QWindowSystemInterface::handleCloseEvent(topLevels.at(i)); } + QWindowSystemInterface::flushWindowSystemEvents(); QGuiApplication::exit(0); startedQuit = false; diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index ce8ebbc79e..a33373c4c1 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -59,6 +59,7 @@ public: QPaintDevice *paintDevice(); void flush(QWindow *widget, const QRegion ®ion, const QPoint &offset); + QImage toImage() const Q_DECL_OVERRIDE; void resize (const QSize &size, const QRegion &); bool scroll(const QRegion &area, int dx, int dy); CGImageRef getBackingStoreCGImage(); diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 3ca611b537..d76645c668 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -59,25 +59,20 @@ QCocoaBackingStore::~QCocoaBackingStore() QPaintDevice *QCocoaBackingStore::paintDevice() { - if (m_qImage.size() / m_qImage.devicePixelRatio() != m_requestedSize) { + QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window()->handle()); + int windowDevicePixelRatio = int(cocoaWindow->devicePixelRatio()); + + // Receate the backing store buffer if the effective buffer size has changed, + // either due to a window resize or devicePixelRatio change. + QSize effectiveBufferSize = m_requestedSize * windowDevicePixelRatio; + if (m_qImage.size() != effectiveBufferSize) { CGImageRelease(m_cgImage); m_cgImage = 0; - int scaleFactor = 1; -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { - QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window()->handle()); - if (cocoaWindow && cocoaWindow->m_contentView && [cocoaWindow->m_contentView window]) { - scaleFactor = int([[cocoaWindow->m_contentView window] backingScaleFactor]); - } - } -#endif - - QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window()->handle()); QImage::Format format = (window()->format().hasAlpha() || cocoaWindow->m_drawContentBorderGradient) ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; - m_qImage = QImage(m_requestedSize * scaleFactor, format); - m_qImage.setDevicePixelRatio(scaleFactor); + m_qImage = QImage(effectiveBufferSize, format); + m_qImage.setDevicePixelRatio(windowDevicePixelRatio); if (format == QImage::Format_ARGB32_Premultiplied) m_qImage.fill(Qt::transparent); } @@ -101,6 +96,11 @@ void QCocoaBackingStore::flush(QWindow *win, const QRegion ®ion, const QPoint } } +QImage QCocoaBackingStore::toImage() const +{ + return m_qImage; +} + void QCocoaBackingStore::resize(const QSize &size, const QRegion &) { m_requestedSize = size; diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm index 13f6423701..592bfc8e50 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.mm +++ b/src/plugins/platforms/cocoa/qcocoacursor.mm @@ -308,8 +308,22 @@ NSCursor *QCocoaCursor::createCursorFromBitmap(const QBitmap *bitmap, const QBit NSCursor *QCocoaCursor::createCursorFromPixmap(const QPixmap pixmap, const QPoint hotspot) { - NSPoint hotSpot = NSMakePoint(hotspot.x(), hotspot.y()); - NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap)); + NSPoint hotSpot = NSMakePoint(hotspot.x() / pixmap.devicePixelRatio(), + hotspot.y() / pixmap.devicePixelRatio()); + NSImage *nsimage; + if (pixmap.devicePixelRatio() > 1.0) { + QSize layoutSize = pixmap.size() / pixmap.devicePixelRatio(); + QPixmap scaledPixmap = pixmap.scaled(layoutSize); + nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(scaledPixmap)); + CGImageRef cgImage = qt_mac_toCGImage(pixmap.toImage()); + NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage]; + [nsimage addRepresentation:imageRep]; + [imageRep release]; + CGImageRelease(cgImage); + } else { + nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(pixmap)); + } + NSCursor *nsCursor = [[NSCursor alloc] initWithImage:nsimage hotSpot: hotSpot]; [nsimage release]; return nsCursor; diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index 9b4d8fd96f..3f61bd81ee 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -194,7 +194,7 @@ void QCocoaGLContext::swapBuffers(QPlatformSurface *surface) bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface) { - Q_ASSERT(surface->surface()->surfaceType() == QSurface::OpenGLSurface); + Q_ASSERT(surface->surface()->supportsOpenGL()); QCocoaAutoReleasePool pool; diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 8975605e5c..a73944c07c 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -102,21 +102,31 @@ CGImageRef qt_mac_toCGImage(const QImage &inImage) uint cgflags = kCGImageAlphaNone; switch (image.format()) { case QImage::Format_ARGB32_Premultiplied: - cgflags = kCGImageAlphaPremultipliedFirst; + cgflags = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; break; case QImage::Format_ARGB32: - cgflags = kCGImageAlphaFirst; + cgflags = kCGImageAlphaFirst | kCGBitmapByteOrder32Host; break; case QImage::Format_RGB32: - cgflags = kCGImageAlphaNoneSkipFirst; + cgflags = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; break; case QImage::Format_RGB888: - cgflags |= kCGImageAlphaNone; - break; + cgflags = kCGImageAlphaNone | kCGBitmapByteOrder32Big; + break; + case QImage::Format_RGBA8888_Premultiplied: + cgflags = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big; + break; + case QImage::Format_RGBA8888: + cgflags = kCGImageAlphaLast | kCGBitmapByteOrder32Big; + break; + case QImage::Format_RGBX8888: + cgflags = kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big; + break; default: + Q_ASSERT(false); // Should never be reached. break; } - cgflags |= kCGBitmapByteOrder32Host; + QCFType<CGDataProviderRef> dataProvider = qt_mac_CGDataProvider(image); return CGImageCreate(image.width(), image.height(), 8, 32, image.bytesPerLine(), diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index b1b73e5f08..24adc7a95b 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -129,6 +129,7 @@ public: QCocoaServices *services() const; QVariant styleHint(StyleHint hint) const; + Qt::KeyboardModifiers queryKeyboardModifiers() const; QList<int> possibleKeys(const QKeyEvent *event) const; void updateScreens(); diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 1892a5b6bf..65a9f87e2d 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -375,6 +375,7 @@ bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) cons case WindowMasks: case MultipleWindows: case ForeignWindows: + case RasterGLSurface: return true; default: return QPlatformIntegration::hasCapability(cap); @@ -464,6 +465,11 @@ QVariant QCocoaIntegration::styleHint(StyleHint hint) const return QPlatformIntegration::styleHint(hint); } +Qt::KeyboardModifiers QCocoaIntegration::queryKeyboardModifiers() const +{ + return QCocoaKeyMapper::queryKeyboardModifiers(); +} + QList<int> QCocoaIntegration::possibleKeys(const QKeyEvent *event) const { return mKeyboardMapper->possibleKeys(event); diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.h b/src/plugins/platforms/cocoa/qcocoakeymapper.h index 0629de9317..4f419b3651 100644 --- a/src/plugins/platforms/cocoa/qcocoakeymapper.h +++ b/src/plugins/platforms/cocoa/qcocoakeymapper.h @@ -83,6 +83,7 @@ class QCocoaKeyMapper public: QCocoaKeyMapper(); ~QCocoaKeyMapper(); + static Qt::KeyboardModifiers queryKeyboardModifiers(); QList<int> possibleKeys(const QKeyEvent *event) const; bool updateKeyboard(); void deleteLayouts(); diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.mm b/src/plugins/platforms/cocoa/qcocoakeymapper.mm index 0745cc2254..e46eaff6be 100644 --- a/src/plugins/platforms/cocoa/qcocoakeymapper.mm +++ b/src/plugins/platforms/cocoa/qcocoakeymapper.mm @@ -346,6 +346,11 @@ QCocoaKeyMapper::~QCocoaKeyMapper() deleteLayouts(); } +Qt::KeyboardModifiers QCocoaKeyMapper::queryKeyboardModifiers() +{ + return qt_mac_get_modifiers(GetCurrentEventKeyModifiers()); +} + bool QCocoaKeyMapper::updateKeyboard() { const UCKeyboardLayout *uchrData = 0; diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index 329c7a264a..35e8fdebb4 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -127,6 +127,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaMenuDelegate); { QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([item tag]); QScopedLoopLevelCounter loopLevelCounter(QGuiApplicationPrivate::instance()->threadData); + QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers:[NSEvent modifierFlags]]; cocoaItem->activated(); } @@ -268,7 +269,7 @@ void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem * QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem); QCocoaMenuItem *beforeItem = static_cast<QCocoaMenuItem *>(before); - menuItem->setParent(this); + SET_COCOA_MENU_ANCESTOR(menuItem, this); cocoaItem->sync(); if (beforeItem) { int index = m_menuItems.indexOf(beforeItem); @@ -325,8 +326,8 @@ void QCocoaMenu::removeMenuItem(QPlatformMenuItem *menuItem) return; } - if (menuItem->parent() == this) - menuItem->setParent(0); + if (COCOA_MENU_ANCESTOR(menuItem) == this) + SET_COCOA_MENU_ANCESTOR(menuItem, 0); m_menuItems.removeOne(cocoaItem); if (!cocoaItem->isMerged()) { @@ -550,7 +551,7 @@ void QCocoaMenu::syncModalState(bool modal) void QCocoaMenu::setMenuBar(QCocoaMenuBar *menuBar) { m_menuBar = menuBar; - setParent(menuBar); + SET_COCOA_MENU_ANCESTOR(this, menuBar); } QCocoaMenuBar *QCocoaMenu::menuBar() const diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.h b/src/plugins/platforms/cocoa/qcocoamenuitem.h index 1e69ed5a4b..b0169b9746 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.h +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.h @@ -118,6 +118,9 @@ private: quintptr m_tag; }; +#define COCOA_MENU_ANCESTOR(m) ((m)->property("_qCocoaMenuAncestor").value<QObject *>()) +#define SET_COCOA_MENU_ANCESTOR(m, ancestor) (m)->setProperty("_qCocoaMenuAncestor", QVariant::fromValue<QObject *>(ancestor)) + QT_END_NAMESPACE #endif diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index 3bba1ee1d5..2246d2ce46 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -126,13 +126,13 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu) { if (menu == m_menu) return; - if (m_menu && m_menu->parent() == this) - m_menu->setParent(0); + if (m_menu && COCOA_MENU_ANCESTOR(m_menu) == this) + SET_COCOA_MENU_ANCESTOR(m_menu, 0); QCocoaAutoReleasePool pool; m_menu = static_cast<QCocoaMenu *>(menu); if (m_menu) { - m_menu->setParent(this); + SET_COCOA_MENU_ANCESTOR(m_menu, this); } else { // we previously had a menu, but no longer // clear out our item so the nexy sync() call builds a new one @@ -217,12 +217,12 @@ NSMenuItem *QCocoaMenuItem::sync() mergeItem = [loader preferencesMenuItem]; break; case TextHeuristicRole: { - QObject *p = parent(); + QObject *p = COCOA_MENU_ANCESTOR(this); int depth = 1; QCocoaMenuBar *menubar = 0; while (depth < 3 && p && !(menubar = qobject_cast<QCocoaMenuBar *>(p))) { ++depth; - p = p->parent(); + p = COCOA_MENU_ANCESTOR(p); } if (depth == 3 || !menubar) break; // Menu item too deep in the hierarchy, or not connected to any menubar diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.h b/src/plugins/platforms/cocoa/qcocoanativeinterface.h index bf7e85619a..efdd433d8f 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.h +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.h @@ -136,10 +136,19 @@ private: // Request a unified title and toolbar look for the window. static void setContentBorderThickness(QWindow *window, int topThickness, int bottomThickness); + // Request a unified title and toolbar look for the window by registering + // an area. Multiple callers can register areas and the platform plugin + // will extend the "unified" area to cover them. + static void registerContentBorderArea(QWindow *window, quintptr identifer, int upper, int lower); + + // Enable the unified title and toolbar area. + static void enableContentBorderArea(QWindow *window, bool enable); + // Sets a NSToolbar instance for the given QWindow. The // toolbar will be attached to the native NSWindow when // that is created; static void setNSToolbar(QWindow *window, void *nsToolbar); + }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm index 5e57200ebc..d6a5be8d52 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm @@ -125,6 +125,10 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QCocoaNativeInter return NativeResourceForIntegrationFunction(QCocoaNativeInterface::setEmbeddedInForeignView); if (resource.toLower() == "setcontentborderthickness") return NativeResourceForIntegrationFunction(QCocoaNativeInterface::setContentBorderThickness); + if (resource.toLower() == "registercontentborderarea") + return NativeResourceForIntegrationFunction(QCocoaNativeInterface::registerContentBorderArea); + if (resource.toLower() == "enablecontentborderarea") + return NativeResourceForIntegrationFunction(QCocoaNativeInterface::enableContentBorderArea); if (resource.toLower() == "setnstoolbar") return NativeResourceForIntegrationFunction(QCocoaNativeInterface::setNSToolbar); @@ -287,11 +291,28 @@ void QCocoaNativeInterface::setContentBorderThickness(QWindow *window, int topTh cocoaWindow->setContentBorderThickness(topThickness, bottomThickness); } -void QCocoaNativeInterface::setNSToolbar(QWindow *window, void *nsToolbar) +void QCocoaNativeInterface::registerContentBorderArea(QWindow *window, quintptr identifier, int upper, int lower) { if (!window) return; + QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()); + if (cocoaWindow) + cocoaWindow->registerContentBorderArea(identifier, upper, lower); +} + +void QCocoaNativeInterface::enableContentBorderArea(QWindow *window, bool enable) +{ + if (!window) + return; + + QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()); + if (cocoaWindow) + cocoaWindow->enableContentBorderArea(enable); +} + +void QCocoaNativeInterface::setNSToolbar(QWindow *window, void *nsToolbar) +{ QCocoaIntegration::instance()->setToolbar(window, static_cast<NSToolbar *>(nsToolbar)); QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()); diff --git a/src/plugins/platforms/cocoa/qcocoaprintdevice.h b/src/plugins/platforms/cocoa/qcocoaprintdevice.h new file mode 100644 index 0000000000..30a2155bc7 --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoaprintdevice.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2014 John Layt <jlayt@kde.org> +** 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 QCOCOAPRINTDEVICE_H +#define QCOCOAPRINTDEVICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of internal files. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include <qpa/qplatformprintdevice.h> + +#ifndef QT_NO_PRINTER + +#include "qt_mac_p.h" + +#include <cups/ppd.h> + +QT_BEGIN_NAMESPACE + +class QCocoaPrintDevice : public QPlatformPrintDevice +{ +public: + QCocoaPrintDevice(); + explicit QCocoaPrintDevice(const QString &id); + QCocoaPrintDevice(const QCocoaPrintDevice &other); + virtual ~QCocoaPrintDevice(); + + QCocoaPrintDevice *clone(); + + bool operator==(const QCocoaPrintDevice &other) const; + + bool isValid() const Q_DECL_OVERRIDE; + bool isDefault() const Q_DECL_OVERRIDE; + + QPrint::DeviceState state() const Q_DECL_OVERRIDE; + + QPageSize defaultPageSize() const Q_DECL_OVERRIDE; + + QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation, + int resolution) const Q_DECL_OVERRIDE; + + int defaultResolution() const Q_DECL_OVERRIDE; + + QPrint::InputSlot defaultInputSlot() const Q_DECL_OVERRIDE; + + QPrint::OutputBin defaultOutputBin() const Q_DECL_OVERRIDE; + + QPrint::DuplexMode defaultDuplexMode() const Q_DECL_OVERRIDE; + + QPrint::ColorMode defaultColorMode() const Q_DECL_OVERRIDE; + + PMPrinter macPrinter() const; + PMPaper macPaper(const QPageSize &pageSize) const; + +protected: + void loadPageSizes() const Q_DECL_OVERRIDE; + void loadResolutions() const Q_DECL_OVERRIDE; + void loadInputSlots() const Q_DECL_OVERRIDE; + void loadOutputBins() const Q_DECL_OVERRIDE; + void loadDuplexModes() const Q_DECL_OVERRIDE; + void loadColorModes() const Q_DECL_OVERRIDE; + void loadMimeTypes() const Q_DECL_OVERRIDE; + +private: + QPageSize createPageSize(const PMPaper &paper) const; + bool openPpdFile(); + + // Mac Core Printing + PMPrinter m_printer; + PMPrintSession m_session; + mutable QHash<QString, PMPaper> m_macPapers; + + // PPD File + ppd_file_t *m_ppd; + + QMarginsF m_customMargins; + mutable QHash<QString, QMarginsF> m_printableMargins; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER +#endif // QCOCOAPRINTDEVICE_H diff --git a/src/plugins/platforms/cocoa/qcocoaprintdevice.mm b/src/plugins/platforms/cocoa/qcocoaprintdevice.mm new file mode 100644 index 0000000000..3061e84470 --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoaprintdevice.mm @@ -0,0 +1,502 @@ +/**************************************************************************** +** +** Copyright (C) 2014 John Layt <jlayt@kde.org> +** 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 "qcocoaprintdevice.h" + +#include <QtCore/qmimedatabase.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PRINTER + +static QPrint::DuplexMode macToDuplexMode(const PMDuplexMode &mode) +{ + if (mode == kPMDuplexTumble) + return QPrint::DuplexShortSide; + else if (mode == kPMDuplexNoTumble) + return QPrint::DuplexLongSide; + else // kPMDuplexNone or kPMSimplexTumble + return QPrint::DuplexNone; +} + +QCocoaPrintDevice::QCocoaPrintDevice() + : QPlatformPrintDevice(), + m_printer(0), + m_session(0), + m_ppd(0) +{ +} + +QCocoaPrintDevice::QCocoaPrintDevice(const QString &id) + : QPlatformPrintDevice(id), + m_printer(0), + m_session(0), + m_ppd(0) +{ + if (!id.isEmpty()) { + m_printer = PMPrinterCreateFromPrinterID(QCFString::toCFStringRef(id)); + if (m_printer) { + m_name = QCFString::toQString(PMPrinterGetName(m_printer)); + m_location = QCFString::toQString(PMPrinterGetLocation(m_printer)); + CFStringRef cfMakeAndModel; + if (PMPrinterGetMakeAndModelName(m_printer, &cfMakeAndModel) == noErr) + m_makeAndModel = QCFString::toQString(cfMakeAndModel); + Boolean isRemote; + if (PMPrinterIsRemote(m_printer, &isRemote) == noErr) + m_isRemote = isRemote; + if (PMCreateSession(&m_session) == noErr) + PMSessionSetCurrentPMPrinter(m_session, m_printer); + + // No native api to query these options, need to use PPD directly, note is deprecated from 1.6 onwards + if (openPpdFile()) { + // Note this is if the hardware does multiple copies, not if Cups can + m_supportsMultipleCopies = !m_ppd->manual_copies; + // Note this is if the hardware does collation, not if Cups can + ppd_option_t *collate = ppdFindOption(m_ppd, "Collate"); + if (collate) + m_supportsCollateCopies = true; + m_supportsCustomPageSizes = m_ppd->custom_max[0] > 0 && m_ppd->custom_max[1] > 0; + m_minimumPhysicalPageSize = QSize(m_ppd->custom_min[0], m_ppd->custom_min[1]); + m_maximumPhysicalPageSize = QSize(m_ppd->custom_max[0], m_ppd->custom_max[1]); + m_customMargins = QMarginsF(m_ppd->custom_margins[0], m_ppd->custom_margins[3], + m_ppd->custom_margins[2], m_ppd->custom_margins[1]); + } + } + } +} + +QCocoaPrintDevice::QCocoaPrintDevice(const QCocoaPrintDevice &other) + : QPlatformPrintDevice(other), + m_printer(0), + m_session(0), + m_ppd(0) +{ + m_printer = other.m_printer; + PMRetain(m_printer); + m_session = other.m_session; + PMRetain(m_session); + m_macPapers = other.m_macPapers; + foreach (PMPaper paper, m_macPapers.values()) + PMRetain(paper); + openPpdFile(); + m_customMargins = other.m_customMargins; + m_printableMargins = other.m_printableMargins; +} + +QCocoaPrintDevice::~QCocoaPrintDevice() +{ + if (m_ppd) + ppdClose(m_ppd); + foreach (PMPaper paper, m_macPapers.values()) + PMRelease(paper); + // Releasing the session appears to also release the printer + if (m_session) + PMRelease(m_session); + else if (m_printer) + PMRelease(m_printer); +} + +QCocoaPrintDevice *QCocoaPrintDevice::clone() +{ + return new QCocoaPrintDevice(*this); +} + +bool QCocoaPrintDevice::operator==(const QCocoaPrintDevice &other) const +{ + return (m_id == other.m_id); +} + +bool QCocoaPrintDevice::isValid() const +{ + return m_printer ? true : false; +} + +bool QCocoaPrintDevice::isDefault() const +{ + return PMPrinterIsDefault(m_printer); +} + +QPrint::DeviceState QCocoaPrintDevice::state() const +{ + PMPrinterState state; + if (PMPrinterGetState(m_printer, &state) == noErr) { + if (state == kPMPrinterIdle) + return QPrint::Idle; + else if (state == kPMPrinterProcessing) + return QPrint::Active; + else if (state == kPMPrinterStopped) + return QPrint::Error; + } + return QPrint::Error; +} + +QPageSize QCocoaPrintDevice::createPageSize(const PMPaper &paper) const +{ + QCFString key; + double width; + double height; + QCFString localizedName; + if (PMPaperGetPPDPaperName(paper, &key) == noErr + && PMPaperGetWidth(paper, &width) == noErr + && PMPaperGetHeight(paper, &height) == noErr + && PMPaperCreateLocalizedName(paper, m_printer, &localizedName) == noErr) { + return(QPlatformPrintDevice::createPageSize(key, QSize(width, height), localizedName)); + } + return QPageSize(); +} + +void QCocoaPrintDevice::loadPageSizes() const +{ + m_pageSizes.clear(); + foreach (PMPaper paper, m_macPapers.values()) + PMRelease(paper); + m_macPapers.clear(); + m_printableMargins.clear(); + CFArrayRef paperSizes; + if (PMPrinterGetPaperList(m_printer, &paperSizes) == noErr) { + int count = CFArrayGetCount(paperSizes); + for (int i = 0; i < count; ++i) { + PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(paperSizes, i))); + QPageSize pageSize = createPageSize(paper); + if (pageSize.isValid()) { + m_pageSizes.append(pageSize); + PMRetain(paper); + m_macPapers.insert(pageSize.key(), paper); + PMPaperMargins printMargins; + PMPaperGetMargins(paper, &printMargins); + m_printableMargins.insert(pageSize.key(), QMarginsF(printMargins.left, printMargins.top, + printMargins.right, printMargins.bottom)); + } + } + } + m_havePageSizes = true; +} + +QPageSize QCocoaPrintDevice::defaultPageSize() const +{ + QPageSize pageSize; + PMPageFormat pageFormat; + PMPaper paper; + if (PMCreatePageFormat(&pageFormat) == noErr + && PMSessionDefaultPageFormat(m_session, pageFormat) == noErr + && PMGetPageFormatPaper(pageFormat, &paper) == noErr) { + pageSize = createPageSize(paper); + PMRelease(pageFormat); + } + return pageSize; +} + +QMarginsF QCocoaPrintDevice::printableMargins(const QPageSize &pageSize, + QPageLayout::Orientation orientation, + int resolution) const +{ + Q_UNUSED(orientation) + Q_UNUSED(resolution) + if (!m_havePageSizes) + loadPageSizes(); + if (m_printableMargins.contains(pageSize.key())) + return m_printableMargins.value(pageSize.key()); + return m_customMargins; +} + +void QCocoaPrintDevice::loadResolutions() const +{ + m_resolutions.clear(); + UInt32 count; + if (PMPrinterGetPrinterResolutionCount(m_printer, &count) == noErr) { + // 1-based index + for (UInt32 i = 1; i <= count; ++i) { + PMResolution resolution; + if (PMPrinterGetIndexedPrinterResolution(m_printer, i, &resolution) == noErr) + m_resolutions.append(int(resolution.hRes)); + } + } + m_haveResolutions = true; +} + +int QCocoaPrintDevice::defaultResolution() const +{ + int defaultResolution = 72; + PMPrintSettings settings; + if (PMCreatePrintSettings(&settings) == noErr) { + PMResolution resolution; + if (PMSessionDefaultPrintSettings(m_session, settings) == noErr + && PMPrinterGetOutputResolution(m_printer, settings, &resolution) == noErr) { + // PMPrinterGetOutputResolution usually fails with -9589 kPMKeyNotFound as not set in PPD + defaultResolution = int(resolution.hRes); + } + PMRelease(settings); + } + // If no value returned (usually means not set in PPD) then use supported resolutions which + // OSX will have populated with at least one default value (but why not returned by call?) + if (defaultResolution <= 0) { + if (!m_haveResolutions) + loadResolutions(); + if (m_resolutions.count() > 0) + return m_resolutions.at(0); // First value or highest? Only likely to be one anyway. + return 72; // TDOD More sensible default value??? + } + return defaultResolution; +} + +void QCocoaPrintDevice::loadInputSlots() const +{ + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // TODO Deal with concatenated names like Tray1Manual or Tray1_Man, + // will currently show as CustomInputSlot + // TODO Deal with separate ManualFeed key + // Try load standard PPD options first + m_inputSlots.clear(); + if (m_ppd) { + ppd_option_t *inputSlots = ppdFindOption(m_ppd, "InputSlot"); + if (inputSlots) { + for (int i = 0; i < inputSlots->num_choices; ++i) + m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[i])); + } + // If no result, try just the default + if (m_inputSlots.size() == 0) { + inputSlots = ppdFindOption(m_ppd, "DefaultInputSlot"); + if (inputSlots) + m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[0])); + } + } + // If still no result, just use Auto + if (m_inputSlots.size() == 0) + m_inputSlots.append(QPlatformPrintDevice::defaultInputSlot()); + m_haveInputSlots = true; +} + +QPrint::InputSlot QCocoaPrintDevice::defaultInputSlot() const +{ + // No native api to query, use PPD directly + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Try load standard PPD option first + if (m_ppd) { + ppd_option_t *inputSlot = ppdFindOption(m_ppd, "DefaultInputSlot"); + if (inputSlot) + return QPrintUtils::ppdChoiceToInputSlot(inputSlot->choices[0]); + // If no result, then try a marked option + ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "InputSlot"); + if (defaultChoice) + return QPrintUtils::ppdChoiceToInputSlot(*defaultChoice); + } + // Otherwise return Auto + return QPlatformPrintDevice::defaultInputSlot(); +} + +void QCocoaPrintDevice::loadOutputBins() const +{ + // No native api to query, use PPD directly + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + m_outputBins.clear(); + if (m_ppd) { + ppd_option_t *outputBins = ppdFindOption(m_ppd, "OutputBin"); + if (outputBins) { + for (int i = 0; i < outputBins->num_choices; ++i) + m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[i])); + } + // If no result, try just the default + if (m_outputBins.size() == 0) { + outputBins = ppdFindOption(m_ppd, "DefaultOutputBin"); + if (outputBins) + m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[0])); + } + } + // If still no result, just use Auto + if (m_outputBins.size() == 0) + m_outputBins.append(QPlatformPrintDevice::defaultOutputBin()); + m_haveOutputBins = true; +} + +QPrint::OutputBin QCocoaPrintDevice::defaultOutputBin() const +{ + // No native api to query, use PPD directly + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Try load standard PPD option first + if (m_ppd) { + ppd_option_t *outputBin = ppdFindOption(m_ppd, "DefaultOutputBin"); + if (outputBin) + return QPrintUtils::ppdChoiceToOutputBin(outputBin->choices[0]); + // If no result, then try a marked option + ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "OutputBin"); + if (defaultChoice) + return QPrintUtils::ppdChoiceToOutputBin(*defaultChoice); + } + // Otherwise return AutoBin + return QPlatformPrintDevice::defaultOutputBin(); +} + +void QCocoaPrintDevice::loadDuplexModes() const +{ + // No native api to query, use PPD directly + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Try load standard PPD options first + m_duplexModes.clear(); + if (m_ppd) { + ppd_option_t *duplexModes = ppdFindOption(m_ppd, "Duplex"); + if (duplexModes) { + for (int i = 0; i < duplexModes->num_choices; ++i) + m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[i].choice)); + } + // If no result, try just the default + if (m_duplexModes.size() == 0) { + duplexModes = ppdFindOption(m_ppd, "DefaultDuplex"); + if (duplexModes) + m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[0].choice)); + } + } + // If still no result, or not added in PPD, then add None + if (m_duplexModes.size() == 0 || !m_duplexModes.contains(QPrint::DuplexNone)) + m_duplexModes.append(QPrint::DuplexNone); + m_haveDuplexModes = true; +} + +QPrint::DuplexMode QCocoaPrintDevice::defaultDuplexMode() const +{ + QPrint::DuplexMode defaultMode = QPrint::DuplexNone; + PMPrintSettings settings; + if (PMCreatePrintSettings(&settings) == noErr) { + PMDuplexMode duplexMode; + if (PMSessionDefaultPrintSettings(m_session, settings) == noErr + && PMGetDuplex(settings, &duplexMode) == noErr) { + defaultMode = macToDuplexMode(duplexMode); + } + PMRelease(settings); + } + return defaultMode; +} + +void QCocoaPrintDevice::loadColorModes() const +{ + // No native api to query, use PPD directly + m_colorModes.clear(); + m_colorModes.append(QPrint::GrayScale); + if (!m_ppd || (m_ppd && m_ppd->color_device)) + m_colorModes.append(QPrint::Color); + m_haveColorModes = true; +} + +QPrint::ColorMode QCocoaPrintDevice::defaultColorMode() const +{ + // No native api to query, use PPD directly + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Not a proper option, usually only know if supports color or not, but some + // users known to abuse ColorModel to always force GrayScale. + if (m_ppd && supportedColorModes().contains(QPrint::Color)) { + ppd_option_t *colorModel = ppdFindOption(m_ppd, "DefaultColorModel"); + if (!colorModel) + colorModel = ppdFindOption(m_ppd, "ColorModel"); + if (!colorModel || (colorModel && !qstrcmp(colorModel->defchoice, "Gray"))) + return QPrint::Color; + } + return QPrint::GrayScale; +} + +void QCocoaPrintDevice::loadMimeTypes() const +{ + // TODO Check how settings affect returned list + m_mimeTypes.clear(); + QMimeDatabase db; + PMPrintSettings settings; + if (PMCreatePrintSettings(&settings) == noErr) { + CFArrayRef mimeTypes; + if (PMPrinterGetMimeTypes(m_printer, settings, &mimeTypes) == noErr) { + int count = CFArrayGetCount(mimeTypes); + for (int i = 0; i < count; ++i) { + CFStringRef mimeName = static_cast<CFStringRef>(const_cast<void *>(CFArrayGetValueAtIndex(mimeTypes, i))); + QMimeType mimeType = db.mimeTypeForName(QCFString::toQString(mimeName)); + if (mimeType.isValid()) + m_mimeTypes.append(mimeType); + } + } + PMRelease(settings); + } + m_haveMimeTypes = true; +} + +bool QCocoaPrintDevice::openPpdFile() +{ + if (m_ppd) + ppdClose(m_ppd); + m_ppd = 0; + CFURLRef ppdURL = NULL; + char ppdPath[MAXPATHLEN]; + if (PMPrinterCopyDescriptionURL(m_printer, kPMPPDDescriptionType, &ppdURL) == noErr + && ppdURL != NULL + && CFURLGetFileSystemRepresentation(ppdURL, true, (UInt8*)ppdPath, sizeof(ppdPath))) { + m_ppd = ppdOpenFile(ppdPath); + } + CFRelease(ppdURL); + return m_ppd ? true : false; +} + +PMPrinter QCocoaPrintDevice::macPrinter() const +{ + return m_printer; +} + +// Returns a cached printer PMPaper, or creates and caches a new custom PMPaper +// Caller should never release a cached PMPaper! +PMPaper QCocoaPrintDevice::macPaper(const QPageSize &pageSize) const +{ + if (!m_havePageSizes) + loadPageSizes(); + // If keys match, then is a supported size or an existing custom size + if (m_macPapers.contains(pageSize.key())) + return m_macPapers.value(pageSize.key()); + // For any other page size, whether custom or just unsupported, needs to be a custom PMPaper + PMPaper paper = 0; + PMPaperMargins paperMargins; + paperMargins.left = m_customMargins.left(); + paperMargins.right = m_customMargins.right(); + paperMargins.top = m_customMargins.top(); + paperMargins.bottom = m_customMargins.bottom(); + PMPaperCreateCustom(m_printer, QCFString(pageSize.key()), QCFString(pageSize.name()), + pageSize.sizePoints().width(), pageSize.sizePoints().height(), + &paperMargins, &paper); + m_macPapers.insert(pageSize.key(), paper); + return paper; +} + +#endif // QT_NO_PRINTER + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaprintersupport.h b/src/plugins/platforms/cocoa/qcocoaprintersupport.h index a48790ef34..61c1bfd3ec 100644 --- a/src/plugins/platforms/cocoa/qcocoaprintersupport.h +++ b/src/plugins/platforms/cocoa/qcocoaprintersupport.h @@ -55,14 +55,10 @@ 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; - -private: - QPrinterInfo printerInfoFromPMPrinter(const PMPrinter &printer); + QPrintDevice createPrintDevice(const QString &id) Q_DECL_OVERRIDE; + QStringList availablePrintDeviceIds() const Q_DECL_OVERRIDE; + QString defaultPrintDeviceId() const Q_DECL_OVERRIDE; }; #endif // QT_NO_PRINTER diff --git a/src/plugins/platforms/cocoa/qcocoaprintersupport.mm b/src/plugins/platforms/cocoa/qcocoaprintersupport.mm index cb2aa7132b..5853135dfb 100644 --- a/src/plugins/platforms/cocoa/qcocoaprintersupport.mm +++ b/src/plugins/platforms/cocoa/qcocoaprintersupport.mm @@ -42,10 +42,10 @@ #include "qcocoaprintersupport.h" #ifndef QT_NO_PRINTER + +#include "qcocoaprintdevice.h" #include "qprintengine_mac_p.h" -#include <QtPrintSupport/QPrinter> -#include <QtPrintSupport/QPrinterInfo> #include <private/qprinterinfo_p.h> QCocoaPrinterSupport::QCocoaPrinterSupport() @@ -69,107 +69,37 @@ QPaintEngine *QCocoaPrinterSupport::createPaintEngine(QPrintEngine *printEngine, return static_cast<QMacPrintEngine *>(printEngine); } -QList<QPrinter::PaperSize> QCocoaPrinterSupport::supportedPaperSizes(const QPrinterInfo &printerInfo) const +QPrintDevice QCocoaPrinterSupport::createPrintDevice(const QString &id) { - QList<QPrinter::PaperSize> 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; - } - - CFIndex count = CFArrayGetCount(array); - for (CFIndex 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) { - // width and height are in points, convertQSizeFToPaperSize() expects millimeters - static const double OnePointInMillimeters = 1.0 / 72.0 * 25.4; - QSizeF size(width * OnePointInMillimeters, height * OnePointInMillimeters); - returnValue += QPlatformPrinterSupport::convertQSizeFToPaperSize(size); - } - } - - PMRelease(printer); - - return returnValue; + return QPlatformPrinterSupport::createPrintDevice(new QCocoaPrintDevice(id)); } -QList<QPrinterInfo> QCocoaPrinterSupport::availablePrinters() +QStringList QCocoaPrinterSupport::availablePrintDeviceIds() const { - QList<QPrinterInfo> returnValue; + QStringList list; QCFType<CFArrayRef> printerList; if (PMServerCreatePrinterList(kPMServerLocal, &printerList) == noErr) { CFIndex count = CFArrayGetCount(printerList); for (CFIndex i = 0; i < count; ++i) { PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(printerList, i))); - returnValue += printerInfoFromPMPrinter(printer); + list.append(QCFString::toQString(PMPrinterGetID(printer))); } } - return returnValue; -} - -QPrinterInfo QCocoaPrinterSupport::printerInfo(const QString &printerName) -{ - PMPrinter printer = PMPrinterCreateFromPrinterID(QCFString::toCFStringRef(printerName)); - QPrinterInfo pi = printerInfoFromPMPrinter(printer); - PMRelease(printer); - return pi; -} - -QPrinterInfo QCocoaPrinterSupport::printerInfoFromPMPrinter(const PMPrinter &printer) -{ - if (!printer) - return QPrinterInfo(); - - QString name = QCFString::toQString(PMPrinterGetID(printer)); - QString description = QCFString::toQString(PMPrinterGetName(printer)); - QString location = QCFString::toQString(PMPrinterGetLocation(printer)); - CFStringRef cfMakeAndModel; - PMPrinterGetMakeAndModelName(printer, &cfMakeAndModel); - QString makeAndModel = QCFString::toQString(cfMakeAndModel); - bool isDefault = PMPrinterIsDefault(printer); - - return createPrinterInfo(name, description, location, makeAndModel, isDefault, 0); + return list; } -QList<QPair<QString, QSizeF> > QCocoaPrinterSupport::supportedSizesWithNames(const QPrinterInfo &printerInfo) const +QString QCocoaPrinterSupport::defaultPrintDeviceId() 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))); + QCFType<CFArrayRef> printerList; + if (PMServerCreatePrinterList(kPMServerLocal, &printerList) == noErr) { + CFIndex count = CFArrayGetCount(printerList); + for (CFIndex i = 0; i < count; ++i) { + PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(printerList, i))); + if (PMPrinterIsDefault(printer)) + return QCFString::toQString(PMPrinterGetID(printer)); } } - PMRelease(printer); - return returnValue; + return QString(); } #endif //QT_NO_PRINTER diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm index 1c08d4bcb7..f18be8b69c 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm @@ -161,7 +161,7 @@ static QMacPaletteMap mac_widget_colors[] = { QMacPaletteMap(QPlatformTheme::HeaderPalette, kThemeTextColorPushButtonActive, kThemeTextColorPushButtonInactive), QMacPaletteMap(QPlatformTheme::ComboBoxPalette, kThemeTextColorPopupButtonActive, kThemeTextColorPopupButtonInactive), QMacPaletteMap(QPlatformTheme::ItemViewPalette, kThemeTextColorListView, kThemeTextColorDialogInactive), - QMacPaletteMap(QPlatformTheme::MessageBoxLabelPelette, kThemeTextColorAlertActive, kThemeTextColorAlertInactive), + QMacPaletteMap(QPlatformTheme::MessageBoxLabelPalette, kThemeTextColorAlertActive, kThemeTextColorAlertInactive), QMacPaletteMap(QPlatformTheme::TabBarPalette, kThemeTextColorTabFrontActive, kThemeTextColorTabFrontInactive), QMacPaletteMap(QPlatformTheme::LabelPalette, kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive), QMacPaletteMap(QPlatformTheme::GroupBoxPalette, kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive), @@ -191,6 +191,8 @@ QHash<QPlatformTheme::Palette, QPalette*> qt_mac_createRolePalettes() pal.setColor(QPalette::Disabled, QPalette::HighlightedText, qc); } if (mac_widget_colors[i].paletteRole == QPlatformTheme::MenuPalette) { + qc = qt_mac_colorForTheme(kThemeBrushMenuBackground); + pal.setBrush(QPalette::Background, qc); qc = qt_mac_colorForThemeTextColor(kThemeTextColorMenuItemActive); pal.setBrush(QPalette::ButtonText, qc); qc = qt_mac_colorForThemeTextColor(kThemeTextColorMenuItemSelected); diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 67cac41383..6e1f00eebe 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -80,6 +80,7 @@ typedef NSWindow<QNSWindowProtocol> QCocoaNSWindow; - (id)initWithNSWindow:(QCocoaNSWindow *)window platformWindow:(QCocoaWindow *)platformWindow; - (void)handleWindowEvent:(NSEvent *)theEvent; +- (void) clearWindow; @end @@ -141,6 +142,7 @@ public: ~QCocoaWindow(); void setGeometry(const QRect &rect); + QRect geometry() const; void setCocoaGeometry(const QRect &rect); void clipChildWindows(); void clipWindow(const NSRect &clipRect); @@ -189,6 +191,7 @@ public: NSInteger windowLevel(Qt::WindowFlags flags); NSUInteger windowStyleMask(Qt::WindowFlags flags); void setWindowShadow(Qt::WindowFlags flags); + void setWindowZoomButton(Qt::WindowFlags flags); void setCurrentContext(QCocoaGLContext *context); QCocoaGLContext *currentContext() const; @@ -206,6 +209,8 @@ public: void registerTouch(bool enable); void setContentBorderThickness(int topThickness, int bottomThickness); + void registerContentBorderArea(quintptr identifier, int upper, int lower); + void enableContentBorderArea(bool enable); void applyContentBorderThickness(NSWindow *window); void updateNSToolbar(); @@ -280,6 +285,16 @@ public: // for QNSView QRect m_normalGeometry; Qt::WindowFlags m_oldWindowFlags; NSApplicationPresentationOptions m_presentationOptions; + + struct BorderRange { + BorderRange(int u, int l) : upper(u), lower(l) { } + int upper; + int lower; + bool operator<(BorderRange const& right) const { + return upper < right.upper; + } + }; + QHash<quintptr, BorderRange> m_contentBorderAreas; // identifer -> uppper/lower }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index f1f88a13dd..c7fba4eef0 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -155,7 +155,14 @@ static bool isMouseEvent(NSEvent *ev) } } + // The call to -[NSWindow sendEvent] may result in the window being deleted + // (e.g., when closing the window by pressing the title bar close button). + [self retain]; [self.window superSendEvent:theEvent]; + bool windowStillAlive = self.window != nil; // We need to read before releasing + [self release]; + if (!windowStillAlive) + return; if (!self.window.delegate) return; // Already detached, pending NSAppKitDefined event @@ -179,6 +186,11 @@ static bool isMouseEvent(NSEvent *ev) self.window.delegate = nil; } +- (void)clearWindow +{ + _window = nil; +} + - (void)dealloc { _window = nil; @@ -259,6 +271,7 @@ static bool isMouseEvent(NSEvent *ev) - (void)dealloc { + [_helper clearWindow]; [_helper release]; _helper = nil; [super dealloc]; @@ -319,6 +332,7 @@ static bool isMouseEvent(NSEvent *ev) - (void)dealloc { + [_helper clearWindow]; [_helper release]; _helper = nil; [super dealloc]; @@ -378,7 +392,7 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) // problem, except if the appilcation wants to have a "custom" viewport. // (like the hellogl example) if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7 - && tlw->surfaceType() == QSurface::OpenGLSurface) { + && tlw->supportsOpenGL()) { BOOL enable = qt_mac_resolveOption(YES, tlw, "_q_mac_wantsBestResolutionOpenGLSurface", "QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE"); [m_contentView setWantsBestResolutionOpenGLSurface:enable]; @@ -448,6 +462,22 @@ void QCocoaWindow::setGeometry(const QRect &rectIn) setCocoaGeometry(rect); } +QRect QCocoaWindow::geometry() const +{ + // QWindows that are embedded in a NSView hiearchy may be considered + // top-level from Qt's point of view but are not from Cocoa's point + // of view. Embedded QWindows get global (screen) geometry. + if (m_contentViewIsEmbedded) { + NSPoint windowPoint = [m_contentView convertPoint:NSMakePoint(0, 0) toView:nil]; + NSPoint screenPoint = [[m_contentView window] convertBaseToScreen:windowPoint]; // ### use convertRectToScreen after 10.6 removal + QPoint position = qt_mac_flipPoint(screenPoint).toPoint(); + QSize size = qt_mac_toQRect([m_contentView bounds]).size(); + return QRect(position, size); + } + + return QPlatformWindow::geometry(); +} + void QCocoaWindow::setCocoaGeometry(const QRect &rect) { QCocoaAutoReleasePool pool; @@ -791,6 +821,18 @@ void QCocoaWindow::setWindowShadow(Qt::WindowFlags flags) [m_nsWindow setHasShadow:(keepShadow ? YES : NO)]; } +void QCocoaWindow::setWindowZoomButton(Qt::WindowFlags flags) +{ + // Disable the zoom (maximize) button for fixed-sized windows and customized + // no-WindowMaximizeButtonHint windows. From a Qt perspective it migth be expected + // that the button would be removed in the latter case, but disabling it is more + // in line with the platform style guidelines. + bool fixedSizeNoZoom = (window()->minimumSize().isValid() && window()->maximumSize().isValid() + && window()->minimumSize() == window()->maximumSize()); + bool customizeNoZoom = ((flags & Qt::CustomizeWindowHint) && !(flags & Qt::WindowMaximizeButtonHint)); + [[m_nsWindow standardWindowButton:NSWindowZoomButton] setEnabled:!(fixedSizeNoZoom || customizeNoZoom)]; +} + void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags) { if (m_nsWindow && !m_isNSWindowChild) { @@ -816,6 +858,7 @@ void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags) } } #endif + setWindowZoomButton(flags); } m_windowFlags = flags; @@ -908,6 +951,9 @@ void QCocoaWindow::raise() [parentNSWindow addChildWindow:m_nsWindow ordered:NSWindowAbove]; } else { [m_nsWindow orderFront: m_nsWindow]; + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly); } } } @@ -979,6 +1025,9 @@ void QCocoaWindow::propagateSizeHints() const QSize maximumSize = window()->maximumSize(); [m_nsWindow setContentMaxSize : NSMakeSize(maximumSize.width(), maximumSize.height())]; + // The window may end up with a fixed size; in this case the zoom button should be disabled. + setWindowZoomButton(m_windowFlags); + // sizeIncrement is observed to take values of (-1, -1) and (0, 0) for windows that should be // resizable and that have no specific size increment set. Cocoa expects (1.0, 1.0) in this case. if (!window()->sizeIncrement().isEmpty()) @@ -1080,7 +1129,9 @@ NSWindow *QCocoaWindow::nativeWindow() const void QCocoaWindow::setEmbeddedInForeignView(bool embedded) { m_contentViewIsToBeEmbedded = embedded; - recreateWindow(0); // destroy what was already created + // Release any previosly created NSWindow. + [m_nsWindow closeAndRelease]; + m_nsWindow = 0; } void QCocoaWindow::windowWillMove() @@ -1505,15 +1556,46 @@ void QCocoaWindow::setContentBorderThickness(int topThickness, int bottomThickne applyContentBorderThickness(m_nsWindow); } +void QCocoaWindow::registerContentBorderArea(quintptr identifier, int upper, int lower) +{ + m_contentBorderAreas.insert(identifier, BorderRange(upper, lower)); + + // Find consecutive registered border areas, starting from the top. + QList<BorderRange> ranges = m_contentBorderAreas.values(); + std::sort(ranges.begin(), ranges.end()); + m_topContentBorderThickness = 0; + foreach (BorderRange range, ranges) { + // Is this sub-range adjacent to or overlaping the + // existing total border area range? If so merge + // it into the total range, + if (range.upper <= (m_topContentBorderThickness + 1)) + m_topContentBorderThickness = qMax(m_topContentBorderThickness, range.lower); + else + break; + } + + m_bottomContentBorderThickness = 0; // (not supported) + if (m_drawContentBorderGradient) + applyContentBorderThickness(m_nsWindow); +} + +void QCocoaWindow::enableContentBorderArea(bool enable) +{ + m_drawContentBorderGradient = enable; + applyContentBorderThickness(m_nsWindow); +} + void QCocoaWindow::applyContentBorderThickness(NSWindow *window) { if (!window) return; - if (m_drawContentBorderGradient) - [window setStyleMask:[window styleMask] | NSTexturedBackgroundWindowMask]; - else + if (!m_drawContentBorderGradient) { [window setStyleMask:[window styleMask] & ~NSTexturedBackgroundWindowMask]; + return; + } + + [window setStyleMask:[window styleMask] | NSTexturedBackgroundWindowMask]; if (m_topContentBorderThickness > 0) { [window setContentBorderThickness:m_topContentBorderThickness forEdge:NSMaxYEdge]; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 90d56bc3f3..47081ab890 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -562,9 +562,11 @@ static QTouchDevice *touchDevice = 0; QPointF qtWindowPoint; QPointF qtScreenPoint; QNSView *targetView = self; - if (m_platformWindow && m_platformWindow->m_forwardWindow - && (theEvent.type == NSLeftMouseDragged || theEvent.type == NSLeftMouseUp)) { - targetView = m_platformWindow->m_forwardWindow->m_qtView; + if (m_platformWindow && m_platformWindow->m_forwardWindow) { + if (theEvent.type == NSLeftMouseDragged || theEvent.type == NSLeftMouseUp) + targetView = m_platformWindow->m_forwardWindow->m_qtView; + else + m_platformWindow->m_forwardWindow = 0; } [targetView convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; @@ -650,7 +652,7 @@ static QTouchDevice *touchDevice = 0; { if (m_window->flags() & Qt::WindowTransparentForInput) return [super mouseDragged:theEvent]; - if (!(m_buttons & Qt::LeftButton)) + if (!(m_buttons & (m_sendUpAsRightButton ? Qt::RightButton : Qt::LeftButton))) qWarning("QNSView mouseDragged: Internal mouse button tracking invalid (missing Qt::LeftButton)"); [self handleMouseEvent:theEvent]; } @@ -1719,7 +1721,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) // keep our state, and QGuiApplication state (buttons member) in-sync, // or future mouse events will be processed incorrectly - m_buttons &= ~Qt::LeftButton; + m_buttons &= ~(m_sendUpAsRightButton ? Qt::RightButton : Qt::LeftButton); NSPoint windowPoint = [self convertPoint: point fromView: nil]; QPoint qtWindowPoint(windowPoint.x, windowPoint.y); diff --git a/src/plugins/platforms/cocoa/qpaintengine_mac.mm b/src/plugins/platforms/cocoa/qpaintengine_mac.mm index 61fbe3a61f..ef67d1166f 100644 --- a/src/plugins/platforms/cocoa/qpaintengine_mac.mm +++ b/src/plugins/platforms/cocoa/qpaintengine_mac.mm @@ -979,43 +979,6 @@ static void drawImageReleaseData (void *info, const void *, size_t) delete static_cast<QImage *>(info); } -CGImageRef qt_mac_createCGImageFromQImage(const QImage &img, const QImage **imagePtr = 0) -{ - QImage *image; - if (img.depth() != 32) - image = new QImage(img.convertToFormat(QImage::Format_ARGB32_Premultiplied)); - else - image = new QImage(img); - - uint cgflags = kCGImageAlphaNone; - switch (image->format()) { - case QImage::Format_ARGB32_Premultiplied: - cgflags = kCGImageAlphaPremultipliedFirst; - break; - case QImage::Format_ARGB32: - cgflags = kCGImageAlphaFirst; - break; - case QImage::Format_RGB32: - cgflags = kCGImageAlphaNoneSkipFirst; - default: - break; - } -#if defined(kCGBitmapByteOrder32Host) //only needed because CGImage.h added symbols in the minor version - cgflags |= kCGBitmapByteOrder32Host; -#endif - QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image, - static_cast<const QImage *>(image)->bits(), - image->byteCount(), - drawImageReleaseData); - if (imagePtr) - *imagePtr = image; - return CGImageCreate(image->width(), image->height(), 8, 32, - image->bytesPerLine(), - QCoreGraphicsPaintEngine::macGenericColorSpace(), - cgflags, dataProvider, 0, false, kCGRenderingIntentDefault); - -} - void QCoreGraphicsPaintEngine::drawImage(const QRectF &r, const QImage &img, const QRectF &sr, Qt::ImageConversionFlags flags) { @@ -1026,8 +989,7 @@ void QCoreGraphicsPaintEngine::drawImage(const QRectF &r, const QImage &img, con if (img.isNull() || state->compositionMode() == QPainter::CompositionMode_Destination) return; - const QImage *image; - QCFType<CGImageRef> cgimage = qt_mac_createCGImageFromQImage(img, &image); + QCFType<CGImageRef> cgimage = qt_mac_toCGImage(img); CGRect rect = CGRectMake(r.x(), r.y(), r.width(), r.height()); if (QRectF(0, 0, img.width(), img.height()) != sr) cgimage = CGImageCreateWithImageInRect(cgimage, CGRectMake(sr.x(), sr.y(), diff --git a/src/plugins/platforms/cocoa/qprintengine_mac.mm b/src/plugins/platforms/cocoa/qprintengine_mac.mm index 3e92a45a62..fb968f31e9 100644 --- a/src/plugins/platforms/cocoa/qprintengine_mac.mm +++ b/src/plugins/platforms/cocoa/qprintengine_mac.mm @@ -40,9 +40,11 @@ ****************************************************************************/ #include "qprintengine_mac_p.h" +#include "qcocoaprintersupport.h" #include <quuid.h> +#include <QtGui/qpagelayout.h> #include <QtCore/qcoreapplication.h> -#include <qpa/qplatformprintersupport.h> +#include <QtCore/qdebug.h> #include "qcocoaautoreleasepool.h" @@ -50,10 +52,14 @@ QT_BEGIN_NAMESPACE +extern QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits); + QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode) : QPaintEngine(*(new QMacPrintEnginePrivate)) { Q_D(QMacPrintEngine); d->mode = mode; + d->m_printDevice = new QCocoaPrintDevice(QCocoaPrinterSupport().defaultPrintDeviceId()); + d->m_pageLayout.setPageSize(d->m_printDevice->defaultPageSize()); d->initialize(); } @@ -137,126 +143,6 @@ QMacPrintEnginePrivate::~QMacPrintEnginePrivate() delete paintEngine; } -void QMacPrintEnginePrivate::setPaperSize(QPrinter::PaperSize ps) -{ - Q_Q(QMacPrintEngine); - if (hasCustomPaperSize) { - PMRelease(customPaper); - customPaper = 0; - } - hasCustomPaperSize = (ps == QPrinter::Custom); - PMPrinter printer; - if (PMSessionGetCurrentPrinter(session(), &printer) == noErr) { - if (ps != QPrinter::Custom) { - QSizeF newSize = QPlatformPrinterSupport::convertPaperSizeToQSizeF(ps); - QCFType<CFArrayRef> formats; - if (PMSessionCreatePageFormatList(session(), printer, &formats) == noErr) { - CFIndex total = CFArrayGetCount(formats); - PMPageFormat tmp; - PMRect paper; - for (CFIndex idx = 0; idx < total; ++idx) { - tmp = static_cast<PMPageFormat>(const_cast<void *>(CFArrayGetValueAtIndex(formats, idx))); - PMGetUnadjustedPaperRect(tmp, &paper); - int wMM = int((paper.right - paper.left) / 72 * 25.4 + 0.5); - int hMM = int((paper.bottom - paper.top) / 72 * 25.4 + 0.5); - if (newSize.width() == wMM && newSize.height() == hMM) { - PMCopyPageFormat(tmp, format()); - // reset the orientation and resolution as they are lost in the copy. - q->setProperty(QPrintEngine::PPK_Orientation, orient); - if (PMSessionValidatePageFormat(session(), format(), kPMDontWantBoolean) != noErr) { - // Don't know, warn for the moment. - qWarning("QMacPrintEngine, problem setting format and resolution for this page size"); - } - break; - } - } - } - } else { - QCFString paperId = QCFString::toCFStringRef(QUuid::createUuid().toString()); - PMPaperMargins paperMargins; - paperMargins.left = leftMargin; - paperMargins.top = topMargin; - paperMargins.right = rightMargin; - paperMargins.bottom = bottomMargin; - PMPaperCreateCustom(printer, paperId, QCFString("Custom size"), customSize.width(), customSize.height(), &paperMargins, &customPaper); - PMPageFormat tmp; - PMCreatePageFormatWithPMPaper(&tmp, customPaper); - PMCopyPageFormat(tmp, format()); - if (PMSessionValidatePageFormat(session(), format(), kPMDontWantBoolean) != noErr) { - // Don't know, warn for the moment. - qWarning("QMacPrintEngine, problem setting paper name"); - } - } - } -} - -QPrinter::PaperSize QMacPrintEnginePrivate::paperSize() const -{ - if (hasCustomPaperSize) - return QPrinter::Custom; - PMRect paper; - PMGetUnadjustedPaperRect(format(), &paper); - QSizeF sizef((paper.right - paper.left) / 72.0 * 25.4, (paper.bottom - paper.top) / 72.0 * 25.4); - return QPlatformPrinterSupport::convertQSizeFToPaperSize(sizef); -} - -void QMacPrintEnginePrivate::setPaperName(const QString &name) -{ - Q_Q(QMacPrintEngine); - if (hasCustomPaperSize) { - PMRelease(customPaper); - customPaper = 0; - hasCustomPaperSize = false; - } - PMPrinter printer; - - if (PMSessionGetCurrentPrinter(session(), &printer) == noErr) { - CFArrayRef array; - if (PMPrinterGetPaperList(printer, &array) != noErr) - return; - int count = CFArrayGetCount(array); - for (int i = 0; i < count; ++i) { - PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(array, i))); - QCFString paperName; - if (PMPaperCreateLocalizedName(paper, printer, &paperName) == noErr) { - if (QString(paperName) == name) { - PMPageFormat tmp; - PMCreatePageFormatWithPMPaper(&tmp, paper); - PMCopyPageFormat(tmp, format()); - q->setProperty(QPrintEngine::PPK_Orientation, orient); - if (PMSessionValidatePageFormat(session(), format(), kPMDontWantBoolean) != noErr) { - // Don't know, warn for the moment. - qWarning("QMacPrintEngine, problem setting paper name"); - } - } - } - } - } -} - -QList<QVariant> QMacPrintEnginePrivate::supportedResolutions() const -{ - Q_ASSERT_X(printInfo, "QMacPrinterEngine::supportedResolutions", - "must have a valid printer session"); - UInt32 resCount; - QList<QVariant> resolutions; - PMPrinter printer; - if (PMSessionGetCurrentPrinter(session(), &printer) == noErr) { - PMResolution res; - OSStatus status = PMPrinterGetPrinterResolutionCount(printer, &resCount); - if (status == noErr) { - // According to the docs, index start at 1. - for (UInt32 i = 1; i <= resCount; ++i) { - if (PMPrinterGetIndexedPrinterResolution(printer, i, &res) == noErr) - resolutions.append(QVariant(int(res.hRes))); - } - } else { - qWarning("QMacPrintEngine::supportedResolutions: Unexpected error: %ld", long(status)); - } - } - return resolutions; -} - QPrinter::PrinterState QMacPrintEngine::printerState() const { return d_func()->state; @@ -291,77 +177,22 @@ bool QMacPrintEngine::abort() return ret; } -static inline int qt_get_PDMWidth(PMPageFormat pformat, bool fullPage, - const PMResolution &resolution) -{ - int val = 0; - PMRect r; - qreal hRatio = resolution.hRes / 72; - if (fullPage) { - if (PMGetAdjustedPaperRect(pformat, &r) == noErr) - val = qRound((r.right - r.left) * hRatio); - } else { - if (PMGetAdjustedPageRect(pformat, &r) == noErr) - val = qRound((r.right - r.left) * hRatio); - } - return val; -} - -static inline int qt_get_PDMHeight(PMPageFormat pformat, bool fullPage, - const PMResolution &resolution) -{ - int val = 0; - PMRect r; - qreal vRatio = resolution.vRes / 72; - if (fullPage) { - if (PMGetAdjustedPaperRect(pformat, &r) == noErr) - val = qRound((r.bottom - r.top) * vRatio); - } else { - if (PMGetAdjustedPageRect(pformat, &r) == noErr) - val = qRound((r.bottom - r.top) * vRatio); - } - return val; -} - - int QMacPrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const { Q_D(const QMacPrintEngine); int val = 1; switch (m) { case QPaintDevice::PdmWidth: - if (d->hasCustomPaperSize) { - val = qRound(d->customSize.width()); - if (d->hasCustomPageMargins) { - val -= qRound(d->leftMargin + d->rightMargin); - } else { - QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList(); - val -= qRound(margins.at(0).toDouble() + margins.at(2).toDouble()); - } - } else { - val = qt_get_PDMWidth(d->format(), property(PPK_FullPage).toBool(), d->resolution); - } + val = d->m_pageLayout.paintRectPixels(d->resolution.hRes).width(); break; case QPaintDevice::PdmHeight: - if (d->hasCustomPaperSize) { - val = qRound(d->customSize.height()); - if (d->hasCustomPageMargins) { - val -= qRound(d->topMargin + d->bottomMargin); - } else { - QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList(); - val -= qRound(margins.at(1).toDouble() + margins.at(3).toDouble()); - } - } else { - val = qt_get_PDMHeight(d->format(), property(PPK_FullPage).toBool(), d->resolution); - } + val = d->m_pageLayout.paintRectPixels(d->resolution.hRes).height(); break; case QPaintDevice::PdmWidthMM: - val = metric(QPaintDevice::PdmWidth); - val = int((val * 254 + 5 * d->resolution.hRes) / (10 * d->resolution.hRes)); + val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).width()); break; case QPaintDevice::PdmHeightMM: - val = metric(QPaintDevice::PdmHeight); - val = int((val * 254 + 5 * d->resolution.vRes) / (10 * d->resolution.vRes)); + val = qRound(d->m_pageLayout.paintRect(QPageLayout::Millimeter).height()); break; case QPaintDevice::PdmPhysicalDpiX: case QPaintDevice::PdmPhysicalDpiY: { @@ -407,33 +238,24 @@ void QMacPrintEnginePrivate::initialize() q->gccaps = paintEngine->gccaps; - fullPage = false; - QCocoaAutoReleasePool pool; printInfo = [[NSPrintInfo alloc] initWithDictionary:[NSDictionary dictionary]]; - PMPrinter printer; - if (printInfo && PMSessionGetCurrentPrinter(session(), &printer) == noErr) { - QList<QVariant> resolutions = supportedResolutions(); - if (!resolutions.isEmpty() && mode != QPrinter::ScreenResolution) { - if (resolutions.count() > 1 && mode == QPrinter::HighResolution) { - int max = 0; - for (int i = 0; i < resolutions.count(); ++i) { - int value = resolutions.at(i).toInt(); - if (value > max) - max = value; - } - resolution.hRes = resolution.vRes = max; - } else { - resolution.hRes = resolution.vRes = resolutions.at(0).toInt(); - } - if (resolution.hRes == 0) - resolution.hRes = resolution.vRes = 600; - } else { - resolution.hRes = resolution.vRes = qt_defaultDpi(); - } + QList<int> resolutions = m_printDevice->supportedResolutions(); + if (!resolutions.isEmpty() && mode != QPrinter::ScreenResolution) { + qSort(resolutions); + if (resolutions.count() > 1 && mode == QPrinter::HighResolution) + resolution.hRes = resolution.vRes = resolutions.last(); + else + resolution.hRes = resolution.vRes = resolutions.first(); + if (resolution.hRes == 0) + resolution.hRes = resolution.vRes = 600; + } else { + resolution.hRes = resolution.vRes = qt_defaultDpi(); } + setPageSize(m_pageLayout.pageSize()); + QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant>::const_iterator propC; for (propC = valueCache.constBegin(); propC != valueCache.constEnd(); propC++) { q->setProperty(propC.key(), propC.value()); @@ -444,8 +266,6 @@ void QMacPrintEnginePrivate::releaseSession() { PMSessionEndPageNoDialog(session()); PMSessionEndDocumentNoDialog(session()); - if (hasCustomPaperSize) - PMRelease(customPaper); [printInfo release]; printInfo = 0; } @@ -473,8 +293,8 @@ bool QMacPrintEnginePrivate::newPage_helper() return false; } - QRect page = q->property(QPrintEngine::PPK_PageRect).toRect(); - QRect paper = q->property(QPrintEngine::PPK_PaperRect).toRect(); + QRect page = m_pageLayout.paintRectPixels(resolution.hRes); + QRect paper = m_pageLayout.fullRectPixels(resolution.hRes); CGContextRef cgContext; OSStatus err = noErr; @@ -491,7 +311,7 @@ bool QMacPrintEnginePrivate::newPage_helper() CGContextScaleCTM(cgContext, 1, -1); CGContextTranslateCTM(cgContext, 0, -paper.height()); - if (!fullPage) + if (m_pageLayout.mode() != QPageLayout::FullPageMode) CGContextTranslateCTM(cgContext, page.x() - paper.x(), page.y() - paper.y()); cgEngine->d_func()->orig_xform = CGContextGetCTM(cgContext); cgEngine->d_func()->setClip(0); @@ -505,6 +325,34 @@ bool QMacPrintEnginePrivate::newPage_helper() return true; } +void QMacPrintEnginePrivate::setPageSize(const QPageSize &pageSize) +{ + if (!pageSize.isValid()) + return; + + // Get the matching printer paper + QPageSize printerPageSize = m_printDevice->supportedPageSize(pageSize); + QPageSize usePageSize = printerPageSize.isValid() ? printerPageSize : pageSize; + + // Get the PMPaper and check it is valid + PMPaper macPaper = m_printDevice->macPaper(usePageSize); + if (macPaper == 0) { + qWarning() << "QMacPrintEngine: Invalid PMPaper returned for " << pageSize; + return; + } + + QMarginsF printable = m_printDevice->printableMargins(usePageSize, m_pageLayout.orientation(), resolution.hRes); + m_pageLayout.setPageSize(usePageSize, qt_convertMargins(printable, QPageLayout::Point, m_pageLayout.units())); + + // You cannot set the page size on a PMPageFormat, you must create a new PMPageFormat + PMPageFormat pageFormat; + PMCreatePageFormatWithPMPaper(&pageFormat, macPaper); + PMSetOrientation(pageFormat, m_pageLayout.orientation() == QPageLayout::Landscape ? kPMLandscape : kPMPortrait, kPMUnlocked); + PMCopyPageFormat(pageFormat, format()); + if (PMSessionValidatePageFormat(session(), format(), kPMDontWantBoolean) != noErr) + qWarning("QMacPrintEngine: Invalid page format"); + PMRelease(pageFormat); +} void QMacPrintEngine::updateState(const QPaintEngineState &state) { @@ -624,32 +472,22 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va break; case PPK_SelectionOption: break; - case PPK_WindowsPageSize: - break; // The following keys are properties and settings that are supported by the Mac PrintEngine case PPK_Resolution: { - PMPrinter printer; - UInt32 count; - if (PMSessionGetCurrentPrinter(d->session(), &printer) != noErr) - break; - if (PMPrinterGetPrinterResolutionCount(printer, &count) != noErr) - break; - PMResolution resolution = { 0.0, 0.0 }; - PMResolution bestResolution = { 0.0, 0.0 }; + // TODO It appears the old code didn't actually set the resolution??? Can we delete all this??? + int bestResolution = 0; int dpi = value.toInt(); int bestDistance = INT_MAX; - for (UInt32 i = 1; i <= count; ++i) { // Yes, it starts at 1 - if (PMPrinterGetIndexedPrinterResolution(printer, i, &resolution) == noErr) { - if (dpi == int(resolution.hRes)) { + foreach (int resolution, d->m_printDevice->supportedResolutions()) { + if (dpi == resolution) { + bestResolution = resolution; + break; + } else { + int distance = qAbs(dpi - resolution); + if (distance < bestDistance) { + bestDistance = distance; bestResolution = resolution; - break; - } else { - int distance = qAbs(dpi - int(resolution.hRes)); - if (distance < bestDistance) { - bestDistance = distance; - bestResolution = resolution; - } } } } @@ -666,75 +504,80 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va PMPrintSettingsSetJobName(d->settings(), QCFString(value.toString())); break; case PPK_FullPage: - d->fullPage = value.toBool(); + if (value.toBool()) + d->m_pageLayout.setMode(QPageLayout::FullPageMode); + else + d->m_pageLayout.setMode(QPageLayout::StandardMode); break; case PPK_CopyCount: // fallthrough case PPK_NumberOfCopies: PMSetCopies(d->settings(), value.toInt(), false); break; case PPK_Orientation: { - QPrinter::Orientation newOrientation = QPrinter::Orientation(value.toInt()); - if (d->hasCustomPaperSize && (d->orient != newOrientation)) - d->customSize = QSizeF(d->customSize.height(), d->customSize.width()); - d->orient = newOrientation; - PMOrientation o = d->orient == QPrinter::Portrait ? kPMPortrait : kPMLandscape; - PMSetOrientation(d->format(), o, false); + // First try set the Mac format orientation, then set our orientation to match result + QPageLayout::Orientation newOrientation = QPageLayout::Orientation(value.toInt()); + PMOrientation macOrientation = (newOrientation == QPageLayout::Landscape) ? kPMLandscape : kPMPortrait; + PMSetOrientation(d->format(), macOrientation, kPMUnlocked); PMSessionValidatePageFormat(d->session(), d->format(), kPMDontWantBoolean); + PMGetOrientation(d->format(), &macOrientation); + d->m_pageLayout.setOrientation(macOrientation == kPMLandscape ? QPageLayout::Landscape : QPageLayout::Portrait); break; } case PPK_OutputFileName: d->outputFilename = value.toString(); break; - case PPK_PaperSize: - d->setPaperSize(QPrinter::PaperSize(value.toInt())); + case PPK_PageSize: + d->setPageSize(QPageSize(QPageSize::PageSizeId(value.toInt()))); break; case PPK_PaperName: - d->setPaperName(value.toString()); + // Get the named page size from the printer if supported + d->setPageSize(d->m_printDevice->supportedPageSize(value.toString())); + break; + case PPK_WindowsPageSize: + d->setPageSize(QPageSize(QPageSize::id(value.toInt()))); break; case PPK_PrinterName: { - bool printerNameSet = false; - OSStatus status = noErr; - QCFType<CFArrayRef> printerList; - status = PMServerCreatePrinterList(kPMServerLocal, &printerList); - if (status == noErr) { - CFIndex count = CFArrayGetCount(printerList); - for (CFIndex i=0; i<count; ++i) { - PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(printerList, i))); - QString name = QCFString::toQString(PMPrinterGetID(printer)); - if (name == value.toString()) { - status = PMSessionSetCurrentPMPrinter(d->session(), printer); - printerNameSet = true; - break; - } - } - } - if (status != noErr) - qWarning("QMacPrintEngine::setPrinterName: Error setting printer: %ld", long(status)); - if (!printerNameSet) { - qWarning("QMacPrintEngine::setPrinterName: Failed to set printer named '%s'.", qPrintable(value.toString())); - d->releaseSession(); - d->state = QPrinter::Idle; - } - break; } - case PPK_CustomPaperSize: - { - PMOrientation orientation; - PMGetOrientation(d->format(), &orientation); - d->customSize = value.toSizeF(); - if (orientation != kPMPortrait) - d->customSize = QSizeF(d->customSize.height(), d->customSize.width()); - d->setPaperSize(QPrinter::Custom); + QString id = value.toString(); + if (id.isEmpty()) + id = QCocoaPrinterSupport().defaultPrintDeviceId(); + else if (!QCocoaPrinterSupport().availablePrintDeviceIds().contains(id)) + break; + d->m_printDevice = new QCocoaPrintDevice(id); + PMPrinter printer = d->m_printDevice->macPrinter(); + PMRetain(printer); + PMSessionSetCurrentPMPrinter(d->session(), printer); + // TODO Do we need to check if the page size, etc, are valid on new printer? break; } + case PPK_CustomPaperSize: + d->setPageSize(QPageSize(value.toSizeF(), QPageSize::Point)); + break; case PPK_PageMargins: { QList<QVariant> margins(value.toList()); Q_ASSERT(margins.size() == 4); - d->leftMargin = margins.at(0).toDouble(); - d->topMargin = margins.at(1).toDouble(); - d->rightMargin = margins.at(2).toDouble(); - d->bottomMargin = margins.at(3).toDouble(); - d->hasCustomPageMargins = true; + d->m_pageLayout.setMargins(QMarginsF(margins.at(0).toReal(), margins.at(1).toReal(), + margins.at(2).toReal(), margins.at(3).toReal())); + break; + } + case PPK_QPageSize: + d->setPageSize(value.value<QPageSize>()); + break; + case PPK_QPageMargins: { + QPair<QMarginsF, QPageLayout::Unit> pair = value.value<QPair<QMarginsF, QPageLayout::Unit> >(); + d->m_pageLayout.setUnits(pair.second); + d->m_pageLayout.setMargins(pair.first); + break; + } + case PPK_QPageLayout: { + QPageLayout pageLayout = value.value<QPageLayout>(); + if (pageLayout.isValid() && d->m_printDevice->isValidPageLayout(pageLayout, d->resolution.hRes)) { + setProperty(PPK_QPageSize, QVariant::fromValue(pageLayout.pageSize())); + setProperty(PPK_FullPage, pageLayout.mode() == QPageLayout::FullPageMode); + setProperty(PPK_Orientation, QVariant::fromValue(pageLayout.orientation())); + d->m_pageLayout.setUnits(pageLayout.units()); + d->m_pageLayout.setMargins(pageLayout.margins()); + } break; } // No default so that compiler will complain if new keys added and not handled in this engine @@ -787,9 +630,6 @@ QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const case PPK_SelectionOption: ret = QString(); break; - case PPK_WindowsPageSize: - // Special case, leave null - break; // The following keys are properties and settings that are supported by the Mac PrintEngine case PPK_CollateCopies: { @@ -808,7 +648,7 @@ QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const break; } case PPK_FullPage: - ret = d->fullPage; + ret = d->m_pageLayout.mode() == QPageLayout::FullPageMode; break; case PPK_NumberOfCopies: ret = 1; @@ -823,107 +663,62 @@ QVariant QMacPrintEngine::property(PrintEnginePropertyKey key) const ret = true; break; case PPK_Orientation: - PMOrientation orientation; - PMGetOrientation(d->format(), &orientation); - ret = orientation == kPMPortrait ? QPrinter::Portrait : QPrinter::Landscape; + ret = d->m_pageLayout.orientation(); break; case PPK_OutputFileName: ret = d->outputFilename; break; - case PPK_PageRect: { + case PPK_PageRect: // PageRect is returned in device pixels - QRect r; - PMRect macrect, macpaper; - qreal hRatio = d->resolution.hRes / 72; - qreal vRatio = d->resolution.vRes / 72; - if (d->hasCustomPaperSize) { - r = QRect(0, 0, qRound(d->customSize.width() * hRatio), qRound(d->customSize.height() * vRatio)); - if (d->hasCustomPageMargins) { - r.adjust(qRound(d->leftMargin * hRatio), qRound(d->topMargin * vRatio), - -qRound(d->rightMargin * hRatio), -qRound(d->bottomMargin * vRatio)); - } else { - QList<QVariant> margins = property(QPrintEngine::PPK_PageMargins).toList(); - r.adjust(qRound(margins.at(0).toDouble() * hRatio), - qRound(margins.at(1).toDouble() * vRatio), - -qRound(margins.at(2).toDouble() * hRatio), - -qRound(margins.at(3).toDouble()) * vRatio); - } - } else if (PMGetAdjustedPageRect(d->format(), ¯ect) == noErr - && PMGetAdjustedPaperRect(d->format(), &macpaper) == noErr) - { - if (d->fullPage || d->hasCustomPageMargins) { - r.setCoords(int(macpaper.left * hRatio), int(macpaper.top * vRatio), - int(macpaper.right * hRatio), int(macpaper.bottom * vRatio)); - r.translate(-r.x(), -r.y()); - if (d->hasCustomPageMargins) { - r.adjust(qRound(d->leftMargin * hRatio), qRound(d->topMargin * vRatio), - -qRound(d->rightMargin * hRatio), -qRound(d->bottomMargin * vRatio)); - } - } else { - r.setCoords(int(macrect.left * hRatio), int(macrect.top * vRatio), - int(macrect.right * hRatio), int(macrect.bottom * vRatio)); - r.translate(int(-macpaper.left * hRatio), int(-macpaper.top * vRatio)); - } - } - ret = r; - break; } - case PPK_PaperSize: - ret = d->paperSize(); + ret = d->m_pageLayout.paintRectPixels(d->resolution.hRes); + break; + case PPK_PageSize: + ret = d->m_pageLayout.pageSize().id(); break; case PPK_PaperName: - ret = QCFString::toQString([d->printInfo localizedPaperName]); - break; - case PPK_PaperRect: { - QRect r; - PMRect macrect; - qreal hRatio = d->resolution.hRes / 72; - qreal vRatio = d->resolution.vRes / 72; - if (d->hasCustomPaperSize) { - r = QRect(0, 0, qRound(d->customSize.width() * hRatio), qRound(d->customSize.height() * vRatio)); - } else if (PMGetAdjustedPaperRect(d->format(), ¯ect) == noErr) { - r.setCoords(int(macrect.left * hRatio), int(macrect.top * vRatio), - int(macrect.right * hRatio), int(macrect.bottom * vRatio)); - r.translate(-r.x(), -r.y()); - } - ret = r; - break; } - case PPK_PrinterName: { - PMPrinter printer; - OSStatus status = PMSessionGetCurrentPrinter(d->session(), &printer); - if (status != noErr) - qWarning("QMacPrintEngine::printerName: Failed getting current PMPrinter: %ld", long(status)); - if (printer) - ret = QCFString::toQString(PMPrinterGetID(printer)); - break; } + ret = d->m_pageLayout.pageSize().name(); + break; + case PPK_WindowsPageSize: + ret = d->m_pageLayout.pageSize().windowsId(); + break; + case PPK_PaperRect: + // PaperRect is returned in device pixels + ret = d->m_pageLayout.fullRectPixels(d->resolution.hRes); + break; + case PPK_PrinterName: + return d->m_printDevice->id(); + break; case PPK_Resolution: { ret = d->resolution.hRes; break; } - case PPK_SupportedResolutions: - ret = d->supportedResolutions(); + case PPK_SupportedResolutions: { + QList<QVariant> list; + foreach (int resolution, d->m_printDevice->supportedResolutions()) + list << resolution; + ret = list; break; + } case PPK_CustomPaperSize: - ret = d->customSize; + ret = d->m_pageLayout.fullRectPoints().size(); break; - case PPK_PageMargins: - { - QList<QVariant> margins; - if (d->hasCustomPageMargins) { - margins << d->leftMargin << d->topMargin - << d->rightMargin << d->bottomMargin; - } else if (!d->hasCustomPaperSize) { - PMPaperMargins paperMargins; - PMPaper paper; - PMGetPageFormatPaper(d->format(), &paper); - PMPaperGetMargins(paper, &paperMargins); - margins << paperMargins.left << paperMargins.top - << paperMargins.right << paperMargins.bottom; - } else { - margins << 0 << 0 << 0 << 0; - } - ret = margins; + case PPK_PageMargins: { + QList<QVariant> list; + QMarginsF margins = d->m_pageLayout.margins(QPageLayout::Point); + list << margins.left() << margins.top() << margins.right() << margins.bottom(); + ret = list; + break; + } + case PPK_QPageSize: + ret.setValue(d->m_pageLayout.pageSize()); + break; + case PPK_QPageMargins: { + QPair<QMarginsF, QPageLayout::Unit> pair = qMakePair(d->m_pageLayout.margins(), d->m_pageLayout.units()); + ret.setValue(pair); break; } + case PPK_QPageLayout: + ret.setValue(d->m_pageLayout); // No default so that compiler will complain if new keys added and not handled in this engine } return ret; diff --git a/src/plugins/platforms/cocoa/qprintengine_mac_p.h b/src/plugins/platforms/cocoa/qprintengine_mac_p.h index e3a8520811..12a87b35e9 100644 --- a/src/plugins/platforms/cocoa/qprintengine_mac_p.h +++ b/src/plugins/platforms/cocoa/qprintengine_mac_p.h @@ -60,6 +60,9 @@ #include <QtPrintSupport/qprinter.h> #include <QtPrintSupport/qprintengine.h> #include <QtGui/private/qpainter_p.h> +#include <QtGui/qpagelayout.h> + +#include "qcocoaprintdevice.h" #include "qpaintengine_mac_p.h" @@ -121,33 +124,24 @@ class QMacPrintEnginePrivate : public QPaintEnginePrivate public: QPrinter::PrinterMode mode; QPrinter::PrinterState state; - QPrinter::Orientation orient; + QSharedDataPointer<QCocoaPrintDevice> m_printDevice; + QPageLayout m_pageLayout; NSPrintInfo *printInfo; PMResolution resolution; QString outputFilename; QString m_creator; - bool fullPage; QPaintEngine *paintEngine; - bool hasCustomPaperSize; - QSizeF customSize; - bool hasCustomPageMargins; - qreal leftMargin; - qreal topMargin; - qreal rightMargin; - qreal bottomMargin; QHash<QMacPrintEngine::PrintEnginePropertyKey, QVariant> valueCache; - PMPaper customPaper; + QMacPrintEnginePrivate() : mode(QPrinter::ScreenResolution), state(QPrinter::Idle), - orient(QPrinter::Portrait), printInfo(0), paintEngine(0), - hasCustomPaperSize(false), hasCustomPageMargins(false) {} + m_pageLayout(QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0, 0, 0, 0))), + printInfo(0), paintEngine(0) {} ~QMacPrintEnginePrivate(); + void initialize(); void releaseSession(); bool newPage_helper(); - void setPaperSize(QPrinter::PaperSize ps); - QPrinter::PaperSize paperSize() const; - void setPaperName(const QString &name); - QList<QVariant> supportedResolutions() const; + void setPageSize(const QPageSize &pageSize); inline bool isPrintSessionInitialized() const { return printInfo != 0; diff --git a/src/plugins/platforms/cocoa/qt_mac_p.h b/src/plugins/platforms/cocoa/qt_mac_p.h index 581157c2e1..7d38b08d84 100644 --- a/src/plugins/platforms/cocoa/qt_mac_p.h +++ b/src/plugins/platforms/cocoa/qt_mac_p.h @@ -194,8 +194,6 @@ extern QPoint qt_mac_nativeMapFromParent(const QWidget *child, const QPoint &pt) QFont qfontForThemeFont(ThemeFontID themeID); -QColor qcolorForTheme(ThemeBrush brush); - QColor qcolorForThemeTextColor(ThemeTextColor themeColor); struct QMacDndAnswerRecord { diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index e19a6be47b..d8f34fc3ed 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -77,6 +77,12 @@ enum { D2DDebugDrawStaticTextItemTag, D2DDebugDrawTextItemTag }; + +//Clipping flags +enum { + UserClip = 0x1, + SimpleSystemClip = 0x2 +}; #define D2D_TAG(tag) d->dc()->SetTags(tag, tag) Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert); @@ -286,7 +292,7 @@ class QWindowsDirect2DPaintEnginePrivate : public QPaintEngineExPrivate public: QWindowsDirect2DPaintEnginePrivate(QWindowsDirect2DBitmap *bm) : bitmap(bm) - , clipPushed(false) + , clipFlags(0) { pen.reset(); brush.reset(); @@ -297,7 +303,7 @@ public: QWindowsDirect2DBitmap *bitmap; QPainterPath clipPath; - bool clipPushed; + unsigned int clipFlags; QPointF currentBrushOrigin; @@ -381,14 +387,14 @@ public: NULL, D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND), NULL); - clipPushed = true; + clipFlags |= UserClip; } void popClip() { - if (clipPushed) { + if (clipFlags & UserClip) { dc()->PopLayer(); - clipPushed = false; + clipFlags &= ~UserClip; } } @@ -397,7 +403,7 @@ public: Q_Q(const QWindowsDirect2DPaintEngine); if (!q->state()->clipEnabled) popClip(); - else if (!clipPushed) + else if (!(clipFlags & UserClip)) pushClip(); } @@ -729,11 +735,29 @@ bool QWindowsDirect2DPaintEngine::begin(QPaintDevice * pdev) d->bitmap->deviceContext()->begin(); d->dc()->SetTransform(D2D1::Matrix3x2F::Identity()); - QRect clip(0, 0, pdev->width(), pdev->height()); - if (!systemClip().isEmpty()) - clip &= systemClip().boundingRect(); + if (systemClip().rectCount() > 1) { + QPainterPath p; + p.addRegion(systemClip()); - d->dc()->PushAxisAlignedClip(to_d2d_rect_f(clip), D2D1_ANTIALIAS_MODE_ALIASED); + ComPtr<ID2D1PathGeometry1> geometry = painterPathToPathGeometry(p); + if (!geometry) + return false; + + d->dc()->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), + geometry.Get(), + d->antialiasMode(), + D2D1::IdentityMatrix(), + 1.0, + NULL, + D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND), + NULL); + } else { + QRect clip(0, 0, pdev->width(), pdev->height()); + if (!systemClip().isEmpty()) + clip &= systemClip().boundingRect(); + d->dc()->PushAxisAlignedClip(to_d2d_rect_f(clip), D2D1_ANTIALIAS_MODE_ALIASED); + d->clipFlags |= SimpleSystemClip; + } D2D_TAG(D2DDebugDrawInitialStateTag); @@ -746,7 +770,12 @@ bool QWindowsDirect2DPaintEngine::end() // First pop any user-applied clipping d->popClip(); // Now the system clip from begin() above - d->dc()->PopAxisAlignedClip(); + if (d->clipFlags & SimpleSystemClip) { + d->dc()->PopAxisAlignedClip(); + d->clipFlags &= ~SimpleSystemClip; + } else { + d->dc()->PopLayer(); + } return d->bitmap->deviceContext()->end(); } diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index 4fe2cae15e..13a0b46745 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -39,13 +39,16 @@ ** ****************************************************************************/ -#include "qiosglobal.h" #include "qiosinputcontext.h" + +#import <UIKit/UIGestureRecognizerSubclass.h> + +#include "qiosglobal.h" #include "qioswindow.h" #include "quiview.h" #include <QGuiApplication> -@interface QIOSKeyboardListener : NSObject { +@interface QIOSKeyboardListener : UIGestureRecognizer { @public QIOSInputContext *m_context; BOOL m_keyboardVisible; @@ -63,7 +66,7 @@ - (id)initWithQIOSInputContext:(QIOSInputContext *)context { - self = [super init]; + self = [super initWithTarget:self action:@selector(gestureTriggered)]; if (self) { m_context = context; m_keyboardVisible = NO; @@ -82,6 +85,14 @@ } } Q_ASSERT(m_viewController); + + // Attach 'hide keyboard' gesture to the window, but keep it disabled when the + // keyboard is not visible. Note that we never trigger the gesture the way it is intended + // since we don't want to cancel touch events and interrupt flicking etc. Instead we use + // the gesture framework more as an event filter and hide the keyboard silently. + self.enabled = NO; + self.delaysTouchesEnded = NO; + [m_viewController.view.window addGestureRecognizer:self]; } [[NSNotificationCenter defaultCenter] @@ -102,7 +113,9 @@ - (void) dealloc { + [m_viewController.view.window removeGestureRecognizer:self]; [m_viewController release]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:@"UIKeyboardWillShowNotification" object:nil]; @@ -131,16 +144,11 @@ - (void) keyboardDidChangeFrame:(NSNotification *)notification { + Q_UNUSED(notification); if (m_ignoreKeyboardChanges) return; - m_keyboardRect = [self getKeyboardRect:notification]; - m_context->emitKeyboardRectChanged(); - BOOL visible = m_keyboardRect.intersects(fromCGRect([UIScreen mainScreen].bounds)); - if (m_keyboardVisible != visible) { - m_keyboardVisible = visible; - m_context->emitInputPanelVisibleChanged(); - } + [self handleKeyboardRectChanged]; // If the keyboard was visible and docked from before, this is just a geometry // change (normally caused by an orientation change). In that case, update scroll: @@ -155,6 +163,7 @@ // Note that UIKeyboardWillShowNotification is only sendt when the keyboard is docked. m_keyboardVisibleAndDocked = YES; m_keyboardEndRect = [self getKeyboardRect:notification]; + self.enabled = YES; if (!m_duration) { m_duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; m_curve = UIViewAnimationCurve([[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue] << 16); @@ -169,9 +178,35 @@ // Note that UIKeyboardWillHideNotification is also sendt when the keyboard is undocked. m_keyboardVisibleAndDocked = NO; m_keyboardEndRect = [self getKeyboardRect:notification]; + self.enabled = NO; m_context->scroll(0); } +- (void) handleKeyboardRectChanged +{ + QRectF rect = m_keyboardEndRect; + rect.moveTop(rect.y() + m_viewController.view.bounds.origin.y); + if (m_keyboardRect != rect) { + m_keyboardRect = rect; + m_context->emitKeyboardRectChanged(); + } + + BOOL visible = m_keyboardEndRect.intersects(fromCGRect([UIScreen mainScreen].bounds)); + if (m_keyboardVisible != visible) { + m_keyboardVisible = visible; + m_context->emitInputPanelVisibleChanged(); + } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + QPointF p = fromCGPoint([[touches anyObject] locationInView:m_viewController.view]); + if (m_keyboardRect.contains(p)) + m_context->hideInputPanel(); + + [super touchesMoved:touches withEvent:event]; +} + @end QIOSInputContext::QIOSInputContext() @@ -295,10 +330,15 @@ void QIOSInputContext::scroll(int y) CGRect newBounds = view.bounds; newBounds.origin.y = y; + QPointer<QIOSInputContext> self = this; [UIView animateWithDuration:m_keyboardListener->m_duration delay:0 options:m_keyboardListener->m_curve animations:^{ view.bounds = newBounds; } - completion:0]; + completion:^(BOOL){ + if (self) + [m_keyboardListener handleKeyboardRectChanged]; + } + ]; } void QIOSInputContext::update(Qt::InputMethodQueries query) diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index 96410952f9..5331d05ae9 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -120,7 +120,9 @@ static QString deviceModelIdentifier() QIOSScreen::QIOSScreen(unsigned int screenIndex) : QPlatformScreen() - , m_uiScreen([[UIScreen screens] objectAtIndex:qMin(NSUInteger(screenIndex), [[UIScreen screens] count] - 1)]) + , m_uiScreen([[UIScreen screens] count] > screenIndex + ? [[UIScreen screens] objectAtIndex:screenIndex] + : [UIScreen mainScreen]) , m_orientationListener(0) { QString deviceIdentifier = deviceModelIdentifier(); diff --git a/src/plugins/platforms/ios/quiview_textinput.mm b/src/plugins/platforms/ios/quiview_textinput.mm index d0088d415a..28fb23d57b 100644 --- a/src/plugins/platforms/ios/quiview_textinput.mm +++ b/src/plugins/platforms/ios/quiview_textinput.mm @@ -492,8 +492,17 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables); if (!focusObject) return; - if ([text isEqualToString:@"\n"] && self.returnKeyType == UIReturnKeyDone) - [self resignFirstResponder]; + if ([text isEqualToString:@"\n"]) { + QKeyEvent press(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier); + QKeyEvent release(QEvent::KeyRelease, Qt::Key_Return, Qt::NoModifier); + [self sendEventToFocusObject:press]; + [self sendEventToFocusObject:release]; + + if (self.returnKeyType == UIReturnKeyDone) + [self resignFirstResponder]; + + return; + } QInputMethodEvent e; e.setCommitString(QString::fromNSString(text)); diff --git a/src/plugins/platforms/qnx/qblackberrytheme.cpp b/src/plugins/platforms/qnx/qblackberrytheme.cpp index a0f334d909..46ab4d7033 100644 --- a/src/plugins/platforms/qnx/qblackberrytheme.cpp +++ b/src/plugins/platforms/qnx/qblackberrytheme.cpp @@ -60,9 +60,11 @@ QBlackberryTheme::QBlackberryTheme(const QQnxIntegration *integration) : m_integ m_defaultPalette.setBrush(QPalette::Disabled, QPalette::WindowText, color); m_defaultPalette.setBrush(QPalette::Disabled, QPalette::Text, color); - m_defaultPalette.setColor(QPalette::Window, QColor(18, 18, 18)); - m_defaultPalette.setColor(QPalette::Base, QColor(18, 18, 18)); + color.setRgb(18, 18, 18); + m_defaultPalette.setColor(QPalette::Window, color); + m_defaultPalette.setColor(QPalette::Base, color); m_defaultPalette.setColor(QPalette::AlternateBase, QColor(50, 50, 50)); + m_defaultPalette.setColor(QPalette::Button, color); m_defaultPalette.setBrush(QPalette::Highlight, QColor(0, 168, 223)); m_defaultPalette.setBrush(QPalette::HighlightedText, QColor(250, 250,250)); diff --git a/src/plugins/platforms/qnx/qqnxeglwindow.cpp b/src/plugins/platforms/qnx/qqnxeglwindow.cpp index 3c08cc9f82..f1f9f5469c 100644 --- a/src/plugins/platforms/qnx/qqnxeglwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxeglwindow.cpp @@ -70,8 +70,7 @@ QQnxEglWindow::QQnxEglWindow(QWindow *window, screen_context_t context, bool nee if (result != 0) qFatal("QQnxEglWindow: failed to set window alpha usage, errno=%d", errno); - m_requestedBufferSize = screen()->rootWindow() == this ? - screen()->geometry().size() : window->geometry().size(); + m_requestedBufferSize = shouldMakeFullScreen() ? screen()->geometry().size() : window->geometry().size(); } QQnxEglWindow::~QQnxEglWindow() @@ -156,7 +155,7 @@ EGLSurface QQnxEglWindow::getSurface() void QQnxEglWindow::setGeometry(const QRect &rect) { //If this is the root window, it has to be shown fullscreen - const QRect &newGeometry = screen()->rootWindow() == this ? screen()->geometry() : rect; + const QRect &newGeometry = shouldMakeFullScreen() ? screen()->geometry() : rect; //We need to request that the GL context updates // the EGLsurface on which it is rendering. diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp index 178ea121e6..5724fbd92a 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp @@ -457,7 +457,7 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType) m_touchPoints[touchId].area = QRectF(w->geometry().left() + windowPos[0] - (touchArea[0]>>1), w->geometry().top() + windowPos[1] - (touchArea[1]>>1), - 0.0, 0.0); + (touchArea[0]>>1), (touchArea[1]>>1)); QWindow *parent = w->parent(); while (parent) { m_touchPoints[touchId].area.translate(parent->geometry().topLeft()); diff --git a/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp b/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp index 156ba8a780..b66de3cac5 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp @@ -103,10 +103,10 @@ void QQnxScreenEventThread::run() Q_SCREEN_CHECKERROR(screen_create_event(&event), "Failed to create screen event"); // block until screen event is available - const int result = screen_get_event(m_screenContext, event, -1); - Q_SCREEN_CRITICALERROR(result, "Failed to get screen event"); + const int error = screen_get_event(m_screenContext, event, -1); + Q_SCREEN_CRITICALERROR(error, "Failed to get screen event"); // Only allow 50 consecutive errors before we exit the thread - if (!result) { + if (error) { errorCounter++; if (errorCounter > 50) m_quit = true; diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp index 9ae2d01ab4..f11a009bca 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxwindow.cpp @@ -242,19 +242,14 @@ QQnxWindow::~QQnxWindow() void QQnxWindow::setGeometry(const QRect &rect) { QRect newGeometry = rect; - if (screen()->rootWindow() == this) //If this is the root window, it has to be shown fullscreen + if (shouldMakeFullScreen()) newGeometry = screen()->geometry(); setGeometryHelper(newGeometry); - // Send a geometry change event to Qt (triggers resizeEvent() in QWindow/QWidget). - - // Calling flushWindowSystemEvents() here would flush input events which - // could result in re-entering QQnxWindow::setGeometry() again. - QWindowSystemInterface::setSynchronousWindowsSystemEvents(true); QWindowSystemInterface::handleGeometryChange(window(), newGeometry); - QWindowSystemInterface::handleExposeEvent(window(), newGeometry); - QWindowSystemInterface::setSynchronousWindowsSystemEvents(false); + if (isExposed()) + QWindowSystemInterface::handleExposeEvent(window(), newGeometry); } void QQnxWindow::setGeometryHelper(const QRect &rect) @@ -714,7 +709,7 @@ void QQnxWindow::initWindow() if (window()->parent() && window()->parent()->handle()) setParent(window()->parent()->handle()); - if (screen()->rootWindow() == this) { + if (shouldMakeFullScreen()) { setGeometryHelper(screen()->geometry()); QWindowSystemInterface::handleGeometryChange(window(), screen()->geometry()); } else { @@ -817,4 +812,9 @@ void QQnxWindow::windowPosted() qqnxLgmonFramePosted(m_cover); // for performance measurements } +bool QQnxWindow::shouldMakeFullScreen() const +{ + return ((screen()->rootWindow() == this) && (QQnxIntegration::options() & QQnxIntegration::FullScreenApplication)); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxwindow.h b/src/plugins/platforms/qnx/qqnxwindow.h index e97e941a08..9a2006396f 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.h +++ b/src/plugins/platforms/qnx/qqnxwindow.h @@ -112,6 +112,8 @@ public: QByteArray groupName() const { return m_windowGroupName; } void joinWindowGroup(const QByteArray &groupName); + bool shouldMakeFullScreen() const; + protected: virtual int pixelFormat() const = 0; virtual void resetBuffers() = 0; diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index b6ff3dc3ce..f70b5b4e2b 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -1195,7 +1195,7 @@ static QList<FilterSpec> filterSpecs(const QStringList &filters, const QRegExp filterSeparatorRE(QStringLiteral("[;\\s]+")); const QString separator = QStringLiteral(";"); Q_ASSERT(filterSeparatorRE.isValid()); - // Split filter specification as 'Texts (*.txt[;] *.doc)' + // Split filter specification as 'Texts (*.txt[;] *.doc)', '*.txt[;] *.doc' // into description and filters specification as '*.txt;*.doc' foreach (const QString &filterString, filters) { const int openingParenPos = filterString.lastIndexOf(QLatin1Char('(')); @@ -1203,8 +1203,10 @@ static QList<FilterSpec> filterSpecs(const QStringList &filters, filterString.indexOf(QLatin1Char(')'), openingParenPos + 1) : -1; FilterSpec filterSpec; filterSpec.filter = closingParenPos == -1 ? - QString(QLatin1Char('*')) : + filterString : filterString.mid(openingParenPos + 1, closingParenPos - openingParenPos - 1).trimmed(); + if (filterSpec.filter.isEmpty()) + filterSpec.filter += QLatin1Char('*'); filterSpec.filter.replace(filterSeparatorRE, separator); filterSpec.description = filterString; if (hideFilterDetails && openingParenPos != -1) { // Do not show pattern in description @@ -1559,7 +1561,7 @@ public: QWindowsFileDialogHelper() {} virtual bool supportsNonModalDialog(const QWindow * /* parent */ = 0) const { return false; } virtual bool defaultNameFilterDisables() const - { return true; } + { return false; } virtual void setDirectory(const QUrl &directory) Q_DECL_OVERRIDE; virtual QUrl directory() const Q_DECL_OVERRIDE; virtual void selectFile(const QUrl &filename) Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp index c45c34ae4d..e7e4028079 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -387,7 +387,12 @@ static int choosePixelFormat(HDC hdc, iAttributes[i++] = WGL_DRAW_TO_WINDOW_ARB; iAttributes[i++] = TRUE; iAttributes[i++] = WGL_COLOR_BITS_ARB; - iAttributes[i++] = 24; + + iAttributes[i++] = (format.redBufferSize() > 0) + && (format.greenBufferSize() > 0) + && (format.blueBufferSize() > 0) ? + format.redBufferSize() + format.greenBufferSize() + format.blueBufferSize() : + 24; switch (format.swapBehavior()) { case QSurfaceFormat::SingleBuffer: iAttributes[i++] = WGL_DOUBLE_BUFFER_ARB; diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index e0e8753e14..de34663286 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -83,6 +83,7 @@ # include "qwindowssessionmanager.h" #endif #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/qpa/qplatforminputcontextfactory_p.h> #include <QtCore/private/qeventdispatcher_win_p.h> #include <QtCore/QDebug> @@ -158,7 +159,7 @@ struct QWindowsIntegrationPrivate #if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES_2) QOpenGLStaticContextPtr m_staticOpenGLContext; #endif - QWindowsInputContext m_inputContext; + QScopedPointer<QPlatformInputContext> m_inputContext; #ifndef QT_NO_ACCESSIBILITY QWindowsAccessibility m_accessibility; #endif @@ -224,6 +225,14 @@ QWindowsIntegration::~QWindowsIntegration() { } +void QWindowsIntegration::initialize() +{ + if (QPlatformInputContext *pluginContext = QPlatformInputContextFactory::create()) + d->m_inputContext.reset(pluginContext); + else + d->m_inputContext.reset(new QWindowsInputContext); +} + bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) const { switch (cap) { @@ -441,7 +450,7 @@ QPlatformDrag *QWindowsIntegration::drag() const QPlatformInputContext * QWindowsIntegration::inputContext() const { - return &d->m_inputContext; + return d->m_inputContext.data(); } #ifndef QT_NO_ACCESSIBILITY diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h index 2202ebd39e..0f417c8239 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -76,6 +76,7 @@ public: virtual QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; #endif virtual QAbstractEventDispatcher *createEventDispatcher() const; + void initialize() Q_DECL_OVERRIDE; #ifndef QT_NO_CLIPBOARD virtual QPlatformClipboard *clipboard() const; # ifndef QT_NO_DRAGANDDROP diff --git a/src/plugins/platforms/winrt/qwinrtbackingstore.cpp b/src/plugins/platforms/winrt/qwinrtbackingstore.cpp index 10136dbead..9b623048af 100644 --- a/src/plugins/platforms/winrt/qwinrtbackingstore.cpp +++ b/src/plugins/platforms/winrt/qwinrtbackingstore.cpp @@ -207,14 +207,24 @@ QWinRTBackingStore::QWinRTBackingStore(QWindow *window) , m_fbo(0) , m_texture(0) , m_screen(static_cast<QWinRTScreen*>(window->screen()->handle())) + , m_initialized(false) { window->setSurfaceType(QSurface::OpenGLSurface); // Required for flipping, but could be done in the swap +} - m_context->setFormat(window->requestedFormat()); - m_context->setScreen(window->screen()); - m_context->create(); +bool QWinRTBackingStore::initialize() +{ + if (m_initialized) + return true; + + m_context->setFormat(window()->requestedFormat()); + m_context->setScreen(window()->screen()); + if (!m_context->create()) + return false; + + if (!m_context->makeCurrent(window())) + return false; - m_context->makeCurrent(window); glGenFramebuffers(1, &m_fbo); glGenRenderbuffers(1, &m_rbo); glGenTextures(1, &m_texture); @@ -258,11 +268,14 @@ QWinRTBackingStore::QWinRTBackingStore(QWindow *window) glProgramBinaryOES(m_shaderProgram, GL_PROGRAM_BINARY_ANGLE, binary.constData(), binary.size()); #endif m_context->doneCurrent(); - resize(window->size(), QRegion()); + m_initialized = true; + return true; } QWinRTBackingStore::~QWinRTBackingStore() { + if (!m_initialized) + return; glDeleteBuffers(1, &m_fbo); glDeleteRenderbuffers(1, &m_rbo); glDeleteTextures(1, &m_texture); @@ -277,6 +290,8 @@ QPaintDevice *QWinRTBackingStore::paintDevice() void QWinRTBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) { Q_UNUSED(offset) + if (m_size.isEmpty()) + return; const QImage *image = static_cast<QImage *>(m_paintDevice.data()); @@ -334,10 +349,16 @@ void QWinRTBackingStore::flush(QWindow *window, const QRegion ®ion, const QPo void QWinRTBackingStore::resize(const QSize &size, const QRegion &staticContents) { Q_UNUSED(staticContents) + if (!initialize()) + return; + if (m_size == size) return; m_size = size; + if (m_size.isEmpty()) + return; + m_paintDevice.reset(new QImage(m_size, QImage::Format_ARGB32_Premultiplied)); m_context->makeCurrent(window()); @@ -360,6 +381,7 @@ void QWinRTBackingStore::resize(const QSize &size, const QRegion &staticContents void QWinRTBackingStore::beginPaint(const QRegion ®ion) { Q_UNUSED(region) + resize(window()->size(), QRegion()); } void QWinRTBackingStore::endPaint() diff --git a/src/plugins/platforms/winrt/qwinrtbackingstore.h b/src/plugins/platforms/winrt/qwinrtbackingstore.h index 8be549b441..726f7c838f 100644 --- a/src/plugins/platforms/winrt/qwinrtbackingstore.h +++ b/src/plugins/platforms/winrt/qwinrtbackingstore.h @@ -62,6 +62,8 @@ public: void resize(const QSize &size, const QRegion &staticContents); private: + bool initialize(); + bool m_initialized; QSize m_size; QScopedPointer<QPaintDevice> m_paintDevice; QScopedPointer<QOpenGLContext> m_context; diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp index c7fa339fad..3da87de708 100644 --- a/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp +++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp @@ -44,6 +44,14 @@ #include <QtCore/QCoreApplication> #include <QtCore/QFile> +#ifndef Q_OS_WINPHONE +#include <QtCore/QUuid> +#include <QtGui/private/qfontengine_ft_p.h> +#include <dwrite_1.h> +#include <wrl.h> +using namespace Microsoft::WRL; +#endif // !Q_OS_WINPHONE + QT_BEGIN_NAMESPACE QString QWinRTFontDatabase::fontDir() const @@ -54,11 +62,315 @@ QString QWinRTFontDatabase::fontDir() const const QString applicationDirPath = QCoreApplication::applicationDirPath(); fontDirectory = applicationDirPath + QLatin1String("/fonts"); if (!QFile::exists(fontDirectory)) { - qWarning("No fonts directory found in application package."); +#ifndef Q_OS_WINPHONE + if (m_fonts.isEmpty()) +#endif + qWarning("No fonts directory found in application package."); fontDirectory = applicationDirPath; } } return fontDirectory; } +#ifndef Q_OS_WINPHONE + +QWinRTFontDatabase::~QWinRTFontDatabase() +{ + foreach (IDWriteFontFile *fontFile, m_fonts.keys()) + fontFile->Release(); +} + +QFont QWinRTFontDatabase::defaultFont() const +{ + return QFont(QStringLiteral("Segoe UI")); +} + +void QWinRTFontDatabase::populateFontDatabase() +{ + ComPtr<IDWriteFactory1> factory; + HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory1), &factory); + if (FAILED(hr)) { + qWarning("Failed to create DirectWrite factory: %s", qPrintable(qt_error_string(hr))); + QBasicFontDatabase::populateFontDatabase(); + return; + } + + ComPtr<IDWriteFontCollection> fontCollection; + hr = factory->GetSystemFontCollection(&fontCollection); + if (FAILED(hr)) { + qWarning("Failed to open system font collection: %s", qPrintable(qt_error_string(hr))); + QBasicFontDatabase::populateFontDatabase(); + return; + } + + int fontFamilyCount = fontCollection->GetFontFamilyCount(); + for (int i = 0; i < fontFamilyCount; ++i) { + ComPtr<IDWriteFontFamily> fontFamily; + hr = fontCollection->GetFontFamily(i, &fontFamily); + if (FAILED(hr)) { + qWarning("Unable to get font family: %s", qPrintable(qt_error_string(hr))); + continue; + } + + ComPtr<IDWriteLocalizedStrings> names; + hr = fontFamily->GetFamilyNames(&names); + if (FAILED(hr)) { + qWarning("Unable to get font family names: %s", qPrintable(qt_error_string(hr))); + continue; + } + quint32 familyNameLength; + hr = names->GetStringLength(0, &familyNameLength); + if (FAILED(hr)) { + qWarning("Unable to get family name length: %s", qPrintable(qt_error_string(hr))); + continue; + } + QVector<wchar_t> familyBuffer(familyNameLength + 1); + hr = names->GetString(0, familyBuffer.data(), familyBuffer.size()); + if (FAILED(hr)) { + qWarning("Unable to create font family name: %s", qPrintable(qt_error_string(hr))); + continue; + } + QString familyName = QString::fromWCharArray(familyBuffer.data(), familyNameLength); + + int fontCount = fontFamily->GetFontCount(); + for (int j = 0; j < fontCount; ++j) { + ComPtr<IDWriteFont> font; + hr = fontFamily->GetFont(j, &font); + if (FAILED(hr)) { + qWarning("Unable to get base font: %s", qPrintable(qt_error_string(hr))); + continue; + } + + ComPtr<IDWriteFontFace> baseFontFace; + hr = font->CreateFontFace(&baseFontFace); + if (FAILED(hr)) { + qWarning("Unable to create base font face: %s", qPrintable(qt_error_string(hr))); + continue; + } + ComPtr<IDWriteFontFace1> fontFace; + hr = baseFontFace.As(&fontFace); + if (FAILED(hr)) { + qWarning("Unable to create font face: %s", qPrintable(qt_error_string(hr))); + continue; + } + + // Only try to load true-type fonts + DWRITE_FONT_FACE_TYPE type = fontFace->GetType(); + if (!(type == DWRITE_FONT_FACE_TYPE_TRUETYPE + || type == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION)) { + continue; + } + + // We can't deal with multi-file fonts + quint32 fileCount; + hr = fontFace->GetFiles(&fileCount, NULL); + if (FAILED(hr)) { + qWarning("Unable to get font file count: %s", qPrintable(qt_error_string(hr))); + continue; + } + if (fileCount != 1) // Should not happen as we only look at TT fonts + continue; + + ComPtr<IDWriteLocalizedStrings> informationalStrings; + BOOL exists; + hr = font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_MANUFACTURER, + &informationalStrings, &exists); + if (FAILED(hr)) { + qWarning("Unable to get font foundry: %s", qPrintable(qt_error_string(hr))); + continue; + } + QString foundryName; + if (exists) { + quint32 length; + hr = informationalStrings->GetStringLength(0, &length); + if (FAILED(hr)) + qWarning("Unable to get foundry name length: %s", qPrintable(qt_error_string(hr))); + if (SUCCEEDED(hr)) { + QVector<wchar_t> buffer(length + 1); + hr = informationalStrings->GetString(0, buffer.data(), buffer.size()); + if (FAILED(hr)) + qWarning("Unable to get foundry name: %s", qPrintable(qt_error_string(hr))); + if (SUCCEEDED(hr)) + foundryName = QString::fromWCharArray(buffer.data(), length); + } + } + + QFont::Weight weight; + switch (font->GetWeight()) { + case DWRITE_FONT_WEIGHT_THIN: + case DWRITE_FONT_WEIGHT_EXTRA_LIGHT: + case DWRITE_FONT_WEIGHT_LIGHT: + case DWRITE_FONT_WEIGHT_SEMI_LIGHT: + weight = QFont::Light; + break; + default: + case DWRITE_FONT_WEIGHT_NORMAL: + case DWRITE_FONT_WEIGHT_MEDIUM: + weight = QFont::Normal; + break; + case DWRITE_FONT_WEIGHT_DEMI_BOLD: + weight = QFont::DemiBold; + break; + case DWRITE_FONT_WEIGHT_BOLD: + case DWRITE_FONT_WEIGHT_EXTRA_BOLD: + weight = QFont::Bold; + break; + case DWRITE_FONT_WEIGHT_BLACK: + case DWRITE_FONT_WEIGHT_EXTRA_BLACK: + weight = QFont::Black; + break; + } + + QFont::Style style; + switch (font->GetStyle()) { + default: + case DWRITE_FONT_STYLE_NORMAL: + style = QFont::StyleNormal; + break; + case DWRITE_FONT_STYLE_OBLIQUE: + style = QFont::StyleOblique; + break; + case DWRITE_FONT_STYLE_ITALIC: + style = QFont::StyleItalic; + break; + } + + QFont::Stretch stretch; + switch (font->GetStretch()) { + default: + case DWRITE_FONT_STRETCH_UNDEFINED: + case DWRITE_FONT_STRETCH_NORMAL: + stretch = QFont::Unstretched; + break; + case DWRITE_FONT_STRETCH_ULTRA_CONDENSED: + stretch = QFont::UltraCondensed; + break; + case DWRITE_FONT_STRETCH_EXTRA_CONDENSED: + stretch = QFont::ExtraCondensed; + break; + case DWRITE_FONT_STRETCH_CONDENSED: + stretch = QFont::Condensed; + break; + case DWRITE_FONT_STRETCH_SEMI_CONDENSED: + stretch = QFont::SemiCondensed; + break; + case DWRITE_FONT_STRETCH_SEMI_EXPANDED: + stretch = QFont::SemiExpanded; + break; + case DWRITE_FONT_STRETCH_EXPANDED: + stretch = QFont::Expanded; + break; + case DWRITE_FONT_STRETCH_EXTRA_EXPANDED: + stretch = QFont::ExtraExpanded; + break; + case DWRITE_FONT_STRETCH_ULTRA_EXPANDED: + stretch = QFont::UltraExpanded; + break; + } + + const bool fixedPitch = fontFace->IsMonospacedFont(); + + quint32 unicodeRange[4]; + quint32 actualRangeCount; + hr = fontFace->GetUnicodeRanges( + 2, reinterpret_cast<DWRITE_UNICODE_RANGE *>(unicodeRange), &actualRangeCount); + if (FAILED(hr) && hr != E_NOT_SUFFICIENT_BUFFER) { // Ignore insufficient buffer; we only need 4 indices + qWarning("Unable to get font unicode range: %s", qPrintable(qt_error_string(hr))); + continue; + } + quint32 codePageRange[2] = { 0, 0 }; + QSupportedWritingSystems writingSystems = + QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + + IDWriteFontFile *fontFile; + hr = fontFace->GetFiles(&fileCount, &fontFile); + if (FAILED(hr)) { + qWarning("Unable to get font file: %s", qPrintable(qt_error_string(hr))); + continue; + } + + FontDescription description = { fontFace->GetIndex(), QUuid::createUuid().toByteArray() }; + m_fonts.insert(fontFile, description); + registerFont(familyName, QString(), foundryName, weight, style, stretch, + true, true, 0, fixedPitch, writingSystems, fontFile); + } + } + + QBasicFontDatabase::populateFontDatabase(); +} + +QFontEngine *QWinRTFontDatabase::fontEngine(const QFontDef &fontDef, void *handle) +{ + IDWriteFontFile *fontFile = reinterpret_cast<IDWriteFontFile *>(handle); + if (!m_fonts.contains(fontFile)) + return QBasicFontDatabase::fontEngine(fontDef, handle); + + const void *referenceKey; + quint32 referenceKeySize; + HRESULT hr = fontFile->GetReferenceKey(&referenceKey, &referenceKeySize); + if (FAILED(hr)) { + qWarning("Unable to get font file reference key: %s", qPrintable(qt_error_string(hr))); + return 0; + } + + ComPtr<IDWriteFontFileLoader> loader; + hr = fontFile->GetLoader(&loader); + if (FAILED(hr)) { + qWarning("Unable to get font file loader: %s", qPrintable(qt_error_string(hr))); + return 0; + } + + ComPtr<IDWriteFontFileStream> stream; + hr =loader->CreateStreamFromKey(referenceKey, referenceKeySize, &stream); + if (FAILED(hr)) { + qWarning("Unable to get font file stream: %s", qPrintable(qt_error_string(hr))); + return 0; + } + + quint64 fileSize; + hr = stream->GetFileSize(&fileSize); + if (FAILED(hr)) { + qWarning("Unable to get font file size: %s", qPrintable(qt_error_string(hr))); + return 0; + } + + const void *data; + void *context; + hr = stream->ReadFileFragment(&data, 0, fileSize, &context); + if (FAILED(hr)) { + qWarning("Unable to get font file data: %s", qPrintable(qt_error_string(hr))); + return 0; + } + const QByteArray fontData((const char *)data, fileSize); + stream->ReleaseFileFragment(context); + + QFontEngine::FaceId faceId; + const FontDescription description = m_fonts.value(fontFile); + faceId.uuid = description.uuid; + faceId.index = description.index; + const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); + QFontEngineFT::GlyphFormat format = antialias ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono; + QFontEngineFT *engine = new QFontEngineFT(fontDef); + if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) { + delete engine; + return 0; + } + + return engine; +} + +void QWinRTFontDatabase::releaseHandle(void *handle) +{ + IDWriteFontFile *fontFile = reinterpret_cast<IDWriteFontFile *>(handle); + if (m_fonts.contains(fontFile)) { + m_fonts.remove(fontFile); + fontFile->Release(); + return; + } + + QBasicFontDatabase::releaseHandle(handle); +} + +#endif // !Q_OS_WINPHONE + QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.h b/src/plugins/platforms/winrt/qwinrtfontdatabase.h index 49e32470c2..6f194a10cc 100644 --- a/src/plugins/platforms/winrt/qwinrtfontdatabase.h +++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.h @@ -46,10 +46,29 @@ QT_BEGIN_NAMESPACE +#ifndef Q_OS_WINPHONE +struct IDWriteFontFile; + +struct FontDescription +{ + quint32 index; + QByteArray uuid; +}; +#endif + class QWinRTFontDatabase : public QBasicFontDatabase { public: QString fontDir() const; +#ifndef Q_OS_WINPHONE + ~QWinRTFontDatabase(); + QFont defaultFont() const Q_DECL_OVERRIDE; + void populateFontDatabase() Q_DECL_OVERRIDE; + QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) Q_DECL_OVERRIDE; + void releaseHandle(void *handle) Q_DECL_OVERRIDE; +private: + QHash<IDWriteFontFile *, FontDescription> m_fonts; +#endif // !Q_OS_WINPHONE }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtservices.cpp b/src/plugins/platforms/winrt/qwinrtservices.cpp index 8f0a1d55bb..73c090351b 100644 --- a/src/plugins/platforms/winrt/qwinrtservices.cpp +++ b/src/plugins/platforms/winrt/qwinrtservices.cpp @@ -106,8 +106,7 @@ bool QWinRTServices::openDocument(const QUrl &url) if (!(m_fileFactory && m_launcher)) return QPlatformServices::openDocument(url); - QString pathString = QDir::toNativeSeparators( - QDir::cleanPath(qApp->applicationDirPath().append(url.toString(QUrl::RemoveScheme)))); + const QString pathString = QDir::toNativeSeparators(url.toLocalFile()); HSTRING_HEADER header; HSTRING path; WindowsCreateStringReference((const wchar_t*)pathString.utf16(), pathString.length(), &header, &path); IAsyncOperation<StorageFile*> *fileOp; diff --git a/src/plugins/platforms/winrt/winrt.pro b/src/plugins/platforms/winrt/winrt.pro index 6e3cfb1d20..306bbc8174 100644 --- a/src/plugins/platforms/winrt/winrt.pro +++ b/src/plugins/platforms/winrt/winrt.pro @@ -11,6 +11,11 @@ DEFINES *= QT_NO_CAST_FROM_ASCII __WRL_NO_DEFAULT_LIB__ GL_GLEXT_PROTOTYPES LIBS += $$QMAKE_LIBS_CORE +!winphone { + LIBS += -ldwrite + INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/freetype/include +} + SOURCES = \ main.cpp \ qwinrtbackingstore.cpp \ diff --git a/src/plugins/platforms/xcb/qglxintegration.cpp b/src/plugins/platforms/xcb/qglxintegration.cpp index eaa4d05311..c183deb3b8 100644 --- a/src/plugins/platforms/xcb/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/qglxintegration.cpp @@ -130,7 +130,7 @@ static void updateFormatFromContext(QSurfaceFormat &format) } format.setProfile(QSurfaceFormat::NoProfile); - format.setOption(QSurfaceFormat::FormatOptions()); + format.setOptions(QSurfaceFormat::FormatOptions()); if (format.renderableType() == QSurfaceFormat::OpenGL) { if (format.version() < qMakePair(3, 0)) { @@ -211,7 +211,7 @@ QGLXContext::QGLXContext(QXcbScreen *screen, const QSurfaceFormat &format, QPlat // Don't bother with versions below ES 2.0 glVersions << 30 << 20; // ES does not support any format option - m_format.setOption(QSurfaceFormat::FormatOptions()); + m_format.setOptions(QSurfaceFormat::FormatOptions()); } Q_ASSERT(glVersions.count() > 0); diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 030090d98d..a68ae8cf71 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -144,23 +144,23 @@ void QXcbConnection::updateScreens() xcb_generic_error_t *error = NULL; xcb_randr_get_output_primary_cookie_t primaryCookie = xcb_randr_get_output_primary(xcb_connection(), xcbScreen->root); - xcb_randr_get_screen_resources_current_cookie_t resourcesCookie = - xcb_randr_get_screen_resources_current(xcb_connection(), xcbScreen->root); + xcb_randr_get_screen_resources_cookie_t resourcesCookie = + xcb_randr_get_screen_resources(xcb_connection(), xcbScreen->root); xcb_randr_get_output_primary_reply_t *primary = xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error); if (!primary || error) { qWarning("QXcbConnection: Failed to get the primary output of the screen"); free(error); } else { - xcb_randr_get_screen_resources_current_reply_t *resources = - xcb_randr_get_screen_resources_current_reply(xcb_connection(), resourcesCookie, &error); + xcb_randr_get_screen_resources_reply_t *resources = + xcb_randr_get_screen_resources_reply(xcb_connection(), resourcesCookie, &error); if (!resources || error) { qWarning("QXcbConnection: Failed to get the screen resources"); free(error); } else { xcb_timestamp_t timestamp = resources->config_timestamp; - outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources); - xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(resources); + outputCount = xcb_randr_get_screen_resources_outputs_length(resources); + xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(resources); for (int i = 0; i < outputCount; i++) { xcb_randr_get_output_info_reply_t *output = diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index 6042ec0f87..bcadcd1f02 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -872,7 +872,6 @@ void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event) } QWindowSystemInterface::handleDrag(w,0,QPoint(),Qt::IgnoreAction); - updateAction(Qt::IgnoreAction); xdnd_dragsource = 0; xdnd_types.clear(); @@ -1045,7 +1044,8 @@ void QXcbDrag::timerEvent(QTimerEvent* e) - showing dialog box on drop event where user's response takes more time than XdndDropTransactionTimeout (QTBUG-14493) - dnd takes unusually long time to process data */ - t.drag->deleteLater(); + if (t.drag) + t.drag->deleteLater(); transactions.removeAt(i--); } else { stopTimer = false; diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index 537898db48..7b0d337e7c 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -156,7 +156,7 @@ private: xcb_window_t proxy_target; QWindow *targetWindow; // QWidget *embedding_widget; - QDrag *drag; + QPointer<QDrag> drag; QTime time; }; QVector<Transaction> transactions; diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index aaa2e81c40..0bab341914 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -396,7 +396,6 @@ QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint) const case QPlatformIntegration::CursorFlashTime: case QPlatformIntegration::KeyboardInputInterval: case QPlatformIntegration::MouseDoubleClickInterval: - case QPlatformIntegration::StartDragDistance: case QPlatformIntegration::StartDragTime: case QPlatformIntegration::KeyboardAutoRepeatRate: case QPlatformIntegration::PasswordMaskDelay: @@ -406,6 +405,20 @@ QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint) const case QPlatformIntegration::PasswordMaskCharacter: // TODO using various xcb, gnome or KDE settings break; // Not implemented, use defaults + case QPlatformIntegration::StartDragDistance: { + // The default (in QPlatformTheme::defaultThemeHint) is 10 pixels, but + // on a high-resolution screen it makes sense to increase it. + const QList<QXcbScreen *> &screens = defaultConnection()->screens(); + qreal dpi = 100.0; + if (screens.length() > 0) { + const QXcbScreen *screen = screens.at(defaultConnection()->primaryScreen()); + if (screen->logicalDpi().first > dpi) + dpi = screen->logicalDpi().first; + if (screen->logicalDpi().second > dpi) + dpi = screen->logicalDpi().second; + } + return 10.0 * dpi / 100.0; + } case QPlatformIntegration::ShowIsFullScreen: // X11 always has support for windows, but the // window manager could prevent it (e.g. matchbox) diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index 966090dbd5..0a52640c9a 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -53,10 +53,6 @@ #include <qpa/qplatformintegration.h> #include <qpa/qplatformcursor.h> -#ifdef XKBCOMMON_0_2_0 -#include <xkbcommon_workaround.h> -#endif - #ifndef XK_ISO_Left_Tab #define XK_ISO_Left_Tab 0xFE20 #endif @@ -1398,23 +1394,7 @@ QString QXcbKeyboard::keysymToUnicode(xcb_keysym_t sym) const QByteArray chars; int bytes; chars.resize(7); - -#ifdef XKBCOMMON_0_2_0 - if (needWorkaround(sym)) { - quint32 codepoint; - if (sym == XKB_KEY_KP_Space) - codepoint = XKB_KEY_space & 0x7f; - else - codepoint = sym & 0x7f; - - bytes = utf32_to_utf8(codepoint, chars.data()); - } else { - bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size()); - } -#else bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size()); -#endif - if (bytes == -1) qWarning("QXcbKeyboard::handleKeyEvent - buffer too small"); chars.resize(bytes-1); diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index b1ef3182ba..9f19841437 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -371,7 +371,6 @@ void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *chan } QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry()); - QWindowSystemInterface::handleScreenAvailableGeometryChange(QPlatformScreen::screen(), availableGeometry()); QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), m_orientation); QDpi ldpi = logicalDpi(); @@ -409,6 +408,8 @@ void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp) m_availableGeometry = m_geometry & virtualAvailableGeometry; } free(workArea); + + QWindowSystemInterface::handleScreenAvailableGeometryChange(QPlatformScreen::screen(), m_availableGeometry); } void QXcbScreen::updateRefreshRate() diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index aabcb84a08..d890398416 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -1823,23 +1823,21 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev connection()->setTime(event->time); const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE; - const xcb_atom_t netWmStateAtom = atom(QXcbAtom::_NET_WM_STATE); - const xcb_atom_t wmStateAtom = atom(QXcbAtom::WM_STATE); - if (event->atom == netWmStateAtom || event->atom == wmStateAtom) { + if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) { if (propertyDeleted) return; Qt::WindowState newState = Qt::WindowNoState; - if (event->atom == wmStateAtom) { // WM_STATE: Quick check for 'Minimize'. + if (event->atom == atom(QXcbAtom::_NET_WM_STATE)) { // WM_STATE: Quick check for 'Minimize'. const xcb_get_property_cookie_t get_cookie = - xcb_get_property(xcb_connection(), 0, m_window, wmStateAtom, + xcb_get_property(xcb_connection(), 0, m_window, atom(QXcbAtom::_NET_WM_STATE), XCB_ATOM_ANY, 0, 1024); xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), get_cookie, NULL); - if (reply && reply->format == 32 && reply->type == wmStateAtom) { + if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::_NET_WM_STATE)) { const quint32 *data = (const quint32 *)xcb_get_property_value(reply); if (reply->length != 0 && XCB_WM_STATE_ICONIC == data[0]) newState = Qt::WindowMinimized; @@ -1860,6 +1858,8 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev m_windowState = newState; } return; + } else if (event->atom == atom(QXcbAtom::_NET_WORKAREA) && event->window == m_screen->root()) { + m_screen->updateGeometry(event->time); } } diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro index bfbec91e3c..e19bb921e1 100644 --- a/src/plugins/platforms/xcb/xcb-plugin.pro +++ b/src/plugins/platforms/xcb/xcb-plugin.pro @@ -131,12 +131,9 @@ contains(QT_CONFIG, xcb-qt) { # libxkbcommon contains(QT_CONFIG, xkbcommon-qt): { + QT_CONFIG += use-xkbcommon-x11support include(../../../3rdparty/xkbcommon.pri) } else { LIBS += $$QMAKE_LIBS_XKBCOMMON QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XKBCOMMON - equals(QMAKE_VERSION_XKBCOMMON, "0.2.0") { - DEFINES += XKBCOMMON_0_2_0 - INCLUDEPATH += ../../../3rdparty/xkbcommon/xkbcommon/ - } } diff --git a/src/plugins/printsupport/cups/cups.pro b/src/plugins/printsupport/cups/cups.pro index f617738a94..cdbb08b10a 100644 --- a/src/plugins/printsupport/cups/cups.pro +++ b/src/plugins/printsupport/cups/cups.pro @@ -6,13 +6,17 @@ load(qt_plugin) QT += core-private gui-private printsupport printsupport-private +LIBS_PRIVATE += -lcups + INCLUDEPATH += ../../../printsupport/kernel SOURCES += main.cpp \ + qppdprintdevice.cpp \ qcupsprintersupport.cpp \ qcupsprintengine.cpp HEADERS += qcupsprintersupport_p.h \ + qppdprintdevice.h \ qcupsprintengine_p.h OTHER_FILES += cups.json diff --git a/src/plugins/printsupport/cups/qcupsprintengine.cpp b/src/plugins/printsupport/cups/qcupsprintengine.cpp index 2fecdc00e9..ec9963197c 100644 --- a/src/plugins/printsupport/cups/qcupsprintengine.cpp +++ b/src/plugins/printsupport/cups/qcupsprintengine.cpp @@ -43,13 +43,17 @@ #ifndef QT_NO_PRINTER +#include <qpa/qplatformprintplugin.h> +#include <qpa/qplatformprintersupport.h> + #include <qiodevice.h> #include <qfile.h> #include <qdebug.h> #include <qbuffer.h> -#include "private/qcups_p.h" -#include "qprinterinfo.h" +#include "private/qcups_p.h" // Only needed for PPK_CupsOptions +#include <QtGui/qpagelayout.h> +#include <cups/cups.h> #include <limits.h> #include <math.h> @@ -57,26 +61,13 @@ QT_BEGIN_NAMESPACE +extern QMarginsF qt_convertMargins(const QMarginsF &margins, QPageLayout::Unit fromUnits, QPageLayout::Unit toUnits); + QCupsPrintEngine::QCupsPrintEngine(QPrinter::PrinterMode m) : QPdfPrintEngine(*new QCupsPrintEnginePrivate(m)) { Q_D(QCupsPrintEngine); - - if (QCUPSSupport::isAvailable()) { - QCUPSSupport cups; - const cups_dest_t* printers = cups.availablePrinters(); - int prnCount = cups.availablePrintersCount(); - - for (int i = 0; i < prnCount; ++i) { - if (printers[i].is_default) { - d->printerName = QString::fromLocal8Bit(printers[i].name); - d->setCupsDefaults(); - break; - } - } - - } - + d->setupDefaultPrinter(); state = QPrinter::Idle; } @@ -89,31 +80,37 @@ void QCupsPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &v Q_D(QCupsPrintEngine); switch (int(key)) { - case PPK_PaperSize: - d->printerPaperSize = QPrinter::PaperSize(value.toInt()); - d->setPaperSize(); + case PPK_PageSize: + d->setPageSize(QPageSize(QPageSize::PageSizeId(value.toInt()))); + break; + case PPK_WindowsPageSize: + d->setPageSize(QPageSize(QPageSize::id(value.toInt()))); break; - case PPK_CupsPageRect: - d->cupsPageRect = value.toRect(); + case PPK_CustomPaperSize: + d->setPageSize(QPageSize(value.toSizeF(), QPageSize::Point)); + break; + case PPK_PaperName: + // Get the named page size from the printer if supported + d->setPageSize(d->m_printDevice.supportedPageSize(value.toString())); break; - case PPK_CupsPaperRect: - d->cupsPaperRect = value.toRect(); + case PPK_PrinterName: + d->changePrinter(value.toString()); break; case PPK_CupsOptions: d->cupsOptions = value.toStringList(); break; - case PPK_CupsStringPageSize: - case PPK_PaperName: - d->cupsStringPageSize = value.toString(); - d->setPaperName(); + case PPK_QPageSize: + d->setPageSize(value.value<QPageSize>()); break; - case PPK_PrinterName: - // prevent setting the defaults again for the same printer - if (d->printerName != value.toString()) { - d->printerName = value.toString(); - d->setCupsDefaults(); + case PPK_QPageLayout: { + QPageLayout pageLayout = value.value<QPageLayout>(); + if (pageLayout.isValid() && d->m_printDevice.isValidPageLayout(pageLayout, d->resolution)) { + d->m_pageLayout = pageLayout; + // Replace the page size with the CUPS page size + d->setPageSize(d->m_printDevice.supportedPageSize(pageLayout.pageSize())); } break; + } default: QPdfPrintEngine::setProperty(key, value); break; @@ -127,24 +124,15 @@ QVariant QCupsPrintEngine::property(PrintEnginePropertyKey key) const QVariant ret; switch (int(key)) { case PPK_SupportsMultipleCopies: + // CUPS server always supports multiple copies, even if individual m_printDevice doesn't ret = true; break; case PPK_NumberOfCopies: ret = 1; break; - case PPK_CupsPageRect: - ret = d->cupsPageRect; - break; - case PPK_CupsPaperRect: - ret = d->cupsPaperRect; - break; case PPK_CupsOptions: ret = d->cupsOptions; break; - case PPK_CupsStringPageSize: - case PPK_PaperName: - ret = d->cupsStringPageSize; - break; default: ret = QPdfPrintEngine::property(key); break; @@ -173,17 +161,16 @@ bool QCupsPrintEnginePrivate::openPrintDevice() return false; } outDevice = file; - } else if (QCUPSSupport::isAvailable()) { - QCUPSSupport cups; - QPair<int, QString> ret = cups.tempFd(); - if (ret.first < 0) { + } else { + char filename[512]; + fd = cupsTempFd(filename, 512); + if (fd < 0) { qWarning("QPdfPrinter: Could not open temporary file to print"); return false; } - cupsTempFile = ret.second; + cupsTempFile = QString::fromLocal8Bit(filename); outDevice = new QFile(); - static_cast<QFile *>(outDevice)->open(ret.first, QIODevice::WriteOnly); - fd = ret.first; + static_cast<QFile *>(outDevice)->open(fd, QIODevice::WriteOnly); } return true; @@ -196,27 +183,19 @@ void QCupsPrintEnginePrivate::closePrintDevice() if (!cupsTempFile.isEmpty()) { QString tempFile = cupsTempFile; cupsTempFile.clear(); - QCUPSSupport cups; + + // Should never have got here without a printer, but check anyway + if (printerName.isEmpty()) { + qWarning("Could not determine printer to print to"); + QFile::remove(tempFile); + return; + } // Set up print options. - QByteArray prnName; QList<QPair<QByteArray, QByteArray> > options; QVector<cups_option_t> cupsOptStruct; - if (!printerName.isEmpty()) { - prnName = printerName.toLocal8Bit(); - } else { - QPrinterInfo def = QPrinterInfo::defaultPrinter(); - if (def.isNull()) { - qWarning("Could not determine printer to print to"); - QFile::remove(tempFile); - return; - } - prnName = def.printerName().toLocal8Bit(); - } - - if (!cupsStringPageSize.isEmpty()) - options.append(QPair<QByteArray, QByteArray>("media", cupsStringPageSize.toLocal8Bit())); + options.append(QPair<QByteArray, QByteArray>("media", m_pageLayout.pageSize().key().toLocal8Bit())); if (copies > 1) options.append(QPair<QByteArray, QByteArray>("copies", QString::number(copies).toLocal8Bit())); @@ -225,24 +204,24 @@ void QCupsPrintEnginePrivate::closePrintDevice() options.append(QPair<QByteArray, QByteArray>("Collate", "True")); switch (duplex) { - case QPrinter::DuplexNone: + case QPrint::DuplexNone: options.append(QPair<QByteArray, QByteArray>("sides", "one-sided")); break; - case QPrinter::DuplexAuto: - if (!landscape) + case QPrint::DuplexAuto: + if (m_pageLayout.orientation() == QPageLayout::Portrait) options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge")); else options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge")); break; - case QPrinter::DuplexLongSide: + case QPrint::DuplexLongSide: options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-long-edge")); break; - case QPrinter::DuplexShortSide: + case QPrint::DuplexShortSide: options.append(QPair<QByteArray, QByteArray>("sides", "two-sided-short-edge")); break; } - if (QCUPSSupport::cupsVersion() >= 10300 && landscape) + if (m_pageLayout.orientation() == QPageLayout::Landscape) options.append(QPair<QByteArray, QByteArray>("landscape", "")); QStringList::const_iterator it = cupsOptions.constBegin(); @@ -260,164 +239,85 @@ void QCupsPrintEnginePrivate::closePrintDevice() // Print the file. cups_option_t* optPtr = cupsOptStruct.size() ? &cupsOptStruct.first() : 0; - cups.printFile(prnName.constData(), tempFile.toLocal8Bit().constData(), - title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr); + cupsPrintFile(printerName.toLocal8Bit().constData(), tempFile.toLocal8Bit().constData(), + title.toLocal8Bit().constData(), cupsOptStruct.size(), optPtr); QFile::remove(tempFile); } } -void QCupsPrintEnginePrivate::updatePaperSize() +void QCupsPrintEnginePrivate::setupDefaultPrinter() { - if (printerPaperSize == QPrinter::Custom) { - paperSize = customPaperSize; - } else if (!cupsPaperRect.isNull()) { - QRect r = cupsPaperRect; - paperSize = r.size(); - } else { - QPdf::PaperSize s = QPdf::paperSize(printerPaperSize); - paperSize = QSize(s.width, s.height); + // Should never have reached here if no plugin available, but check just in case + QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get(); + if (!ps) + return; + + // Get default printer id, if no default then use the first available + // TODO Find way to remove printerName from base class? + printerName = ps->defaultPrintDeviceId(); + if (printerName.isEmpty()) { + QStringList list = ps->availablePrintDeviceIds(); + if (list.size() > 0) + printerName = list.at(0); } -} -void QCupsPrintEnginePrivate::setPaperSize() -{ - if (QCUPSSupport::isAvailable()) { - QCUPSSupport cups; - QPdf::PaperSize size = QPdf::paperSize(QPrinter::PaperSize(printerPaperSize)); - - if (cups.currentPPD()) { - cupsStringPageSize = QLatin1String("Custom"); - const ppd_option_t* pageSizes = cups.pageSizes(); - for (int i = 0; i < pageSizes->num_choices; ++i) { - QByteArray cupsPageSize = pageSizes->choices[i].choice; - QRect tmpCupsPaperRect = cups.paperRect(cupsPageSize); - QRect tmpCupsPageRect = cups.pageRect(cupsPageSize); - - if (qAbs(size.width - tmpCupsPaperRect.width()) < 5 && qAbs(size.height - tmpCupsPaperRect.height()) < 5) { - cupsPaperRect = tmpCupsPaperRect; - cupsPageRect = tmpCupsPageRect; - cupsStringPageSize = pageSizes->choices[i].text; - leftMargin = cupsPageRect.x() - cupsPaperRect.x(); - topMargin = cupsPageRect.y() - cupsPaperRect.y(); - rightMargin = cupsPaperRect.right() - cupsPageRect.right(); - bottomMargin = cupsPaperRect.bottom() - cupsPageRect.bottom(); - - updatePaperSize(); - break; - } - } - } - } + // Should never have reached here if no printers available, but check just in case + if (printerName.isEmpty()) + return; + + m_printDevice = ps->createPrintDevice(printerName); + if (!m_printDevice.isValid()) + return; + + // Setup the printer defaults + duplex = m_printDevice.defaultDuplexMode(); + grayscale = m_printDevice.defaultColorMode() == QPrint::GrayScale; + // CUPS server always supports collation, even if individual m_printDevice doesn't + collate = true; + setPageSize(m_printDevice.defaultPageSize()); } -void QCupsPrintEnginePrivate::setPaperName() +void QCupsPrintEnginePrivate::changePrinter(const QString &newPrinter) { - if (QCUPSSupport::isAvailable()) { - QCUPSSupport cups; - if (cups.currentPPD()) { - const ppd_option_t* pageSizes = cups.pageSizes(); - bool foundPaperName = false; - for (int i = 0; i < pageSizes->num_choices; ++i) { - if (cupsStringPageSize == pageSizes->choices[i].text) { - foundPaperName = true; - QByteArray cupsPageSize = pageSizes->choices[i].choice; - cupsPaperRect = cups.paperRect(cupsPageSize); - cupsPageRect = cups.pageRect(cupsPageSize); - leftMargin = cupsPageRect.x() - cupsPaperRect.x(); - topMargin = cupsPageRect.y() - cupsPaperRect.y(); - rightMargin = cupsPaperRect.right() - cupsPageRect.right(); - bottomMargin = cupsPaperRect.bottom() - cupsPageRect.bottom(); - printerPaperSize = QPrinter::Custom; - customPaperSize = cupsPaperRect.size(); - for (int ps = 0; ps < QPrinter::NPageSize; ++ps) { - QPdf::PaperSize size = QPdf::paperSize(QPrinter::PaperSize(ps)); - if (qAbs(size.width - cupsPaperRect.width()) < 5 && qAbs(size.height - cupsPaperRect.height()) < 5) { - printerPaperSize = static_cast<QPrinter::PaperSize>(ps); - customPaperSize = QSize(); - break; - } - } - updatePaperSize(); - break; - } - } - if (!foundPaperName) - cupsStringPageSize = QString(); - } - } + // Don't waste time if same printer name + if (newPrinter == printerName) + return; + + // Should never have reached here if no plugin available, but check just in case + QPlatformPrinterSupport *ps = QPlatformPrinterSupportPlugin::get(); + if (!ps) + return; + + // Try create the printer, only use it if it returns valid + QPrintDevice printDevice = ps->createPrintDevice(newPrinter); + if (!m_printDevice.isValid()) + return; + m_printDevice.swap(printDevice); + printerName = m_printDevice.id(); + + // Check if new printer supports current settings, otherwise us defaults + if (duplex != QPrint::DuplexAuto && !m_printDevice.supportedDuplexModes().contains(duplex)) + duplex = m_printDevice.defaultDuplexMode(); + QPrint::ColorMode colorMode = grayscale ? QPrint::GrayScale : QPrint::Color; + if (!m_printDevice.supportedColorModes().contains(colorMode)) + grayscale = m_printDevice.defaultColorMode() == QPrint::GrayScale; + + // Get the equivalent page size for this printer as supported names may be different + setPageSize(m_pageLayout.pageSize()); } -void QCupsPrintEnginePrivate::setCupsDefaults() +void QCupsPrintEnginePrivate::setPageSize(const QPageSize &pageSize) { - if (QCUPSSupport::isAvailable()) { - int cupsPrinterIndex = -1; - QCUPSSupport cups; - - const cups_dest_t* printers = cups.availablePrinters(); - int prnCount = cups.availablePrintersCount(); - for (int i = 0; i < prnCount; ++i) { - QString name = QString::fromLocal8Bit(printers[i].name); - if (name == printerName) { - cupsPrinterIndex = i; - break; - } - } - - if (cupsPrinterIndex < 0) - return; - - cups.setCurrentPrinter(cupsPrinterIndex); - - if (cups.currentPPD()) { - const ppd_option_t *ppdDuplex = cups.ppdOption("Duplex"); - if (ppdDuplex) { - if (qstrcmp(ppdDuplex->defchoice, "DuplexTumble") == 0) - duplex = QPrinter::DuplexShortSide; - else if (qstrcmp(ppdDuplex->defchoice, "DuplexNoTumble") == 0) - duplex = QPrinter::DuplexLongSide; - else - duplex = QPrinter::DuplexNone; - } - - grayscale = !cups.currentPPD()->color_device; - - const ppd_option_t *ppdCollate = cups.ppdOption("Collate"); - if (ppdCollate) - collate = qstrcmp(ppdCollate->defchoice, "True") == 0; - - const ppd_option_t* pageSizes = cups.pageSizes(); - QByteArray cupsPageSize; - for (int i = 0; i < pageSizes->num_choices; ++i) { - if (static_cast<int>(pageSizes->choices[i].marked) == 1) { - cupsPageSize = pageSizes->choices[i].choice; - cupsStringPageSize = pageSizes->choices[i].text; - } - } - - cupsOptions = cups.options(); - cupsPaperRect = cups.paperRect(cupsPageSize); - cupsPageRect = cups.pageRect(cupsPageSize); - - for (int ps = 0; ps < QPrinter::NPageSize; ++ps) { - QPdf::PaperSize size = QPdf::paperSize(QPrinter::PaperSize(ps)); - if (qAbs(size.width - cupsPaperRect.width()) < 5 && qAbs(size.height - cupsPaperRect.height()) < 5) { - printerPaperSize = static_cast<QPrinter::PaperSize>(ps); - - leftMargin = cupsPageRect.x() - cupsPaperRect.x(); - topMargin = cupsPageRect.y() - cupsPaperRect.y(); - rightMargin = cupsPaperRect.right() - cupsPageRect.right(); - bottomMargin = cupsPaperRect.bottom() - cupsPageRect.bottom(); - - updatePaperSize(); - break; - } - } - } + if (pageSize.isValid()) { + // Find if the requested page size has a matching printer page size, if so use its defined name instead + QPageSize printerPageSize = m_printDevice.supportedPageSize(pageSize); + QPageSize usePageSize = printerPageSize.isValid() ? printerPageSize : pageSize; + QMarginsF printable = m_printDevice.printableMargins(usePageSize, m_pageLayout.orientation(), resolution); + m_pageLayout.setPageSize(usePageSize, qt_convertMargins(printable, QPageLayout::Point, m_pageLayout.units())); } } - QT_END_NAMESPACE #endif // QT_NO_PRINTER diff --git a/src/plugins/printsupport/cups/qcupsprintengine_p.h b/src/plugins/printsupport/cups/qcupsprintengine_p.h index db947a0232..393fef42a3 100644 --- a/src/plugins/printsupport/cups/qcupsprintengine_p.h +++ b/src/plugins/printsupport/cups/qcupsprintengine_p.h @@ -61,9 +61,8 @@ #include <QtGui/qpaintengine.h> #include <private/qpaintengine_p.h> +#include <private/qprintdevice_p.h> #include <private/qprintengine_pdf_p.h> -#include <QtPrintSupport/qprintengine.h> -#include <private/qcups_p.h> QT_BEGIN_NAMESPACE @@ -95,18 +94,15 @@ public: bool openPrintDevice(); void closePrintDevice(); - void updatePaperSize(); - void setPaperSize(); - void setPaperName(); - void setCupsDefaults(); - private: Q_DISABLE_COPY(QCupsPrintEnginePrivate) + void setupDefaultPrinter(); + void changePrinter(const QString &newPrinter); + void setPageSize(const QPageSize &pageSize); + + QPrintDevice m_printDevice; QStringList cupsOptions; - QString cupsStringPageSize; - QRect cupsPaperRect; - QRect cupsPageRect; QString cupsTempFile; }; diff --git a/src/plugins/printsupport/cups/qcupsprintersupport.cpp b/src/plugins/printsupport/cups/qcupsprintersupport.cpp index b9f0c394f8..b2abb07fc7 100644 --- a/src/plugins/printsupport/cups/qcupsprintersupport.cpp +++ b/src/plugins/printsupport/cups/qcupsprintersupport.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 John Layt <jlayt@kde.org> ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -44,7 +45,9 @@ #ifndef QT_NO_PRINTER #include "qcupsprintengine_p.h" +#include "qppdprintdevice.h" #include <private/qprinterinfo_p.h> +#include <private/qprintdevice_p.h> #include <QtPrintSupport/QPrinterInfo> @@ -55,18 +58,13 @@ QT_BEGIN_NAMESPACE -QCupsPrinterSupport::QCupsPrinterSupport() : QPlatformPrinterSupport(), - m_cups(QLatin1String("cups"), 2), - m_cupsPrinters(0), - m_cupsPrintersCount(0) +QCupsPrinterSupport::QCupsPrinterSupport() + : QPlatformPrinterSupport() { - loadCups(); - loadCupsPrinters(); } QCupsPrinterSupport::~QCupsPrinterSupport() { - freeCupsPrinters(); } QPrintEngine *QCupsPrinterSupport::createNativePrintEngine(QPrinter::PrinterMode printerMode) @@ -80,85 +78,42 @@ QPaintEngine *QCupsPrinterSupport::createPaintEngine(QPrintEngine *engine, QPrin return static_cast<QCupsPrintEngine *>(engine); } -QList<QPrinter::PaperSize> QCupsPrinterSupport::supportedPaperSizes(const QPrinterInfo &printerInfo) const +QPrintDevice QCupsPrinterSupport::createPrintDevice(const QString &id) { - return QCUPSSupport::getCupsPrinterPaperSizes(printerIndex(printerInfo)); + return QPlatformPrinterSupport::createPrintDevice(new QPpdPrintDevice(id)); } -QList<QPair<QString, QSizeF> > QCupsPrinterSupport::supportedSizesWithNames(const QPrinterInfo &printerInfo) const +QStringList QCupsPrinterSupport::availablePrintDeviceIds() const { - return QCUPSSupport::getCupsPrinterPaperSizesWithNames(printerIndex(printerInfo)); -} - -void QCupsPrinterSupport::loadCups() -{ - cupsGetDests = (CupsGetDests) m_cups.resolve("cupsGetDests"); - cupsFreeDests = (CupsFreeDests) m_cups.resolve("cupsFreeDests"); - cupsGetOption = (CupsGetOption) m_cups.resolve("cupsGetOption"); -} - -void QCupsPrinterSupport::freeCupsPrinters() -{ - if (cupsFreeDests && m_cupsPrintersCount) { - cupsFreeDests(m_cupsPrintersCount, m_cupsPrinters); - m_cupsPrintersCount = 0; - m_cupsPrinters = 0; + QStringList list; + cups_dest_t *dests; + int count = cupsGetDests(&dests); + for (int i = 0; i < count; ++i) { + QString printerId = QString::fromLocal8Bit(dests[i].name); + if (dests[i].instance) + printerId += QLatin1Char('/') + QString::fromLocal8Bit(dests[i].instance); + list.append(printerId); } + cupsFreeDests(count, dests); + return list; } -void QCupsPrinterSupport::loadCupsPrinters() -{ - freeCupsPrinters(); - m_printers.clear(); - - if (cupsGetDests) - m_cupsPrintersCount = cupsGetDests(&m_cupsPrinters); - - for (int i = 0; i < m_cupsPrintersCount; ++i) { - QString printerName = QString::fromLocal8Bit(m_cupsPrinters[i].name); - if (m_cupsPrinters[i].instance) - printerName += QLatin1Char('/') + QString::fromLocal8Bit(m_cupsPrinters[i].instance); - QString description = cupsOption(i, "printer-info"); - QString location = cupsOption(i, "printer-location"); - QString makeAndModel = cupsOption(i, "printer-make-and-model"); - QPrinterInfo printer = createPrinterInfo(printerName, description, location, makeAndModel, - m_cupsPrinters[i].is_default, i); - m_printers.append(printer); - } -} - -QList<QPrinterInfo> QCupsPrinterSupport::availablePrinters() -{ - loadCupsPrinters(); - return QPlatformPrinterSupport::availablePrinters(); -} - -QString QCupsPrinterSupport::printerOption(const QPrinterInfo &printer, const QString &key) const -{ - return cupsOption(printerIndex(printer), key); -} - -QString QCupsPrinterSupport::cupsOption(int i, const QString &key) const -{ - QString value; - if (i > -1 && i < m_cupsPrintersCount && cupsGetOption) - value = cupsGetOption(key.toLocal8Bit(), m_cupsPrinters[i].num_options, m_cupsPrinters[i].options); - return value; -} - -PrinterOptions QCupsPrinterSupport::printerOptions(const QPrinterInfo &printer) const +QString QCupsPrinterSupport::defaultPrintDeviceId() const { - PrinterOptions options; - int p = printerIndex(printer); - if (p <= -1 || p >= m_cupsPrintersCount) - return options; - int numOptions = m_cupsPrinters[p].num_options; - for (int i = 0; i < numOptions; ++i) { - QString name = m_cupsPrinters[p].options[i].name; - QString value = m_cupsPrinters[p].options[i].value; - options.insert(name, value); + QString printerId; + cups_dest_t *dests; + int count = cupsGetDests(&dests); + for (int i = 0; i < count; ++i) { + if (dests[i].is_default) { + printerId = QString::fromLocal8Bit(dests[i].name); + if (dests[i].instance) { + printerId += QLatin1Char('/') + QString::fromLocal8Bit(dests[i].instance); + break; + } + } } - return options; + cupsFreeDests(count, dests); + return printerId; } QT_END_NAMESPACE diff --git a/src/plugins/printsupport/cups/qcupsprintersupport_p.h b/src/plugins/printsupport/cups/qcupsprintersupport_p.h index d42c0d2630..1cba4e997b 100644 --- a/src/plugins/printsupport/cups/qcupsprintersupport_p.h +++ b/src/plugins/printsupport/cups/qcupsprintersupport_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 John Layt <jlayt@kde.org> ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -42,49 +43,29 @@ #ifndef QCUPSPRINTERSUPPORT_H #define QCUPSPRINTERSUPPORT_H -#include <QtCore/qfeatures.h> // Some feature dependencies might define QT_NO_PRINTER -#ifndef QT_NO_PRINTER - #include <qpa/qplatformprintersupport.h> -#include <QtCore/qlibrary.h> -#include <QtCore/qlist.h> +#ifndef QT_NO_PRINTER -#include <cups/cups.h> +#include <QtCore/qstringlist.h> QT_BEGIN_NAMESPACE -typedef int (*CupsGetDests)(cups_dest_t **dests); -typedef void (*CupsFreeDests)(int num_dests, cups_dest_t *dests); -typedef const char* (*CupsGetOption)(const char *name, int num_options, cups_option_t *options); - class QCupsPrinterSupport : public QPlatformPrinterSupport { public: QCupsPrinterSupport(); ~QCupsPrinterSupport(); - 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 QList<QPrinterInfo> availablePrinters(); - virtual QString printerOption(const QPrinterInfo &printer, const QString &key) const; - virtual PrinterOptions printerOptions(const QPrinterInfo &printer) const; + QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode) Q_DECL_OVERRIDE; + QPaintEngine *createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode) Q_DECL_OVERRIDE; + + QPrintDevice createPrintDevice(const QString &id) Q_DECL_OVERRIDE; + QStringList availablePrintDeviceIds() const Q_DECL_OVERRIDE; + QString defaultPrintDeviceId() const Q_DECL_OVERRIDE; private: - void loadCups(); - void loadCupsPrinters(); - void freeCupsPrinters(); QString cupsOption(int i, const QString &key) const; - - QLibrary m_cups; - cups_dest_t *m_cupsPrinters; - int m_cupsPrintersCount; - - CupsGetDests cupsGetDests; - CupsFreeDests cupsFreeDests; - CupsGetOption cupsGetOption; }; QT_END_NAMESPACE diff --git a/src/plugins/printsupport/cups/qppdprintdevice.cpp b/src/plugins/printsupport/cups/qppdprintdevice.cpp new file mode 100644 index 0000000000..56ae5600c4 --- /dev/null +++ b/src/plugins/printsupport/cups/qppdprintdevice.cpp @@ -0,0 +1,503 @@ +/**************************************************************************** +** +** Copyright (C) 2014 John Layt <jlayt@kde.org> +** 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 "qppdprintdevice.h" + +#include <QtCore/QMimeDatabase> +#include <qdebug.h> + +#ifndef QT_LINUXBASE // LSB merges everything into cups.h +#include <cups/language.h> +#endif + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PRINTER + +QPpdPrintDevice::QPpdPrintDevice() + : QPlatformPrintDevice(), + m_cupsDest(0), + m_ppd(0) +{ +} + +QPpdPrintDevice::QPpdPrintDevice(const QString &id) + : QPlatformPrintDevice(id), + m_cupsDest(0), + m_ppd(0) +{ + if (!id.isEmpty()) { + + // TODO For now each dest is an individual device + QStringList parts = id.split(QLatin1Char('/')); + m_cupsName = parts.at(0).toUtf8(); + if (parts.size() > 1) + m_cupsInstance = parts.at(1).toUtf8(); + loadPrinter(); + + if (m_cupsDest && m_ppd) { + m_name = printerOption("printer-info"); + m_location = printerOption("printer-location"); + m_makeAndModel = printerOption("printer-make-and-model"); + cups_ptype_e type = printerTypeFlags(); + m_isRemote = type & CUPS_PRINTER_REMOTE; + // Note this is if the hardware does multiple copies, not if Cups can + m_supportsMultipleCopies = type & CUPS_PRINTER_COPIES; + // Note this is if the hardware does collation, not if Cups can + m_supportsCollateCopies = type & CUPS_PRINTER_COLLATE; + + // Custom Page Size support + // Cups cups_ptype_e CUPS_PRINTER_VARIABLE + // Cups ppd_file_t variable_sizes custom_min custom_max + // PPD MaxMediaWidth MaxMediaHeight + m_supportsCustomPageSizes = type & CUPS_PRINTER_VARIABLE; + m_minimumPhysicalPageSize = QSize(m_ppd->custom_min[0], m_ppd->custom_min[1]); + m_maximumPhysicalPageSize = QSize(m_ppd->custom_max[0], m_ppd->custom_max[1]); + m_customMargins = QMarginsF(m_ppd->custom_margins[0], m_ppd->custom_margins[3], + m_ppd->custom_margins[2], m_ppd->custom_margins[1]); + } + } +} + +QPpdPrintDevice::QPpdPrintDevice(const QPpdPrintDevice &other) + : QPlatformPrintDevice(other), + m_cupsDest(0), + m_ppd(0) +{ + m_cupsName = other.m_cupsName; + m_cupsInstance = other.m_cupsInstance; + loadPrinter(); +} + +QPpdPrintDevice::~QPpdPrintDevice() +{ + if (m_ppd) + ppdClose(m_ppd); + if (m_cupsDest) + cupsFreeDests(1, m_cupsDest); + m_cupsDest = 0; + m_ppd = 0; +} + +QPpdPrintDevice &QPpdPrintDevice::operator=(const QPpdPrintDevice &other) +{ + m_cupsName = other.m_cupsName; + m_cupsInstance = other.m_cupsInstance; + if (other.m_cupsDest && other.m_ppd) + loadPrinter(); + return *this; +} + +bool QPpdPrintDevice::operator==(const QPpdPrintDevice &other) const +{ + return (m_id == other.m_id); +} + +bool QPpdPrintDevice::isValid() const +{ + return m_cupsDest && m_ppd; +} + +bool QPpdPrintDevice::isDefault() const +{ + return printerTypeFlags() & CUPS_PRINTER_DEFAULT; +} + +QPrint::DeviceState QPpdPrintDevice::state() const +{ + // 3 = idle, 4 = printing, 5 = stopped + // More details available from printer-state-message and printer-state-reasons + int state = printerOption(QStringLiteral("printer-state")).toInt(); + if (state == 3) + return QPrint::Idle; + else if (state == 4) + return QPrint::Active; + else + return QPrint::Error; +} + +void QPpdPrintDevice::loadPageSizes() const +{ + m_pageSizes.clear(); + m_printableMargins.clear(); + + ppd_option_t *pageSizes = ppdFindOption(m_ppd, "PageSize"); + if (pageSizes) { + for (int i = 0; i < pageSizes->num_choices; ++i) { + const ppd_size_t *ppdSize = ppdPageSize(m_ppd, pageSizes->choices[i].choice); + if (ppdSize) { + // Returned size is in points + QString key = QString::fromUtf8(ppdSize->name); + QSize size = QSize(qRound(ppdSize->width), qRound(ppdSize->length)); + QString name = QString::fromUtf8(pageSizes->choices[i].text); + if (!size.isEmpty()) { + QPageSize ps = createPageSize(key, size, name); + if (ps.isValid()) { + m_pageSizes.append(ps); + m_printableMargins.insert(key, QMarginsF(ppdSize->left, ppdSize->length - ppdSize->top, + ppdSize->width - ppdSize->right, ppdSize->bottom)); + } + } + } + } + m_havePageSizes = true; + } +} + +QPageSize QPpdPrintDevice::defaultPageSize() const +{ + ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "PageSize"); + if (defaultChoice) { + ppd_size_t *ppdSize = ppdPageSize(m_ppd, defaultChoice->choice); + if (ppdSize) { + // Returned size is in points + QString key = QString::fromUtf8(ppdSize->name); + QSize size = QSize(qRound(ppdSize->width), qRound(ppdSize->length)); + QString name = QString::fromUtf8(defaultChoice->text); + return createPageSize(key, size, name); + } + } + return QPageSize(); +} + +QMarginsF QPpdPrintDevice::printableMargins(const QPageSize &pageSize, + QPageLayout::Orientation orientation, + int resolution) const +{ + Q_UNUSED(orientation) + Q_UNUSED(resolution) + if (!m_havePageSizes) + loadPageSizes(); + // TODO Orientation? + if (m_printableMargins.contains(pageSize.key())) + return m_printableMargins.value(pageSize.key()); + return m_customMargins; +} + +void QPpdPrintDevice::loadResolutions() const +{ + m_resolutions.clear(); + + // Try load standard PPD options first + ppd_option_t *resolutions = ppdFindOption(m_ppd, "Resolution"); + if (resolutions) { + for (int i = 0; i < resolutions->num_choices; ++i) { + int res = QPrintUtils::parsePpdResolution(resolutions->choices[i].choice); + if (res > 0) + m_resolutions.append(res); + } + } + // If no result, try just the default + if (m_resolutions.size() == 0) { + resolutions = ppdFindOption(m_ppd, "DefaultResolution"); + if (resolutions) { + int res = QPrintUtils::parsePpdResolution(resolutions->choices[0].choice); + if (res > 0) + m_resolutions.append(res); + } + } + // If still no result, then try HP's custom options + if (m_resolutions.size() == 0) { + resolutions = ppdFindOption(m_ppd, "HPPrintQuality"); + if (resolutions) { + for (int i = 0; i < resolutions->num_choices; ++i) { + int res = QPrintUtils::parsePpdResolution(resolutions->choices[i].choice); + if (res > 0) + m_resolutions.append(res); + } + } + } + if (m_resolutions.size() == 0) { + resolutions = ppdFindOption(m_ppd, "DefaultHPPrintQuality"); + if (resolutions) { + int res = QPrintUtils::parsePpdResolution(resolutions->choices[0].choice); + if (res > 0) + m_resolutions.append(res); + } + } + m_haveResolutions = true; +} + +int QPpdPrintDevice::defaultResolution() const +{ + // Try load standard PPD option first + ppd_option_t *resolution = ppdFindOption(m_ppd, "DefaultResolution"); + if (resolution) { + int res = QPrintUtils::parsePpdResolution(resolution->choices[0].choice); + if (res > 0) + return res; + } + // If no result, then try a marked option + ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "Resolution"); + if (defaultChoice) { + int res = QPrintUtils::parsePpdResolution(defaultChoice->choice); + if (res > 0) + return res; + } + // If still no result, then try HP's custom options + resolution = ppdFindOption(m_ppd, "DefaultHPPrintQuality"); + if (resolution) { + int res = QPrintUtils::parsePpdResolution(resolution->choices[0].choice); + if (res > 0) + return res; + } + defaultChoice = ppdFindMarkedChoice(m_ppd, "HPPrintQuality"); + if (defaultChoice) { + int res = QPrintUtils::parsePpdResolution(defaultChoice->choice); + if (res > 0) + return res; + } + // Otherwise return a sensible default. + // TODO What is sensible? 150? 300? + return 72; +} + +void QPpdPrintDevice::loadInputSlots() const +{ + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // TODO Deal with concatenated names like Tray1Manual or Tray1_Man, + // will currently show as CustomInputSlot + // TODO Deal with separate ManualFeed key + // Try load standard PPD options first + m_inputSlots.clear(); + if (m_ppd) { + ppd_option_t *inputSlots = ppdFindOption(m_ppd, "InputSlot"); + if (inputSlots) { + for (int i = 0; i < inputSlots->num_choices; ++i) + m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[i])); + } + // If no result, try just the default + if (m_inputSlots.size() == 0) { + inputSlots = ppdFindOption(m_ppd, "DefaultInputSlot"); + if (inputSlots) + m_inputSlots.append(QPrintUtils::ppdChoiceToInputSlot(inputSlots->choices[0])); + } + } + // If still no result, just use Auto + if (m_inputSlots.size() == 0) + m_inputSlots.append(QPlatformPrintDevice::defaultInputSlot()); + m_haveInputSlots = true; +} + +QPrint::InputSlot QPpdPrintDevice::defaultInputSlot() const +{ + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Try load standard PPD option first + if (m_ppd) { + ppd_option_t *inputSlot = ppdFindOption(m_ppd, "DefaultInputSlot"); + if (inputSlot) + return QPrintUtils::ppdChoiceToInputSlot(inputSlot->choices[0]); + // If no result, then try a marked option + ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "InputSlot"); + if (defaultChoice) + return QPrintUtils::ppdChoiceToInputSlot(*defaultChoice); + } + // Otherwise return Auto + return QPlatformPrintDevice::defaultInputSlot(); +} + +void QPpdPrintDevice::loadOutputBins() const +{ + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + m_outputBins.clear(); + if (m_ppd) { + ppd_option_t *outputBins = ppdFindOption(m_ppd, "OutputBin"); + if (outputBins) { + for (int i = 0; i < outputBins->num_choices; ++i) + m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[i])); + } + // If no result, try just the default + if (m_outputBins.size() == 0) { + outputBins = ppdFindOption(m_ppd, "DefaultOutputBin"); + if (outputBins) + m_outputBins.append(QPrintUtils::ppdChoiceToOutputBin(outputBins->choices[0])); + } + } + // If still no result, just use Auto + if (m_outputBins.size() == 0) + m_outputBins.append(QPlatformPrintDevice::defaultOutputBin()); + m_haveOutputBins = true; +} + +QPrint::OutputBin QPpdPrintDevice::defaultOutputBin() const +{ + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Try load standard PPD option first + if (m_ppd) { + ppd_option_t *outputBin = ppdFindOption(m_ppd, "DefaultOutputBin"); + if (outputBin) + return QPrintUtils::ppdChoiceToOutputBin(outputBin->choices[0]); + // If no result, then try a marked option + ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "OutputBin"); + if (defaultChoice) + return QPrintUtils::ppdChoiceToOutputBin(*defaultChoice); + } + // Otherwise return AutoBin + return QPlatformPrintDevice::defaultOutputBin(); +} + +void QPpdPrintDevice::loadDuplexModes() const +{ + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Try load standard PPD options first + m_duplexModes.clear(); + if (m_ppd) { + ppd_option_t *duplexModes = ppdFindOption(m_ppd, "Duplex"); + if (duplexModes) { + for (int i = 0; i < duplexModes->num_choices; ++i) + m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[i].choice)); + } + // If no result, try just the default + if (m_duplexModes.size() == 0) { + duplexModes = ppdFindOption(m_ppd, "DefaultDuplex"); + if (duplexModes) + m_duplexModes.append(QPrintUtils::ppdChoiceToDuplexMode(duplexModes->choices[0].choice)); + } + } + // If still no result, or not added in PPD, then add None + if (m_duplexModes.size() == 0 || !m_duplexModes.contains(QPrint::DuplexNone)) + m_duplexModes.append(QPrint::DuplexNone); + m_haveDuplexModes = true; +} + +QPrint::DuplexMode QPpdPrintDevice::defaultDuplexMode() const +{ + // Try load standard PPD option first + if (m_ppd) { + ppd_option_t *inputSlot = ppdFindOption(m_ppd, "DefaultDuplex"); + if (inputSlot) + return QPrintUtils::ppdChoiceToDuplexMode(inputSlot->choices[0].choice); + // If no result, then try a marked option + ppd_choice_t *defaultChoice = ppdFindMarkedChoice(m_ppd, "Duplex"); + if (defaultChoice) + return QPrintUtils::ppdChoiceToDuplexMode(defaultChoice->choice); + } + // Otherwise return None + return QPrint::DuplexNone; +} + +void QPpdPrintDevice::loadColorModes() const +{ + // Cups cups_ptype_e CUPS_PRINTER_BW CUPS_PRINTER_COLOR + // Cups ppd_file_t color_device + // PPD ColorDevice + m_colorModes.clear(); + cups_ptype_e type = printerTypeFlags(); + if (type & CUPS_PRINTER_BW) + m_colorModes.append(QPrint::GrayScale); + if (type & CUPS_PRINTER_COLOR) + m_colorModes.append(QPrint::Color); + m_haveColorModes = true; +} + +QPrint::ColorMode QPpdPrintDevice::defaultColorMode() const +{ + // NOTE: Implemented in both CUPS and Mac plugins, please keep in sync + // Not a proper option, usually only know if supports color or not, but some + // users known to abuse ColorModel to always force GrayScale. + if (m_ppd && supportedColorModes().contains(QPrint::Color)) { + ppd_option_t *colorModel = ppdFindOption(m_ppd, "DefaultColorModel"); + if (!colorModel) + colorModel = ppdFindOption(m_ppd, "ColorModel"); + if (!colorModel || (colorModel && !qstrcmp(colorModel->defchoice, "Gray"))) + return QPrint::Color; + } + return QPrint::GrayScale; +} + +void QPpdPrintDevice::loadMimeTypes() const +{ + // TODO No CUPS api? Need to manually load CUPS mime.types file? + // For now hard-code most common support types + QMimeDatabase db; + m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("application/pdf"))); + m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("application/postscript"))); + m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/gif"))); + m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/png"))); + m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/jpeg"))); + m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("image/tiff"))); + m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("text/html"))); + m_mimeTypes.append(db.mimeTypeForName(QStringLiteral("text/plain"))); + m_haveMimeTypes = true; +} + +void QPpdPrintDevice::loadPrinter() +{ + // Just to be safe, check if existing printer needs closing + if (m_ppd) { + ppdClose(m_ppd); + m_ppd = 0; + } + if (m_cupsDest) { + cupsFreeDests(1, m_cupsDest); + m_cupsDest = 0; + } + + // Get the print instance and PPD file + m_cupsDest = cupsGetNamedDest(CUPS_HTTP_DEFAULT, m_cupsName, m_cupsInstance); + if (m_cupsDest) { + const char *ppdFile = cupsGetPPD(m_cupsName); + if (ppdFile) + m_ppd = ppdOpenFile(ppdFile); + unlink(ppdFile); + if (m_ppd) { + ppdMarkDefaults(m_ppd); + } else { + cupsFreeDests(1, m_cupsDest); + m_cupsDest = 0; + m_ppd = 0; + } + } +} + +QString QPpdPrintDevice::printerOption(const QString &key) const +{ + return cupsGetOption(key.toUtf8(), m_cupsDest->num_options, m_cupsDest->options); +} + +cups_ptype_e QPpdPrintDevice::printerTypeFlags() const +{ + return static_cast<cups_ptype_e>(printerOption("printer-type").toUInt()); +} + +#endif // QT_NO_PRINTER + +QT_END_NAMESPACE diff --git a/src/plugins/printsupport/cups/qppdprintdevice.h b/src/plugins/printsupport/cups/qppdprintdevice.h new file mode 100644 index 0000000000..982f46d71f --- /dev/null +++ b/src/plugins/printsupport/cups/qppdprintdevice.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2014 John Layt <jlayt@kde.org> +** 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 QPPDPRINTDEVICE_H +#define QPPDPRINTDEVICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of internal files. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include <qpa/qplatformprintdevice.h> + +#ifndef QT_NO_PRINTER + +#include <cups/cups.h> +#include <cups/ppd.h> + +QT_BEGIN_NAMESPACE + +class QPpdPrintDevice : public QPlatformPrintDevice +{ +public: + QPpdPrintDevice(); + explicit QPpdPrintDevice(const QString &id); + QPpdPrintDevice(const QPpdPrintDevice &other); + virtual ~QPpdPrintDevice(); + + QPpdPrintDevice &operator=(const QPpdPrintDevice &other); + + QPpdPrintDevice *clone(); + + bool operator==(const QPpdPrintDevice &other) const; + + bool isValid() const Q_DECL_OVERRIDE; + bool isDefault() const Q_DECL_OVERRIDE; + + QPrint::DeviceState state() const Q_DECL_OVERRIDE; + + QPageSize defaultPageSize() const Q_DECL_OVERRIDE; + + QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation, + int resolution) const Q_DECL_OVERRIDE; + + int defaultResolution() const Q_DECL_OVERRIDE; + + QPrint::InputSlot defaultInputSlot() const Q_DECL_OVERRIDE; + + QPrint::OutputBin defaultOutputBin() const Q_DECL_OVERRIDE; + + QPrint::DuplexMode defaultDuplexMode() const Q_DECL_OVERRIDE; + + QPrint::ColorMode defaultColorMode() const Q_DECL_OVERRIDE; + +protected: + void loadPageSizes() const Q_DECL_OVERRIDE; + void loadResolutions() const Q_DECL_OVERRIDE; + void loadInputSlots() const Q_DECL_OVERRIDE; + void loadOutputBins() const Q_DECL_OVERRIDE; + void loadDuplexModes() const Q_DECL_OVERRIDE; + void loadColorModes() const Q_DECL_OVERRIDE; + void loadMimeTypes() const Q_DECL_OVERRIDE; + +private: + void loadPrinter(); + QString printerOption(const QString &key) const; + cups_ptype_e printerTypeFlags() const; + + cups_dest_t *m_cupsDest; + ppd_file_t *m_ppd; + QByteArray m_cupsName; + QByteArray m_cupsInstance; + QMarginsF m_customMargins; + mutable QHash<QString, QMarginsF> m_printableMargins; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER +#endif // QPPDPRINTDEVICE_H diff --git a/src/plugins/printsupport/windows/qwindowsprintdevice.cpp b/src/plugins/printsupport/windows/qwindowsprintdevice.cpp new file mode 100644 index 0000000000..1b55937ec7 --- /dev/null +++ b/src/plugins/printsupport/windows/qwindowsprintdevice.cpp @@ -0,0 +1,480 @@ +/**************************************************************************** +** +** Copyright (C) 2014 John Layt <jlayt@kde.org> +** 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 "qwindowsprintdevice.h" + +#include <qdebug.h> + +#ifndef DC_COLLATE +# define DC_COLLATE 22 +#endif + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PRINTER + +extern qreal qt_pointMultiplier(QPageLayout::Unit unit); + +static inline uint qwcsnlen(const wchar_t *str, uint maxlen) +{ + uint length = 0; + if (str) { + while (length < maxlen && *str++) + length++; + } + return length; +} + +static QPrint::InputSlot paperBinToInputSlot(int windowsId, const QString &name) +{ + QPrint::InputSlot slot; + slot.name = name; + int i; + for (i = 0; inputSlotMap[i].id != QPrint::CustomInputSlot; ++i) { + if (inputSlotMap[i].windowsId == windowsId) { + slot.key = inputSlotMap[i].key; + slot.id = inputSlotMap[i].id; + slot.windowsId = inputSlotMap[i].windowsId; + return slot; + } + } + slot.key = inputSlotMap[i].key; + slot.id = inputSlotMap[i].id; + return slot; +} + + +QWindowsPrintDevice::QWindowsPrintDevice() + : QPlatformPrintDevice(), + m_hPrinter(0) +{ +} + +QWindowsPrintDevice::QWindowsPrintDevice(const QString &id) + : QPlatformPrintDevice(id), + m_hPrinter(0) +{ + // First do a fast lookup to see if printer exists, if it does then open it + if (!id.isEmpty() && QWindowsPrintDevice::availablePrintDeviceIds().contains(id)) { + if (OpenPrinter((LPWSTR)m_id.utf16(), &m_hPrinter, NULL)) { + DWORD needed = 0; + GetPrinter(m_hPrinter, 2, 0, 0, &needed); + QScopedArrayPointer<BYTE> buffer(new BYTE[needed]); + if (GetPrinter(m_hPrinter, 2, buffer.data(), needed, &needed)) { + PPRINTER_INFO_2 info = reinterpret_cast<PPRINTER_INFO_2>(buffer.data()); + m_name = QString::fromWCharArray(info->pPrinterName); + m_location = QString::fromWCharArray(info->pLocation); + m_makeAndModel = QString::fromWCharArray(info->pDriverName); // TODO Check is not available elsewhere + m_isRemote = info->Attributes & PRINTER_ATTRIBUTE_NETWORK; + } + m_supportsMultipleCopies = (DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_COPIES, NULL, NULL) > 1); + m_supportsCollateCopies = DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_COLLATE, NULL, NULL); + // Min/Max custom size is in tenths of a millimeter + const qreal multiplier = qt_pointMultiplier(QPageLayout::Millimeter); + DWORD min = DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_MINEXTENT, NULL, NULL); + m_minimumPhysicalPageSize = QSize((LOWORD(min) / 10.0) * multiplier, (HIWORD(min) / 10.0) * multiplier); + DWORD max = DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_MAXEXTENT, NULL, NULL); + m_maximumPhysicalPageSize = QSize((LOWORD(max) / 10.0) * multiplier, (HIWORD(max) / 10.0) * multiplier); + m_supportsCustomPageSizes = (m_maximumPhysicalPageSize.width() > 0 && m_maximumPhysicalPageSize.height() > 0); + } + } +} + +QWindowsPrintDevice::QWindowsPrintDevice(const QWindowsPrintDevice &other) + : QPlatformPrintDevice(other) +{ + OpenPrinter((LPWSTR)other.m_id.utf16(), &m_hPrinter, NULL); +} + +QWindowsPrintDevice::~QWindowsPrintDevice() +{ + ClosePrinter(m_hPrinter); +} + +QWindowsPrintDevice &QWindowsPrintDevice::operator=(const QWindowsPrintDevice &other) +{ + OpenPrinter((LPWSTR)other.m_id.utf16(), &m_hPrinter, NULL); + return *this; +} + +bool QWindowsPrintDevice::operator==(const QWindowsPrintDevice &other) const +{ + return (m_id == other.m_id); +} + +bool QWindowsPrintDevice::isValid() const +{ + return m_hPrinter; +} + +bool QWindowsPrintDevice::isDefault() const +{ + return m_id == defaultPrintDeviceId(); +} + +QPrint::DeviceState QWindowsPrintDevice::state() const +{ + DWORD needed = 0; + GetPrinter(m_hPrinter, 6, 0, 0, &needed); + QScopedArrayPointer<BYTE> buffer(new BYTE[needed]); + + if (GetPrinter(m_hPrinter, 6, buffer.data(), needed, &needed)) { + PPRINTER_INFO_6 info = reinterpret_cast<PPRINTER_INFO_6>(buffer.data()); + // TODO Check mapping + if (info->dwStatus == 0 + || (info->dwStatus & PRINTER_STATUS_WAITING) == PRINTER_STATUS_WAITING + || (info->dwStatus & PRINTER_STATUS_POWER_SAVE) == PRINTER_STATUS_POWER_SAVE) { + return QPrint::Idle; + } else if ((info->dwStatus & PRINTER_STATUS_PRINTING) == PRINTER_STATUS_PRINTING + || (info->dwStatus & PRINTER_STATUS_BUSY) == PRINTER_STATUS_BUSY + || (info->dwStatus & PRINTER_STATUS_INITIALIZING) == PRINTER_STATUS_INITIALIZING + || (info->dwStatus & PRINTER_STATUS_IO_ACTIVE) == PRINTER_STATUS_IO_ACTIVE + || (info->dwStatus & PRINTER_STATUS_PROCESSING) == PRINTER_STATUS_PROCESSING + || (info->dwStatus & PRINTER_STATUS_WARMING_UP) == PRINTER_STATUS_WARMING_UP) { + return QPrint::Active; + } + } + + return QPrint::Error; +} + +void QWindowsPrintDevice::loadPageSizes() const +{ + // Get the number of paper sizes and check all 3 attributes have same count + DWORD paperCount = DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_PAPERNAMES, NULL, NULL); + if (int(paperCount) > 0 + && DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_PAPERSIZE, NULL, NULL) == paperCount + && DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_PAPERS, NULL, NULL) == paperCount) { + + QScopedArrayPointer<wchar_t> paperNames(new wchar_t[paperCount*64]); + QScopedArrayPointer<POINT> winSizes(new POINT[paperCount*sizeof(POINT)]); + QScopedArrayPointer<wchar_t> papers(new wchar_t[paperCount]); + + // Get the details and match the default paper size + if (DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_PAPERNAMES, paperNames.data(), NULL) == paperCount + && DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_PAPERSIZE, (wchar_t *)winSizes.data(), NULL) == paperCount + && DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_PAPERS, papers.data(), NULL) == paperCount) { + + // Returned size is in tenths of a millimeter + const qreal multiplier = qt_pointMultiplier(QPageLayout::Millimeter); + for (int i = 0; i < int(paperCount); ++i) { + QSize size = QSize(qRound((winSizes[i].x / 10.0) * multiplier), qRound((winSizes[i].y / 10.0) * multiplier)); + wchar_t *paper = paperNames.data() + (i * 64); + QString name = QString::fromWCharArray(paper, qwcsnlen(paper, 64)); + m_pageSizes.append(createPageSize(papers[i], size, name)); + } + + } + } + + m_havePageSizes = true; +} + +QPageSize QWindowsPrintDevice::defaultPageSize() const +{ + if (!m_havePageSizes) + loadPageSizes(); + + QPageSize pageSize; + + // Allocate the required DEVMODE buffer + DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); + LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); + + // Get the default DevMode + DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); + + // Get the default paper size + if (result == IDOK && pDevMode->dmFields & DM_PAPERSIZE) { + // Find the supported page size that matches, in theory default should be one of them + foreach (const QPageSize &ps, m_pageSizes) { + if (ps.windowsId() == pDevMode->dmPaperSize) { + pageSize = ps; + break; + } + } + } + + // Clean-up + free(pDevMode); + return pageSize; +} + +QMarginsF QWindowsPrintDevice::printableMargins(const QPageSize &pageSize, + QPageLayout::Orientation orientation, + int resolution) const +{ + // TODO This is slow, need to cache values or find better way! + // Modify the DevMode to get the DC printable margins in device pixels + QMarginsF margins = QMarginsF(0, 0, 0, 0); + DWORD needed = 0; + GetPrinter(m_hPrinter, 2, 0, 0, &needed); + QScopedArrayPointer<BYTE> buffer(new BYTE[needed]); + if (GetPrinter(m_hPrinter, 2, buffer.data(), needed, &needed)) { + PPRINTER_INFO_2 info = reinterpret_cast<PPRINTER_INFO_2>(buffer.data()); + DEVMODE *devMode = info->pDevMode; + HDC pDC = CreateDC(NULL, (LPWSTR)m_id.utf16(), NULL, devMode); + if (pageSize.id() == QPageSize::Custom || pageSize.windowsId() <= 0 || pageSize.windowsId() > DMPAPER_LAST) { + devMode->dmPaperSize = 0; + devMode->dmPaperWidth = pageSize.size(QPageSize::Millimeter).width() * 10.0; + devMode->dmPaperLength = pageSize.size(QPageSize::Millimeter).height() * 10.0; + } else { + devMode->dmPaperSize = pageSize.windowsId(); + } + devMode->dmPrintQuality = resolution; + devMode->dmOrientation = orientation == QPageLayout::Portrait ? DMORIENT_PORTRAIT : DMORIENT_LANDSCAPE; + ResetDC(pDC, devMode); + const int dpiWidth = GetDeviceCaps(pDC, LOGPIXELSX); + const int dpiHeight = GetDeviceCaps(pDC, LOGPIXELSY); + const qreal wMult = 72.0 / dpiWidth; + const qreal hMult = 72.0 / dpiHeight; + const qreal physicalWidth = GetDeviceCaps(pDC, PHYSICALWIDTH) * wMult; + const qreal physicalHeight = GetDeviceCaps(pDC, PHYSICALHEIGHT) * hMult; + const qreal printableWidth = GetDeviceCaps(pDC, HORZRES) * wMult; + const qreal printableHeight = GetDeviceCaps(pDC, VERTRES) * hMult; + const qreal leftMargin = GetDeviceCaps(pDC, PHYSICALOFFSETX)* wMult; + const qreal topMargin = GetDeviceCaps(pDC, PHYSICALOFFSETY) * hMult; + const qreal rightMargin = physicalWidth - leftMargin - printableWidth; + const qreal bottomMargin = physicalHeight - topMargin - printableHeight; + margins = QMarginsF(leftMargin, topMargin, rightMargin, bottomMargin); + ReleaseDC(NULL, pDC); + } + return margins; +} + +void QWindowsPrintDevice::loadResolutions() const +{ + DWORD resCount = DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_ENUMRESOLUTIONS, NULL, NULL); + if (int(resCount) > 0) { + QScopedArrayPointer<LONG> resolutions(new LONG[resCount*sizeof(LONG)]); + // Get the details and match the default paper size + if (DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_ENUMRESOLUTIONS, (LPWSTR)resolutions.data(), NULL) == resCount) { + for (int i = 0; i < int(resCount); ++i) + m_resolutions.append(resolutions[i]); + } + } + m_haveResolutions = true; +} + +int QWindowsPrintDevice::defaultResolution() const +{ + int resolution = 72; // TODO Set a sensible default? + + // Allocate the required DEVMODE buffer + DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); + LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); + + // Get the default DevMode + DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); + + // Get the default resolution + if (result == IDOK && pDevMode->dmFields & DM_YRESOLUTION) { + if (pDevMode->dmPrintQuality > 0) + resolution = pDevMode->dmPrintQuality; + else + resolution = pDevMode->dmYResolution; + } + + // Clean-up + free(pDevMode); + return resolution; +} + +void QWindowsPrintDevice::loadInputSlots() const +{ + DWORD binCount = DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_BINS, NULL, NULL); + if (int(binCount) > 0 + && DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_BINNAMES, NULL, NULL) == binCount) { + + QScopedArrayPointer<WORD> bins(new WORD[binCount*sizeof(WORD)]); + QScopedArrayPointer<wchar_t> binNames(new wchar_t[binCount*24]); + + // Get the details and match the default paper size + if (DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_BINS, (LPWSTR)bins.data(), NULL) == binCount + && DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_BINNAMES, binNames.data(), NULL) == binCount) { + + for (int i = 0; i < int(binCount); ++i) { + wchar_t *binName = binNames.data() + (i * 24); + QString name = QString::fromWCharArray(binName, qwcsnlen(binName, 24)); + m_inputSlots.append(paperBinToInputSlot(bins[i], name)); + } + + } + } + + m_haveInputSlots = true; +} + +QPrint::InputSlot QWindowsPrintDevice::defaultInputSlot() const +{ + QPrint::InputSlot inputSlot = QPlatformPrintDevice::defaultInputSlot();; + + // Allocate the required DEVMODE buffer + DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); + LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); + + // Get the default DevMode + DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); + + // Get the default input slot + if (result == IDOK && pDevMode->dmFields & DM_DEFAULTSOURCE) { + QPrint::InputSlot tempSlot = paperBinToInputSlot(pDevMode->dmDefaultSource, QString()); + foreach (const QPrint::InputSlot &slot, supportedInputSlots()) { + if (slot.key == tempSlot.key) { + inputSlot = slot; + break; + } + } + } + + // Clean-up + free(pDevMode); + return inputSlot; +} + +void QWindowsPrintDevice::loadOutputBins() const +{ + m_outputBins.append(QPlatformPrintDevice::defaultOutputBin()); + m_haveOutputBins = true; +} + +void QWindowsPrintDevice::loadDuplexModes() const +{ + m_duplexModes.append(QPrint::DuplexNone); + DWORD duplex = DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_DUPLEX, NULL, NULL); + if (int(duplex) == 1) { + // TODO Assume if duplex flag supports both modes + m_duplexModes.append(QPrint::DuplexLongSide); + m_duplexModes.append(QPrint::DuplexShortSide); + } + m_haveDuplexModes = true; +} + +QPrint::DuplexMode QWindowsPrintDevice::defaultDuplexMode() const +{ + QPrint::DuplexMode duplexMode = QPrint::DuplexNone; + + // Allocate the required DEVMODE buffer + DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); + LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); + + // Get the default DevMode + DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); + + // Get the default duplex mode + if (result == IDOK && pDevMode->dmFields & DM_DUPLEX) { + if (pDevMode->dmDuplex == DMDUP_VERTICAL) + duplexMode = QPrint::DuplexLongSide; + else if (pDevMode->dmDuplex == DMDUP_HORIZONTAL) + duplexMode = QPrint::DuplexShortSide; + } + + // Clean-up + free(pDevMode); + return duplexMode; +} + +void QWindowsPrintDevice::loadColorModes() const +{ + m_colorModes.append(QPrint::GrayScale); + DWORD color = DeviceCapabilities((LPWSTR)m_id.utf16(), NULL, DC_COLORDEVICE, NULL, NULL); + if (int(color) == 1) + m_colorModes.append(QPrint::Color); + m_haveColorModes = true; +} + +QPrint::ColorMode QWindowsPrintDevice::defaultColorMode() const +{ + if (!m_haveColorModes) + loadColorModes(); + if (!m_colorModes.contains(QPrint::Color)) + return QPrint::GrayScale; + + QPrint::ColorMode colorMode = QPrint::GrayScale; + + // Allocate the required DEVMODE buffer + DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); + LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); + + // Get the default DevMode + DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); + + // Get the default color mode + if (result == IDOK && pDevMode->dmFields & DM_COLOR) { + if (pDevMode->dmColor == DMCOLOR_COLOR) + colorMode = QPrint::Color; + } + + // Clean-up + free(pDevMode); + return colorMode; +} + +QStringList QWindowsPrintDevice::availablePrintDeviceIds() +{ + QStringList list; + DWORD needed = 0; + DWORD returned = 0; + if ((!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, 0, 0, &needed, &returned) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + || !needed) { + return list; + } + QScopedArrayPointer<BYTE> buffer(new BYTE[needed]); + if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, buffer.data(), needed, &needed, &returned)) + return list; + PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer.data()); + for (uint i = 0; i < returned; ++i) + list.append(QString::fromWCharArray(infoList[i].pPrinterName)); + return list; +} + +QString QWindowsPrintDevice::defaultPrintDeviceId() +{ + DWORD size = 0; + GetDefaultPrinter(NULL, &size); + QScopedArrayPointer<wchar_t> name(new wchar_t[size]); + GetDefaultPrinter(name.data(), &size); + return QString::fromWCharArray(name.data()); +} + +#endif // QT_NO_PRINTER + +QT_END_NAMESPACE diff --git a/src/plugins/printsupport/windows/qwindowsprintdevice.h b/src/plugins/printsupport/windows/qwindowsprintdevice.h new file mode 100644 index 0000000000..2d11787305 --- /dev/null +++ b/src/plugins/printsupport/windows/qwindowsprintdevice.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2014 John Layt <jlayt@kde.org> +** 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 QWINDOWSPRINTDEVICE_H +#define QWINDOWSPRINTDEVICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of internal files. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#include <qpa/qplatformprintdevice.h> + +#ifndef QT_NO_PRINTER + +#include <QtCore/qt_windows.h> + +QT_BEGIN_NAMESPACE + +class QWindowsPrintDevice : public QPlatformPrintDevice +{ +public: + QWindowsPrintDevice(); + explicit QWindowsPrintDevice(const QString &id); + QWindowsPrintDevice(const QWindowsPrintDevice &other); + virtual ~QWindowsPrintDevice(); + + QWindowsPrintDevice &operator=(const QWindowsPrintDevice &other); + + QWindowsPrintDevice *clone(); + + bool operator==(const QWindowsPrintDevice &other) const; + + bool isValid() const Q_DECL_OVERRIDE; + bool isDefault() const Q_DECL_OVERRIDE; + + QPrint::DeviceState state() const Q_DECL_OVERRIDE; + + QPageSize defaultPageSize() const Q_DECL_OVERRIDE; + + QMarginsF printableMargins(const QPageSize &pageSize, QPageLayout::Orientation orientation, + int resolution) const Q_DECL_OVERRIDE; + + int defaultResolution() const Q_DECL_OVERRIDE; + + QPrint::InputSlot defaultInputSlot() const Q_DECL_OVERRIDE; + + QPrint::DuplexMode defaultDuplexMode() const Q_DECL_OVERRIDE; + + QPrint::ColorMode defaultColorMode() const Q_DECL_OVERRIDE; + + static QStringList availablePrintDeviceIds(); + static QString defaultPrintDeviceId(); + +protected: + void loadPageSizes() const Q_DECL_OVERRIDE; + void loadResolutions() const Q_DECL_OVERRIDE; + void loadInputSlots() const Q_DECL_OVERRIDE; + void loadOutputBins() const Q_DECL_OVERRIDE; + void loadDuplexModes() const Q_DECL_OVERRIDE; + void loadColorModes() const Q_DECL_OVERRIDE; + +private: + HANDLE m_hPrinter; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER +#endif // QWINDOWSPRINTDEVICE_H diff --git a/src/plugins/printsupport/windows/qwindowsprintersupport.cpp b/src/plugins/printsupport/windows/qwindowsprintersupport.cpp index b7ba9ef5e7..997082a367 100644 --- a/src/plugins/printsupport/windows/qwindowsprintersupport.cpp +++ b/src/plugins/printsupport/windows/qwindowsprintersupport.cpp @@ -40,20 +40,17 @@ ****************************************************************************/ #include "qwindowsprintersupport.h" +#include "qwindowsprintdevice.h" -#include <QtCore/QList> -#include <QtCore/QScopedArrayPointer> -#include <QtPrintSupport/QPrinterInfo> +#include <QtCore/QStringList> #include <qprintengine_win_p.h> -#include <private/qpaintengine_alpha_p.h> -#include <private/qprinterinfo_p.h> +#include <private/qprintdevice_p.h> QT_BEGIN_NAMESPACE QWindowsPrinterSupport::QWindowsPrinterSupport() : QPlatformPrinterSupport() { - m_printers = QWindowsPrinterSupport::queryPrinters(); } QWindowsPrinterSupport::~QWindowsPrinterSupport() @@ -71,43 +68,19 @@ QPaintEngine *QWindowsPrinterSupport::createPaintEngine(QPrintEngine *engine, QP return static_cast<QWin32PrintEngine *>(engine); } -QList<QPrinter::PaperSize> QWindowsPrinterSupport::supportedPaperSizes(const QPrinterInfo &printerInfo) const +QPrintDevice QWindowsPrinterSupport::createPrintDevice(const QString &id) { - return QWin32PrintEngine::supportedPaperSizes(printerInfo); + return QPlatformPrinterSupport::createPrintDevice(new QWindowsPrintDevice(id)); } -QList<QPair<QString, QSizeF> >QWindowsPrinterSupport::supportedSizesWithNames(const QPrinterInfo &printerInfo) const +QStringList QWindowsPrinterSupport::availablePrintDeviceIds() const { - return QWin32PrintEngine::supportedSizesWithNames(printerInfo); + return QWindowsPrintDevice::availablePrintDeviceIds(); } -QList<QPrinterInfo> QWindowsPrinterSupport::availablePrinters() +QString QWindowsPrinterSupport::defaultPrintDeviceId() const { - m_printers = QWindowsPrinterSupport::queryPrinters(); - return QPlatformPrinterSupport::availablePrinters(); -} - -QList<QPrinterInfo> QWindowsPrinterSupport::queryPrinters() -{ - QList<QPrinterInfo> result; - DWORD needed = 0; - DWORD returned = 0; - if ((!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, 0, 0, &needed, &returned) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) - || !needed) { - return result; - } - QScopedArrayPointer<BYTE> buffer(new BYTE[needed]); - if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, buffer.data(), needed, &needed, &returned)) - return result; - PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer.data()); - QString defaultPrinterName; - QWin32PrintEngine::queryDefaultPrinter(defaultPrinterName); - for (uint i = 0; i < returned; ++i) { - const QString printerName(QString::fromWCharArray(infoList[i].pPrinterName)); - const bool isDefault = (printerName == defaultPrinterName); - result.append(QPlatformPrinterSupport::createPrinterInfo(printerName, QString(), QString(), QString(), isDefault, i)); - } - return result; + return QWindowsPrintDevice::defaultPrintDeviceId(); } QT_END_NAMESPACE diff --git a/src/plugins/printsupport/windows/qwindowsprintersupport.h b/src/plugins/printsupport/windows/qwindowsprintersupport.h index 1b1b1fa215..6a84b667dd 100644 --- a/src/plugins/printsupport/windows/qwindowsprintersupport.h +++ b/src/plugins/printsupport/windows/qwindowsprintersupport.h @@ -46,22 +46,18 @@ QT_BEGIN_NAMESPACE -class QWin32PrintEngine; - class QWindowsPrinterSupport : public QPlatformPrinterSupport { public: QWindowsPrinterSupport(); ~QWindowsPrinterSupport(); - 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; - virtual QList<QPrinterInfo> availablePrinters(); + QPrintEngine *createNativePrintEngine(QPrinter::PrinterMode printerMode) Q_DECL_OVERRIDE; + QPaintEngine *createPaintEngine(QPrintEngine *printEngine, QPrinter::PrinterMode) Q_DECL_OVERRIDE; -private: - static QList<QPrinterInfo> queryPrinters(); + QPrintDevice createPrintDevice(const QString &id) Q_DECL_OVERRIDE; + QStringList availablePrintDeviceIds() const Q_DECL_OVERRIDE; + QString defaultPrintDeviceId() const Q_DECL_OVERRIDE; }; QT_END_NAMESPACE diff --git a/src/plugins/printsupport/windows/windows.pro b/src/plugins/printsupport/windows/windows.pro index ae9efa342b..364e19e68e 100644 --- a/src/plugins/printsupport/windows/windows.pro +++ b/src/plugins/printsupport/windows/windows.pro @@ -12,10 +12,12 @@ INCLUDEPATH *= $$QT_SOURCE_TREE/src/printsupport/kernel SOURCES += \ main.cpp \ - qwindowsprintersupport.cpp + qwindowsprintersupport.cpp \ + qwindowsprintdevice.cpp \ HEADERS += \ - qwindowsprintersupport.h + qwindowsprintersupport.h \ + qwindowsprintdevice.h \ OTHER_FILES += windows.json |