From 84dc7d5f5560d69a0d7973f591ce253605a458ac Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 10 Oct 2016 21:53:09 +0200 Subject: QComboBox: fix build with GCC 7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC 7 warns about implicit fall-throughs now. Fix by adding the missing comments. Interestingly, Coverity only found one of them, even though all three still exist in dev, too. Change-Id: I9f2c5e2700d5ec5234fee3a532feffe01b7c4ce3 Coverity-Id: 11156 Reviewed-by: Edward Welbourne Reviewed-by: Sérgio Martins --- src/widgets/widgets/qcombobox.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index 0ef76b95f0..22ba26b6a3 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -3125,6 +3125,7 @@ void QComboBox::keyPressEvent(QKeyEvent *e) case Qt::Key_Up: if (e->modifiers() & Qt::ControlModifier) break; // pass to line edit for auto completion + // fall through case Qt::Key_PageUp: #ifdef QT_KEYPAD_NAVIGATION if (QApplication::keypadNavigationEnabled()) @@ -3210,6 +3211,7 @@ void QComboBox::keyPressEvent(QKeyEvent *e) switch (move) { case MoveFirst: newIndex = -1; + // fall through case MoveDown: newIndex++; while ((newIndex < count()) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled)) @@ -3217,6 +3219,7 @@ void QComboBox::keyPressEvent(QKeyEvent *e) break; case MoveLast: newIndex = count(); + // fall through case MoveUp: newIndex--; while ((newIndex >= 0) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled)) -- cgit v1.2.3 From 30b7278c5cf1df0b507cd3139a53b7adcc190850 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 6 Oct 2016 16:32:49 +0200 Subject: Remove type punning from ucstrncmp Type punning is UB. Change-Id: I0022d2a38136d80f5ddda21cea7dc0968c736242 Reviewed-by: Oswald Buddenhagen Reviewed-by: Thiago Macieira --- src/corelib/tools/qstring.cpp | 48 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index ff7e7fd406..b7f83e4b9d 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -480,48 +480,46 @@ static int ucstrncmp(const QChar *a, const QChar *b, int l) if (!l) return 0; - union { - const QChar *w; - const quint32 *d; - quintptr value; - } sa, sb; - sa.w = a; - sb.w = b; - // check alignment - if ((sa.value & 2) == (sb.value & 2)) { + if ((reinterpret_cast(a) & 2) == (reinterpret_cast(b) & 2)) { // both addresses have the same alignment - if (sa.value & 2) { + if (reinterpret_cast(a) & 2) { // both addresses are not aligned to 4-bytes boundaries // compare the first character - if (*sa.w != *sb.w) - return sa.w->unicode() - sb.w->unicode(); + if (*a != *b) + return a->unicode() - b->unicode(); --l; - ++sa.w; - ++sb.w; + ++a; + ++b; // now both addresses are 4-bytes aligned } // both addresses are 4-bytes aligned // do a fast 32-bit comparison - const quint32 *e = sa.d + (l >> 1); - for ( ; sa.d != e; ++sa.d, ++sb.d) { - if (*sa.d != *sb.d) { - if (*sa.w != *sb.w) - return sa.w->unicode() - sb.w->unicode(); - return sa.w[1].unicode() - sb.w[1].unicode(); + const quint32 *da = reinterpret_cast(a); + const quint32 *db = reinterpret_cast(b); + const quint32 *e = da + (l >> 1); + for ( ; da != e; ++da, ++db) { + if (*da != *db) { + a = reinterpret_cast(da); + b = reinterpret_cast(db); + if (*a != *b) + return a->unicode() - b->unicode(); + return a[1].unicode() - b[1].unicode(); } } // do we have a tail? - return (l & 1) ? sa.w->unicode() - sb.w->unicode() : 0; + a = reinterpret_cast(da); + b = reinterpret_cast(db); + return (l & 1) ? a->unicode() - b->unicode() : 0; } else { // one of the addresses isn't 4-byte aligned but the other is - const QChar *e = sa.w + l; - for ( ; sa.w != e; ++sa.w, ++sb.w) { - if (*sa.w != *sb.w) - return sa.w->unicode() - sb.w->unicode(); + const QChar *e = a + l; + for ( ; a != e; ++a, ++b) { + if (*a != *b) + return a->unicode() - b->unicode(); } } return 0; -- cgit v1.2.3 From 592614ea3ecd90ede2ae1b8e6579d1b898f474ec Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Fri, 21 Oct 2016 10:13:20 +0200 Subject: Don't claim all fonts are smoothly scalable on Windows Since 40ebda3efbcf00c3393cb70c9eee203c68a57311, bitmap fonts are detected as smoothly scalable on Windows. While the fontsAlwaysScalable() does say whether or not the font engine can rasterize the fonts at any size, it does not determine whether or not the result is "attractive" as the documentation for isSmoothlyScalable() says. Only outline fonts are smoothly scalable, and this fact is used in Qt Quick to determine whether we can use the distance field renderer for the font or if we have to fall back to the native renderer. The consequence was that the fonts were no longer usable in Qt Quick. We also need to revert the optimization for isBitmapScalable() since there a font that is smoothly scalable should not be identified as bitmap scalable (basically this means: Font is not smoothly scalable, but it can be scaled with bitmap scaling artifacts). [ChangeLog][QtGui][Text] Fixed a regression where raster fonts on Windows were detected as smoothly scalable and thus rendering with said fonts in Qt Quick would break. Task-number: QTBUG-56659 Change-Id: Ia7db6fee8249aca347233a488388be5c3a00c2df Reviewed-by: Lars Knoll --- src/gui/text/qfontdatabase.cpp | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src') diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index f063541249..04081a5ca7 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -1657,9 +1657,6 @@ bool QFontDatabase::isFixedPitch(const QString &family, bool QFontDatabase::isBitmapScalable(const QString &family, const QString &style) const { - if (QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fontsAlwaysScalable()) - return true; - bool bitmapScalable = false; QString familyName, foundryName; parseFontName(family, foundryName, familyName); @@ -1700,9 +1697,6 @@ bool QFontDatabase::isBitmapScalable(const QString &family, */ bool QFontDatabase::isSmoothlyScalable(const QString &family, const QString &style) const { - if (QGuiApplicationPrivate::platformIntegration()->fontDatabase()->fontsAlwaysScalable()) - return true; - bool smoothScalable = false; QString familyName, foundryName; parseFontName(family, foundryName, familyName); -- cgit v1.2.3 From f6eb570c7dee2ec92383607c614db91f31804707 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 29 Sep 2016 11:59:01 +0200 Subject: Darwin: normalize all watched paths to composed from MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be done by all POSIX APIs for strings coming in that way, but because other code (like NSWhateverViews) will most likely return decomposed form, we make sure that those are in composed form too. Task-number: QTBUG-55896 Change-Id: I065e11cee6b59706d4346ed20d4b59b9b95163b8 Reviewed-by: Morten Johan Sørvig --- src/corelib/io/qfilesystemwatcher_fsevents.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/io/qfilesystemwatcher_fsevents.mm b/src/corelib/io/qfilesystemwatcher_fsevents.mm index c6fa6c9846..6af9e5f5ef 100644 --- a/src/corelib/io/qfilesystemwatcher_fsevents.mm +++ b/src/corelib/io/qfilesystemwatcher_fsevents.mm @@ -343,7 +343,7 @@ QStringList QFseventsFileSystemWatcherEngine::addPaths(const QStringList &paths, QStringList p = paths; QMutableListIterator it(p); while (it.hasNext()) { - QString origPath = it.next(); + QString origPath = it.next().normalized(QString::NormalizationForm_C); QString realPath = origPath; if (realPath.endsWith(QDir::separator())) realPath = realPath.mid(0, realPath.size() - 1); -- cgit v1.2.3 From 843247e1c509ec3ce55a2e2051b83fcfa6634135 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 14 Oct 2016 13:05:00 +0200 Subject: Call raise on the window that contains the widget and not the widget When the mouse is clicked on the widget in a window while a popup is visible then it should raise just the window and not the widget inside it. If the widget is in a stacked layout then calling raise() on it can cause it to appear on top so avoid this by calling raise() directly on the window. Task-number: QTBUG-52670 Change-Id: Idd287c6cc7038c57e14e92f4a3e1c50985925684 Reviewed-by: Friedemann Kleint --- src/widgets/kernel/qwidgetwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index 95f63025fb..40c488857e 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -529,7 +529,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event) // activate window of the widget under mouse pointer if (!w->isActiveWindow()) { w->activateWindow(); - w->raise(); + w->window()->raise(); } QWindow *win = w->windowHandle(); -- cgit v1.2.3 From e2d8c7bf2203cbc0c7ff923caf3132881aafa0b0 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Thu, 1 Sep 2016 11:53:15 +0200 Subject: Don't change the size of widgets just because it is in a floating dock When a QDockWidget was floating on macOS then it would force the size of various widgets, such as buttons, comboboxes, to be the smallest needed rather than the size they had when it was docked. Task-number: QTBUG-7460 Task-number: QTBUG-52354 Change-Id: Id348180934f113f3a9a9ce5622a9af03eed04108 Reviewed-by: Jake Petroules --- src/widgets/styles/qmacstyle_mac.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/widgets/styles/qmacstyle_mac.mm b/src/widgets/styles/qmacstyle_mac.mm index 46918a2d5f..84445bfce4 100644 --- a/src/widgets/styles/qmacstyle_mac.mm +++ b/src/widgets/styles/qmacstyle_mac.mm @@ -1034,6 +1034,8 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg #if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT) static QAquaWidgetSize qt_aqua_guess_size(const QWidget *widg, QSize large, QSize small, QSize mini) { + Q_UNUSED(widg); + if (large == QSize(-1, -1)) { if (small != QSize(-1, -1)) return QAquaSizeSmall; @@ -1049,7 +1051,7 @@ static QAquaWidgetSize qt_aqua_guess_size(const QWidget *widg, QSize large, QSiz } #ifndef QT_NO_MAINWINDOW - if (qobject_cast(widg->window()) || qEnvironmentVariableIsSet("QWIDGET_ALL_SMALL")) { + if (qEnvironmentVariableIsSet("QWIDGET_ALL_SMALL")) { //if (small.width() != -1 || small.height() != -1) return QAquaSizeSmall; } else if (qEnvironmentVariableIsSet("QWIDGET_ALL_MINI")) { -- cgit v1.2.3 From 9c83d7f871f95b1cc03fb404bd602df32056b9cb Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Fri, 30 Sep 2016 15:39:03 -0700 Subject: QCocoaMenuBar: Update even if no window is attached Then we need to check if the current active (or focused) window has any menubar associated. In case there isn't, and the menubar has no window associated, then we should update immediately. The previous condition is still valid. Change-Id: I4532ccc87354d91c76b53f5433dc3944b9e29584 Task-number: QTBUG-56275 Reviewed-by: Erik Verbruggen --- src/plugins/platforms/cocoa/qcocoamenubar.h | 1 + src/plugins/platforms/cocoa/qcocoamenubar.mm | 28 +++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.h b/src/plugins/platforms/cocoa/qcocoamenubar.h index e84da7aeb0..2865dd2460 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.h +++ b/src/plugins/platforms/cocoa/qcocoamenubar.h @@ -70,6 +70,7 @@ private: static QCocoaWindow *findWindowForMenubar(); static QCocoaMenuBar *findGlobalMenubar(); + bool needsImmediateUpdate(); bool shouldDisable(QCocoaWindow *active) const; NSMenuItem *nativeItemForMenu(QCocoaMenu *menu) const; diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm index 3523182e6d..82aebf8cf3 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.mm +++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm @@ -89,6 +89,32 @@ QCocoaMenuBar::~QCocoaMenuBar() } } +bool QCocoaMenuBar::needsImmediateUpdate() +{ + if (m_window && m_window->window()->isActive()) { + return true; + } else if (!m_window) { + // Only update if the focus/active window has no + // menubar, which means it'll be using this menubar. + // This is to avoid a modification in a parentless + // menubar to affect a window-assigned menubar. + QWindow *fw = QGuiApplication::focusWindow(); + if (!fw) { + // Same if there's no focus window, BTW. + return true; + } else { + QCocoaWindow *cw = static_cast(fw->handle()); + if (cw && !cw->menubar()) + return true; + } + } + + // Either the menubar is attached to a non-active window, + // or the application's focus window has its own menubar + // (which is different from this one) + return false; +} + void QCocoaMenuBar::insertMenu(QPlatformMenu *platformMenu, QPlatformMenu *before) { QCocoaMenu *menu = static_cast(platformMenu); @@ -129,7 +155,7 @@ void QCocoaMenuBar::insertMenu(QPlatformMenu *platformMenu, QPlatformMenu *befor syncMenu(menu); - if (m_window && m_window->window()->isActive()) + if (needsImmediateUpdate()) updateMenuBarImmediately(); } -- cgit v1.2.3 From 0cffe2135e7303bd103bdf888a345fe0a8b94cd6 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 14 Sep 2016 11:29:37 -0700 Subject: qversiontagging.h: Don't tag binaries in static mode Though there should have been no ill-effects, they happen. Task-number: QTBUG-52605 Change-Id: I9093948278414644a416fffd147444078edc3183 Reviewed-by: Oswald Buddenhagen Reviewed-by: Thiago Macieira --- src/corelib/global/qversiontagging.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/global/qversiontagging.h b/src/corelib/global/qversiontagging.h index fa824d1c89..b46bb7aa77 100644 --- a/src/corelib/global/qversiontagging.h +++ b/src/corelib/global/qversiontagging.h @@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE * There will only be one copy of the section in the output library or application. */ -#if defined(QT_BUILD_CORE_LIB) || defined(QT_BOOTSTRAPPED) || defined(QT_NO_VERSION_TAGGING) +#if defined(QT_BUILD_CORE_LIB) || defined(QT_BOOTSTRAPPED) || defined(QT_NO_VERSION_TAGGING) || defined(QT_STATIC) // don't make tags in QtCore, bootstrapped systems or if the user asked not to #elif defined(Q_CC_GNU) && !defined(Q_OS_ANDROID) # if defined(Q_PROCESSOR_X86) && (defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD_KERNEL)) -- cgit v1.2.3 From 60dbd80e5a26182b3ed9e2c60bb7212dfd32632b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Somers?= Date: Wed, 28 Sep 2016 10:13:27 +0200 Subject: Document that qFuzzyCompare does not work on NaN or infinity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On a discussion in the Development mailing list it became clear that qFuzzyCompare does not work for NaN or infinity values. That was not mentioned in the method documentation though. This patch fixes that hiatus. It also clarifies how to deal with the comparing to 0.0 limitation. Change-Id: I8b6d54cc0c1136e79b0d7be1a62bc9ed394d2575 Reviewed-by: Topi Reiniö Reviewed-by: Martin Smith --- src/corelib/global/qglobal.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 6d7fbce946..59799b07bb 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -4109,8 +4109,10 @@ bool QInternal::activateCallbacks(Callback cb, void **parameters) Compares the floating point value \a p1 and \a p2 and returns \c true if they are considered equal, otherwise \c false. - Note that comparing values where either \a p1 or \a p2 is 0.0 will not work. - The solution to this is to compare against values greater than or equal to 1.0. + Note that comparing values where either \a p1 or \a p2 is 0.0 will not work, + nor does comparing values where one of the values is NaN or infinity. + If one of the values is always 0.0, use qFuzzyIsNull instead. If one of the + values is likely to be 0.0, one solution is to add 1.0 to both values. \snippet code/src_corelib_global_qglobal.cpp 46 -- cgit v1.2.3 From 99ce5d5e85007cb656cd48ab161be14af8f16238 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Wed, 28 Sep 2016 13:58:56 +0200 Subject: iOS: refactor usage of photos into optional plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starting from iOS 10, apps that tries to access photos on the device need to specify the reason for this up front by adding the key 'NSPhotoLibraryUsageDescription' into Info.plist. If the key is missing, the app will be rejected from AppStore. This causes problems for the iOS plugin as it stands since parts of it already tries to access photos, e.g to show an image picker dialog if a file dialog is set to open QStandardPaths::PicturesLocation. This means that currently, all apps written with Qt will be rejected from AppStore unless the developer adds this key, whether he tries to access photos or not. To solve this, we choose to split the plugin into two parts, one that contains the core functionality, and one that contains optional support. The latter will need to be enabled explicit by the developer in the pro file, or in this case, indirectly by adding the right key to the Info.plist. This patch refactors the code in the plugin that gives access to photos into a separate optional plugin called 'nsphotolibrarysupport'. Change-Id: Ic4351eb0bbfffdf840fd88cd00bb29a25907798f Reviewed-by: Tor Arne Vestbø Reviewed-by: Jake Petroules --- src/gui/gui.pro | 1 + src/plugins/platforms/ios/ios.pro | 63 +-- src/plugins/platforms/ios/kernel.pro | 58 +++ .../nsphotolibrarysupport.pro | 22 + .../ios/optional/nsphotolibrarysupport/plugin.json | 3 + .../ios/optional/nsphotolibrarysupport/plugin.mm | 64 +++ .../qiosfileengineassetslibrary.h | 76 ++++ .../qiosfileengineassetslibrary.mm | 469 +++++++++++++++++++++ .../nsphotolibrarysupport/qiosfileenginefactory.h | 55 +++ .../qiosimagepickercontroller.h | 42 ++ .../qiosimagepickercontroller.mm | 66 +++ src/plugins/platforms/ios/optional/optional.pro | 2 + src/plugins/platforms/ios/qiosfiledialog.h | 2 + src/plugins/platforms/ios/qiosfiledialog.mm | 79 ++-- .../platforms/ios/qiosfileengineassetslibrary.h | 76 ---- .../platforms/ios/qiosfileengineassetslibrary.mm | 469 --------------------- src/plugins/platforms/ios/qiosfileenginefactory.h | 55 --- src/plugins/platforms/ios/qiosintegration.h | 7 +- src/plugins/platforms/ios/qiosintegration.mm | 8 + .../platforms/ios/qiosoptionalplugininterface.h | 59 +++ 20 files changed, 967 insertions(+), 709 deletions(-) create mode 100644 src/plugins/platforms/ios/kernel.pro create mode 100644 src/plugins/platforms/ios/optional/nsphotolibrarysupport/nsphotolibrarysupport.pro create mode 100644 src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.json create mode 100644 src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.mm create mode 100644 src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h create mode 100644 src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm create mode 100644 src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileenginefactory.h create mode 100644 src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h create mode 100644 src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm create mode 100644 src/plugins/platforms/ios/optional/optional.pro delete mode 100644 src/plugins/platforms/ios/qiosfileengineassetslibrary.h delete mode 100644 src/plugins/platforms/ios/qiosfileengineassetslibrary.mm delete mode 100644 src/plugins/platforms/ios/qiosfileenginefactory.h create mode 100644 src/plugins/platforms/ios/qiosoptionalplugininterface.h (limited to 'src') diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 69434e801f..2cdb3a7ab7 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -9,6 +9,7 @@ QMAKE_DOCS = $$PWD/doc/qtgui.qdocconf MODULE_PLUGIN_TYPES = \ platforms \ + platforms/darwin \ xcbglintegrations \ platformthemes \ platforminputcontexts \ diff --git a/src/plugins/platforms/ios/ios.pro b/src/plugins/platforms/ios/ios.pro index 545db8c093..594ccefcf1 100644 --- a/src/plugins/platforms/ios/ios.pro +++ b/src/plugins/platforms/ios/ios.pro @@ -1,61 +1,2 @@ -TARGET = qios - -QT += core-private gui-private platformsupport-private -LIBS += -framework Foundation -framework UIKit -framework QuartzCore -framework AssetsLibrary - -OBJECTIVE_SOURCES = \ - plugin.mm \ - qiosintegration.mm \ - qioseventdispatcher.mm \ - qioswindow.mm \ - qiosscreen.mm \ - qiosbackingstore.mm \ - qiosapplicationdelegate.mm \ - qiosapplicationstate.mm \ - qiosviewcontroller.mm \ - qioscontext.mm \ - qiosinputcontext.mm \ - qiostheme.mm \ - qiosglobal.mm \ - qiosservices.mm \ - quiview.mm \ - qiosclipboard.mm \ - quiaccessibilityelement.mm \ - qiosplatformaccessibility.mm \ - qiostextresponder.mm \ - qiosmenu.mm \ - qiosfileengineassetslibrary.mm \ - qiosfiledialog.mm - -HEADERS = \ - qiosintegration.h \ - qioseventdispatcher.h \ - qioswindow.h \ - qiosscreen.h \ - qiosbackingstore.h \ - qiosapplicationdelegate.h \ - qiosapplicationstate.h \ - qiosviewcontroller.h \ - qioscontext.h \ - qiosinputcontext.h \ - qiostheme.h \ - qiosglobal.h \ - qiosservices.h \ - quiview.h \ - qiosclipboard.h \ - quiaccessibilityelement.h \ - qiosplatformaccessibility.h \ - qiostextresponder.h \ - qiosmenu.h \ - qiosfileenginefactory.h \ - qiosfileengineassetslibrary.h \ - qiosfiledialog.h - -OTHER_FILES = \ - quiview_textinput.mm \ - quiview_accessibility.mm - -PLUGIN_TYPE = platforms -PLUGIN_CLASS_NAME = QIOSIntegrationPlugin -!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - -load(qt_plugin) +TEMPLATE = subdirs +SUBDIRS = kernel.pro optional diff --git a/src/plugins/platforms/ios/kernel.pro b/src/plugins/platforms/ios/kernel.pro new file mode 100644 index 0000000000..84035877c5 --- /dev/null +++ b/src/plugins/platforms/ios/kernel.pro @@ -0,0 +1,58 @@ +TARGET = qios + +QT += core-private gui-private platformsupport-private +LIBS += -framework Foundation -framework UIKit -framework QuartzCore + +OBJECTIVE_SOURCES = \ + plugin.mm \ + qiosintegration.mm \ + qioseventdispatcher.mm \ + qioswindow.mm \ + qiosscreen.mm \ + qiosbackingstore.mm \ + qiosapplicationdelegate.mm \ + qiosapplicationstate.mm \ + qiosviewcontroller.mm \ + qioscontext.mm \ + qiosinputcontext.mm \ + qiostheme.mm \ + qiosglobal.mm \ + qiosservices.mm \ + quiview.mm \ + qiosclipboard.mm \ + quiaccessibilityelement.mm \ + qiosplatformaccessibility.mm \ + qiostextresponder.mm \ + qiosmenu.mm \ + qiosfiledialog.mm + +HEADERS = \ + qiosintegration.h \ + qioseventdispatcher.h \ + qioswindow.h \ + qiosscreen.h \ + qiosbackingstore.h \ + qiosapplicationdelegate.h \ + qiosapplicationstate.h \ + qiosviewcontroller.h \ + qioscontext.h \ + qiosinputcontext.h \ + qiostheme.h \ + qiosglobal.h \ + qiosservices.h \ + quiview.h \ + qiosclipboard.h \ + quiaccessibilityelement.h \ + qiosplatformaccessibility.h \ + qiostextresponder.h \ + qiosmenu.h \ + qiosfiledialog.h + +OTHER_FILES = \ + quiview_textinput.mm \ + quiview_accessibility.mm + +PLUGIN_TYPE = platforms +PLUGIN_CLASS_NAME = QIOSIntegrationPlugin +!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - +load(qt_plugin) diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/nsphotolibrarysupport.pro b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/nsphotolibrarysupport.pro new file mode 100644 index 0000000000..f4588dda03 --- /dev/null +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/nsphotolibrarysupport.pro @@ -0,0 +1,22 @@ +TARGET = qiosnsphotolibrarysupport + +QT += core gui gui-private +LIBS += -framework UIKit -framework AssetsLibrary + +HEADERS = \ + qiosfileengineassetslibrary.h \ + qiosfileenginefactory.h \ + qiosimagepickercontroller.h + +OBJECTIVE_SOURCES = \ + plugin.mm \ + qiosfileengineassetslibrary.mm \ + qiosimagepickercontroller.mm \ + +OTHER_FILES = \ + plugin.json + +PLUGIN_CLASS_NAME = QIosOptionalPlugin_NSPhotoLibrary +PLUGIN_EXTENDS = - +PLUGIN_TYPE = platforms/darwin +load(qt_plugin) diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.json b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.json new file mode 100644 index 0000000000..4491fb3d59 --- /dev/null +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "NSPhotoLibrarySupport" ] +} diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.mm b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.mm new file mode 100644 index 0000000000..2ec0d33a41 --- /dev/null +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/plugin.mm @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "../../qiosoptionalplugininterface.h" +#include "../../qiosfiledialog.h" + +#include "qiosimagepickercontroller.h" +#include "qiosfileenginefactory.h" + +QT_BEGIN_NAMESPACE + +class QIosOptionalPlugin_NSPhotoLibrary : public QObject, QIosOptionalPluginInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QIosOptionalPluginInterface_iid FILE "plugin.json") + Q_INTERFACES(QIosOptionalPluginInterface) + +public: + explicit QIosOptionalPlugin_NSPhotoLibrary(QObject* = 0) {}; + ~QIosOptionalPlugin_NSPhotoLibrary() {} + + UIViewController* createImagePickerController(QIOSFileDialog *fileDialog) const override + { + return [[[QIOSImagePickerController alloc] initWithQIOSFileDialog:fileDialog] autorelease]; + } + +private: + QIOSFileEngineFactory m_fileEngineFactory; + +}; + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h new file mode 100644 index 0000000000..0e29a1003e --- /dev/null +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QIOSFILEENGINEASSETSLIBRARY_H +#define QIOSFILEENGINEASSETSLIBRARY_H + +#include + +Q_FORWARD_DECLARE_OBJC_CLASS(ALAsset); +class QIOSAssetData; + +class QIOSFileEngineAssetsLibrary : public QAbstractFileEngine +{ +public: + QIOSFileEngineAssetsLibrary(const QString &fileName); + ~QIOSFileEngineAssetsLibrary(); + + bool open(QIODevice::OpenMode openMode) Q_DECL_OVERRIDE; + bool close() Q_DECL_OVERRIDE; + FileFlags fileFlags(FileFlags type) const Q_DECL_OVERRIDE; + qint64 size() const Q_DECL_OVERRIDE; + qint64 read(char *data, qint64 maxlen) Q_DECL_OVERRIDE; + qint64 pos() const Q_DECL_OVERRIDE; + bool seek(qint64 pos) Q_DECL_OVERRIDE; + QString fileName(FileName file) const Q_DECL_OVERRIDE; + void setFileName(const QString &file) Q_DECL_OVERRIDE; + QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const Q_DECL_OVERRIDE; + +#ifndef QT_NO_FILESYSTEMITERATOR + Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) Q_DECL_OVERRIDE; + Iterator *endEntryList() Q_DECL_OVERRIDE; +#endif + + void setError(QFile::FileError error, const QString &str) { QAbstractFileEngine::setError(error, str); } + +private: + QString m_fileName; + QString m_assetUrl; + qint64 m_offset; + mutable QIOSAssetData *m_data; + + ALAsset *loadAsset() const; +}; + +#endif // QIOSFILEENGINEASSETSLIBRARY_H + diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm new file mode 100644 index 0000000000..2e88c37c01 --- /dev/null +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm @@ -0,0 +1,469 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qiosfileengineassetslibrary.h" + +#import +#import + +#include +#include +#include +#include +#include + +static QThreadStorage g_iteratorCurrentUrl; +static QThreadStorage > g_assetDataCache; + +static const int kBufferSize = 10; +static ALAsset *kNoAsset = 0; + +static bool ensureAuthorizationDialogNotBlocked() +{ + if ([ALAssetsLibrary authorizationStatus] != ALAuthorizationStatusNotDetermined) + return true; + + if (static_cast(QObjectPrivate::get(qApp))->in_exec) + return true; + + if ([NSThread isMainThread]) { + // The dialog is about to show, but since main has not finished, the dialog will be held + // back until the launch completes. This is problematic since we cannot successfully return + // back to the caller before the asset is ready, which also includes showing the dialog. To + // work around this, we create an event loop to that will complete the launch (return from the + // applicationDidFinishLaunching callback). But this will only work if we're on the main thread. + QEventLoop loop; + QTimer::singleShot(1, &loop, &QEventLoop::quit); + loop.exec(); + } else { + NSLog(@"QIOSFileEngine: unable to show assets authorization dialog from non-gui thread before QApplication is executing."); + return false; + } + + return true; +} + +// ------------------------------------------------------------------------- + +class QIOSAssetEnumerator +{ +public: + QIOSAssetEnumerator(ALAssetsLibrary *assetsLibrary, ALAssetsGroupType type) + : m_semWriteAsset(dispatch_semaphore_create(kBufferSize)) + , m_semReadAsset(dispatch_semaphore_create(0)) + , m_stop(false) + , m_assetsLibrary([assetsLibrary retain]) + , m_type(type) + , m_buffer(QVector(kBufferSize)) + , m_readIndex(0) + , m_writeIndex(0) + , m_nextAssetReady(false) + { + if (!ensureAuthorizationDialogNotBlocked()) + writeAsset(kNoAsset); + else + startEnumerate(); + } + + ~QIOSAssetEnumerator() + { + m_stop = true; + + // Flush and autorelease remaining assets in the buffer + while (hasNext()) + next(); + + // Documentation states that we need to balance out calls to 'wait' + // and 'signal'. Since the enumeration function always will be one 'wait' + // ahead, we need to signal m_semProceedToNextAsset one last time. + dispatch_semaphore_signal(m_semWriteAsset); + dispatch_release(m_semReadAsset); + dispatch_release(m_semWriteAsset); + + [m_assetsLibrary autorelease]; + } + + bool hasNext() + { + if (!m_nextAssetReady) { + dispatch_semaphore_wait(m_semReadAsset, DISPATCH_TIME_FOREVER); + m_nextAssetReady = true; + } + return m_buffer[m_readIndex] != kNoAsset; + } + + ALAsset *next() + { + Q_ASSERT(m_nextAssetReady); + Q_ASSERT(m_buffer[m_readIndex]); + + ALAsset *asset = [m_buffer[m_readIndex] autorelease]; + dispatch_semaphore_signal(m_semWriteAsset); + + m_readIndex = (m_readIndex + 1) % kBufferSize; + m_nextAssetReady = false; + return asset; + } + +private: + dispatch_semaphore_t m_semWriteAsset; + dispatch_semaphore_t m_semReadAsset; + std::atomic_bool m_stop; + + ALAssetsLibrary *m_assetsLibrary; + ALAssetsGroupType m_type; + QVector m_buffer; + int m_readIndex; + int m_writeIndex; + bool m_nextAssetReady; + + void writeAsset(ALAsset *asset) + { + dispatch_semaphore_wait(m_semWriteAsset, DISPATCH_TIME_FOREVER); + m_buffer[m_writeIndex] = [asset retain]; + dispatch_semaphore_signal(m_semReadAsset); + m_writeIndex = (m_writeIndex + 1) % kBufferSize; + } + + void startEnumerate() + { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [m_assetsLibrary enumerateGroupsWithTypes:m_type usingBlock:^(ALAssetsGroup *group, BOOL *stopEnumerate) { + + if (!group) { + writeAsset(kNoAsset); + return; + } + + if (m_stop) { + *stopEnumerate = true; + return; + } + + [group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stopEnumerate) { + Q_UNUSED(index); + if (!asset || ![[asset valueForProperty:ALAssetPropertyType] isEqual:ALAssetTypePhoto]) + return; + + writeAsset(asset); + *stopEnumerate = m_stop; + }]; + } failureBlock:^(NSError *error) { + NSLog(@"QIOSFileEngine: %@", error); + writeAsset(kNoAsset); + }]; + }); + } + +}; + +// ------------------------------------------------------------------------- + +class QIOSAssetData : public QObject +{ +public: + QIOSAssetData(const QString &assetUrl, QIOSFileEngineAssetsLibrary *engine) + : m_asset(0) + , m_assetUrl(assetUrl) + , m_assetLibrary(0) + { + if (!ensureAuthorizationDialogNotBlocked()) + return; + + if (QIOSAssetData *assetData = g_assetDataCache.localData()) { + // It's a common pattern that QFiles pointing to the same path are created and destroyed + // several times during a single event loop cycle. To avoid loading the same asset + // over and over, we check if the last loaded asset has not been destroyed yet, and try to + // reuse its data. + if (assetData->m_assetUrl == assetUrl) { + m_assetLibrary = [assetData->m_assetLibrary retain]; + m_asset = [assetData->m_asset retain]; + return; + } + } + + // We can only load images from the asset library async. And this might take time, since it + // involves showing the authorization dialog. But the QFile API is synchronuous, so we need to + // wait until we have access to the data. [ALAssetLibrary assetForUrl:] will shedule a block on + // the current thread. But instead of spinning the event loop to force the block to execute, we + // wrap the call inside a synchronuous dispatch queue so that it executes on another thread. + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + NSURL *url = [NSURL URLWithString:assetUrl.toNSString()]; + m_assetLibrary = [[ALAssetsLibrary alloc] init]; + [m_assetLibrary assetForURL:url resultBlock:^(ALAsset *asset) { + + if (!asset) { + // When an asset couldn't be loaded, chances are that it belongs to ALAssetsGroupPhotoStream. + // Such assets can be stored in the cloud and might need to be downloaded first. Unfortunately, + // forcing that to happen is hidden behind private APIs ([ALAsset requestDefaultRepresentation]). + // As a work-around, we search for it instead, since that will give us a pointer to the asset. + QIOSAssetEnumerator e(m_assetLibrary, ALAssetsGroupPhotoStream); + while (e.hasNext()) { + ALAsset *a = e.next(); + QString url = QUrl::fromNSURL([a valueForProperty:ALAssetPropertyAssetURL]).toString(); + if (url == assetUrl) { + asset = a; + break; + } + } + } + + if (!asset) + engine->setError(QFile::OpenError, QLatin1String("could not open image")); + + m_asset = [asset retain]; + dispatch_semaphore_signal(semaphore); + } failureBlock:^(NSError *error) { + engine->setError(QFile::OpenError, QString::fromNSString(error.localizedDescription)); + dispatch_semaphore_signal(semaphore); + }]; + }); + + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_release(semaphore); + + g_assetDataCache.setLocalData(this); + } + + ~QIOSAssetData() + { + [m_assetLibrary release]; + [m_asset release]; + if (g_assetDataCache.localData() == this) + g_assetDataCache.setLocalData(0); + } + + ALAsset *m_asset; + +private: + QString m_assetUrl; + ALAssetsLibrary *m_assetLibrary; +}; + +// ------------------------------------------------------------------------- + +#ifndef QT_NO_FILESYSTEMITERATOR + +class QIOSFileEngineIteratorAssetsLibrary : public QAbstractFileEngineIterator +{ +public: + QIOSAssetEnumerator *m_enumerator; + + QIOSFileEngineIteratorAssetsLibrary( + QDir::Filters filters, const QStringList &nameFilters) + : QAbstractFileEngineIterator(filters, nameFilters) + , m_enumerator(new QIOSAssetEnumerator([[[ALAssetsLibrary alloc] init] autorelease], ALAssetsGroupAll)) + { + } + + ~QIOSFileEngineIteratorAssetsLibrary() + { + delete m_enumerator; + g_iteratorCurrentUrl.setLocalData(QString()); + } + + QString next() Q_DECL_OVERRIDE + { + // Cache the URL that we are about to return, since QDir will immediately create a + // new file engine on the file and ask if it exists. Unless we do this, we end up + // creating a new ALAsset just to verify its existence, which will be especially + // costly for assets belonging to ALAssetsGroupPhotoStream. + ALAsset *asset = m_enumerator->next(); + QString url = QUrl::fromNSURL([asset valueForProperty:ALAssetPropertyAssetURL]).toString(); + g_iteratorCurrentUrl.setLocalData(url); + return url; + } + + bool hasNext() const Q_DECL_OVERRIDE + { + return m_enumerator->hasNext(); + } + + QString currentFileName() const Q_DECL_OVERRIDE + { + return g_iteratorCurrentUrl.localData(); + } + + QFileInfo currentFileInfo() const Q_DECL_OVERRIDE + { + return QFileInfo(currentFileName()); + } +}; + +#endif + +// ------------------------------------------------------------------------- + +QIOSFileEngineAssetsLibrary::QIOSFileEngineAssetsLibrary(const QString &fileName) + : m_offset(0) + , m_data(0) +{ + setFileName(fileName); +} + +QIOSFileEngineAssetsLibrary::~QIOSFileEngineAssetsLibrary() +{ + close(); +} + +ALAsset *QIOSFileEngineAssetsLibrary::loadAsset() const +{ + if (!m_data) + m_data = new QIOSAssetData(m_assetUrl, const_cast(this)); + return m_data->m_asset; +} + +bool QIOSFileEngineAssetsLibrary::open(QIODevice::OpenMode openMode) +{ + if (openMode & (QIODevice::WriteOnly | QIODevice::Text)) + return false; + return loadAsset(); +} + +bool QIOSFileEngineAssetsLibrary::close() +{ + if (m_data) { + // Delete later, so that we can reuse the asset if a QFile is + // opened with the same path during the same event loop cycle. + m_data->deleteLater(); + m_data = 0; + } + return true; +} + +QAbstractFileEngine::FileFlags QIOSFileEngineAssetsLibrary::fileFlags(QAbstractFileEngine::FileFlags type) const +{ + QAbstractFileEngine::FileFlags flags = 0; + const bool isDir = (m_assetUrl == QLatin1String("assets-library://")); + const bool exists = isDir || m_assetUrl == g_iteratorCurrentUrl.localData() || loadAsset(); + + if (!exists) + return flags; + + if (type & FlagsMask) + flags |= ExistsFlag; + if (type & PermsMask) { + ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus]; + if (status != ALAuthorizationStatusRestricted && status != ALAuthorizationStatusDenied) + flags |= ReadOwnerPerm | ReadUserPerm | ReadGroupPerm | ReadOtherPerm; + } + if (type & TypesMask) + flags |= isDir ? DirectoryType : FileType; + + return flags; +} + +qint64 QIOSFileEngineAssetsLibrary::size() const +{ + if (ALAsset *asset = loadAsset()) + return [[asset defaultRepresentation] size]; + return 0; +} + +qint64 QIOSFileEngineAssetsLibrary::read(char *data, qint64 maxlen) +{ + ALAsset *asset = loadAsset(); + if (!asset) + return -1; + + qint64 bytesRead = qMin(maxlen, size() - m_offset); + if (!bytesRead) + return 0; + + NSError *error = 0; + [[asset defaultRepresentation] getBytes:(uint8_t *)data fromOffset:m_offset length:bytesRead error:&error]; + + if (error) { + setError(QFile::ReadError, QString::fromNSString(error.localizedDescription)); + return -1; + } + + m_offset += bytesRead; + return bytesRead; +} + +qint64 QIOSFileEngineAssetsLibrary::pos() const +{ + return m_offset; +} + +bool QIOSFileEngineAssetsLibrary::seek(qint64 pos) +{ + if (pos >= size()) + return false; + m_offset = pos; + return true; +} + +QString QIOSFileEngineAssetsLibrary::fileName(FileName file) const +{ + Q_UNUSED(file); + return m_fileName; +} + +void QIOSFileEngineAssetsLibrary::setFileName(const QString &file) +{ + if (m_data) + close(); + m_fileName = file; + // QUrl::fromLocalFile() will remove double slashes. Since the asset url is + // passed around as a file name in the app (and converted to/from a file url, e.g + // in QFileDialog), we need to ensure that m_assetUrl ends up being valid. + int index = file.indexOf(QLatin1String("/asset")); + if (index == -1) + m_assetUrl = QLatin1String("assets-library://"); + else + m_assetUrl = QLatin1String("assets-library:/") + file.mid(index); +} + +QStringList QIOSFileEngineAssetsLibrary::entryList(QDir::Filters filters, const QStringList &filterNames) const +{ + return QAbstractFileEngine::entryList(filters, filterNames); +} + +#ifndef QT_NO_FILESYSTEMITERATOR + +QAbstractFileEngine::Iterator *QIOSFileEngineAssetsLibrary::beginEntryList( + QDir::Filters filters, const QStringList &filterNames) +{ + return new QIOSFileEngineIteratorAssetsLibrary(filters, filterNames); +} + +QAbstractFileEngine::Iterator *QIOSFileEngineAssetsLibrary::endEntryList() +{ + return 0; +} + +#endif diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileenginefactory.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileenginefactory.h new file mode 100644 index 0000000000..29f543caae --- /dev/null +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileenginefactory.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QIOSFILEENGINEFACTORY_H +#define QIOSFILEENGINEFACTORY_H + +#include +#include +#include "qiosfileengineassetslibrary.h" + +class QIOSFileEngineFactory : public QAbstractFileEngineHandler +{ +public: + QAbstractFileEngine* create(const QString &fileName) const + { + static QLatin1String assetsScheme("assets-library:"); + + if (fileName.toLower().startsWith(assetsScheme)) + return new QIOSFileEngineAssetsLibrary(fileName); + + return 0; + } +}; + +#endif // QIOSFILEENGINEFACTORY_H diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h new file mode 100644 index 0000000000..df3f6b9fa3 --- /dev/null +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import + +#include "../../qiosfiledialog.h" + +@interface QIOSImagePickerController : UIImagePickerController { + QIOSFileDialog *m_fileDialog; +} +- (id)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog; +@end diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm new file mode 100644 index 0000000000..f9662b964a --- /dev/null +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosimagepickercontroller.mm @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import + +#include "qiosimagepickercontroller.h" + +@implementation QIOSImagePickerController + +- (id)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog +{ + self = [super init]; + if (self) { + m_fileDialog = fileDialog; + [self setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]; + [self setDelegate:self]; + } + return self; +} + +- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info +{ + Q_UNUSED(picker); + NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL]; + QUrl fileUrl = QUrl::fromLocalFile(QString::fromNSString([url description])); + m_fileDialog->selectedFilesChanged(QList() << fileUrl); + emit m_fileDialog->accept(); +} + +- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker +{ + Q_UNUSED(picker) + emit m_fileDialog->reject(); +} + +@end diff --git a/src/plugins/platforms/ios/optional/optional.pro b/src/plugins/platforms/ios/optional/optional.pro new file mode 100644 index 0000000000..5e3421a025 --- /dev/null +++ b/src/plugins/platforms/ios/optional/optional.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = nsphotolibrarysupport diff --git a/src/plugins/platforms/ios/qiosfiledialog.h b/src/plugins/platforms/ios/qiosfiledialog.h index b4bf85edd9..668cf18caf 100644 --- a/src/plugins/platforms/ios/qiosfiledialog.h +++ b/src/plugins/platforms/ios/qiosfiledialog.h @@ -66,6 +66,8 @@ private: QList m_selection; QEventLoop m_eventLoop; UIViewController *m_viewController; + + bool showImagePickerDialog(QWindow *parent); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosfiledialog.mm b/src/plugins/platforms/ios/qiosfiledialog.mm index 7c32784e9d..a70eb11ef8 100644 --- a/src/plugins/platforms/ios/qiosfiledialog.mm +++ b/src/plugins/platforms/ios/qiosfiledialog.mm @@ -31,52 +31,18 @@ ** ****************************************************************************/ -#include "qiosfiledialog.h" - #import #include #include +#include -@interface QIOSImagePickerController : UIImagePickerController { - QIOSFileDialog *m_fileDialog; -} -@end - -@implementation QIOSImagePickerController - -- (id)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog -{ - self = [super init]; - if (self) { - m_fileDialog = fileDialog; - [self setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]; - [self setDelegate:self]; - } - return self; -} - -- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info -{ - Q_UNUSED(picker); - NSURL *url = [info objectForKey:UIImagePickerControllerReferenceURL]; - QUrl fileUrl = QUrl::fromLocalFile(QString::fromNSString([url description])); - m_fileDialog->selectedFilesChanged(QList() << fileUrl); - emit m_fileDialog->accept(); -} - -- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker -{ - Q_UNUSED(picker) - emit m_fileDialog->reject(); -} - -@end - -// -------------------------------------------------------------------------- +#include "qiosfiledialog.h" +#include "qiosintegration.h" +#include "qiosoptionalplugininterface.h" QIOSFileDialog::QIOSFileDialog() - : m_viewController(0) + : m_viewController(Q_NULLPTR) { } @@ -98,17 +64,36 @@ bool QIOSFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality window bool acceptOpen = options()->acceptMode() == QFileDialogOptions::AcceptOpen; QString directory = options()->initialDirectory().toLocalFile(); - if (acceptOpen && directory.startsWith(QLatin1String("assets-library:"))) { - m_viewController = [[QIOSImagePickerController alloc] initWithQIOSFileDialog:this]; - UIWindow *window = parent ? reinterpret_cast(parent->winId()).window - : [UIApplication sharedApplication].keyWindow; - [window.rootViewController presentViewController:m_viewController animated:YES completion:nil]; - return true; - } + if (acceptOpen && directory.startsWith(QLatin1String("assets-library:"))) + return showImagePickerDialog(parent); return false; } +bool QIOSFileDialog::showImagePickerDialog(QWindow *parent) +{ + if (!m_viewController) { + QFactoryLoader *plugins = QIOSIntegration::instance()->optionalPlugins(); + for (int i = 0; i < plugins->metaData().size(); ++i) { + QIosOptionalPluginInterface *plugin = qobject_cast(plugins->instance(i)); + m_viewController = [plugin->createImagePickerController(this) retain]; + if (m_viewController) + break; + } + } + + if (!m_viewController) { + qWarning() << "QIOSFileDialog: Could not resolve Qt plugin that gives access to photos on iOS"; + return false; + } + + UIWindow *window = parent ? reinterpret_cast(parent->winId()).window + : [UIApplication sharedApplication].keyWindow; + [window.rootViewController presentViewController:m_viewController animated:YES completion:nil]; + + return true; +} + void QIOSFileDialog::hide() { // QFileDialog will remember the last directory set, and open subsequent dialogs in the same @@ -120,6 +105,8 @@ void QIOSFileDialog::hide() emit directoryEntered(QUrl::fromLocalFile(QDir::currentPath())); [m_viewController dismissViewControllerAnimated:YES completion:nil]; + [m_viewController release]; + m_viewController = Q_NULLPTR; m_eventLoop.exit(); } diff --git a/src/plugins/platforms/ios/qiosfileengineassetslibrary.h b/src/plugins/platforms/ios/qiosfileengineassetslibrary.h deleted file mode 100644 index 37bbc7bf23..0000000000 --- a/src/plugins/platforms/ios/qiosfileengineassetslibrary.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QIOSFILEENGINEASSETSLIBRARY_H -#define QIOSFILEENGINEASSETSLIBRARY_H - -#include - -Q_FORWARD_DECLARE_OBJC_CLASS(ALAsset); -class QIOSAssetData; - -class QIOSFileEngineAssetsLibrary : public QAbstractFileEngine -{ -public: - QIOSFileEngineAssetsLibrary(const QString &fileName); - ~QIOSFileEngineAssetsLibrary(); - - bool open(QIODevice::OpenMode openMode) Q_DECL_OVERRIDE; - bool close() Q_DECL_OVERRIDE; - FileFlags fileFlags(FileFlags type) const Q_DECL_OVERRIDE; - qint64 size() const Q_DECL_OVERRIDE; - qint64 read(char *data, qint64 maxlen) Q_DECL_OVERRIDE; - qint64 pos() const Q_DECL_OVERRIDE; - bool seek(qint64 pos) Q_DECL_OVERRIDE; - QString fileName(FileName file) const Q_DECL_OVERRIDE; - void setFileName(const QString &file) Q_DECL_OVERRIDE; - QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const Q_DECL_OVERRIDE; - -#ifndef QT_NO_FILESYSTEMITERATOR - Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) Q_DECL_OVERRIDE; - Iterator *endEntryList() Q_DECL_OVERRIDE; -#endif - - void setError(QFile::FileError error, const QString &str) { QAbstractFileEngine::setError(error, str); } - -private: - QString m_fileName; - QString m_assetUrl; - qint64 m_offset; - mutable QIOSAssetData *m_data; - - ALAsset *loadAsset() const; -}; - -#endif // QIOSFILEENGINEASSETSLIBRARY_H - diff --git a/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm b/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm deleted file mode 100644 index bb12c164d6..0000000000 --- a/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm +++ /dev/null @@ -1,469 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qiosfileengineassetslibrary.h" - -#import -#import - -#include -#include -#include -#include -#include - -static QThreadStorage g_iteratorCurrentUrl; -static QThreadStorage > g_assetDataCache; - -static const int kBufferSize = 10; -static ALAsset *kNoAsset = 0; - -static bool ensureAuthorizationDialogNotBlocked() -{ - if ([ALAssetsLibrary authorizationStatus] != ALAuthorizationStatusNotDetermined) - return true; - - if (static_cast(QObjectPrivate::get(qApp))->in_exec) - return true; - - if ([NSThread isMainThread]) { - // The dialog is about to show, but since main has not finished, the dialog will be held - // back until the launch completes. This is problematic since we cannot successfully return - // back to the caller before the asset is ready, which also includes showing the dialog. To - // work around this, we create an event loop to that will complete the launch (return from the - // applicationDidFinishLaunching callback). But this will only work if we're on the main thread. - QEventLoop loop; - QTimer::singleShot(1, &loop, &QEventLoop::quit); - loop.exec(); - } else { - NSLog(@"QIOSFileEngine: unable to show assets authorization dialog from non-gui thread before QApplication is executing."); - return false; - } - - return true; -} - -// ------------------------------------------------------------------------- - -class QIOSAssetEnumerator -{ -public: - QIOSAssetEnumerator(ALAssetsLibrary *assetsLibrary, ALAssetsGroupType type) - : m_semWriteAsset(dispatch_semaphore_create(kBufferSize)) - , m_semReadAsset(dispatch_semaphore_create(0)) - , m_stop(false) - , m_assetsLibrary([assetsLibrary retain]) - , m_type(type) - , m_buffer(QVector(kBufferSize)) - , m_readIndex(0) - , m_writeIndex(0) - , m_nextAssetReady(false) - { - if (!ensureAuthorizationDialogNotBlocked()) - writeAsset(kNoAsset); - else - startEnumerate(); - } - - ~QIOSAssetEnumerator() - { - m_stop = true; - - // Flush and autorelease remaining assets in the buffer - while (hasNext()) - next(); - - // Documentation states that we need to balance out calls to 'wait' - // and 'signal'. Since the enumeration function always will be one 'wait' - // ahead, we need to signal m_semProceedToNextAsset one last time. - dispatch_semaphore_signal(m_semWriteAsset); - dispatch_release(m_semReadAsset); - dispatch_release(m_semWriteAsset); - - [m_assetsLibrary autorelease]; - } - - bool hasNext() - { - if (!m_nextAssetReady) { - dispatch_semaphore_wait(m_semReadAsset, DISPATCH_TIME_FOREVER); - m_nextAssetReady = true; - } - return m_buffer[m_readIndex] != kNoAsset; - } - - ALAsset *next() - { - Q_ASSERT(m_nextAssetReady); - Q_ASSERT(m_buffer[m_readIndex]); - - ALAsset *asset = [m_buffer[m_readIndex] autorelease]; - dispatch_semaphore_signal(m_semWriteAsset); - - m_readIndex = (m_readIndex + 1) % kBufferSize; - m_nextAssetReady = false; - return asset; - } - -private: - dispatch_semaphore_t m_semWriteAsset; - dispatch_semaphore_t m_semReadAsset; - std::atomic_bool m_stop; - - ALAssetsLibrary *m_assetsLibrary; - ALAssetsGroupType m_type; - QVector m_buffer; - int m_readIndex; - int m_writeIndex; - bool m_nextAssetReady; - - void writeAsset(ALAsset *asset) - { - dispatch_semaphore_wait(m_semWriteAsset, DISPATCH_TIME_FOREVER); - m_buffer[m_writeIndex] = [asset retain]; - dispatch_semaphore_signal(m_semReadAsset); - m_writeIndex = (m_writeIndex + 1) % kBufferSize; - } - - void startEnumerate() - { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [m_assetsLibrary enumerateGroupsWithTypes:m_type usingBlock:^(ALAssetsGroup *group, BOOL *stopEnumerate) { - - if (!group) { - writeAsset(kNoAsset); - return; - } - - if (m_stop) { - *stopEnumerate = true; - return; - } - - [group enumerateAssetsUsingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stopEnumerate) { - Q_UNUSED(index); - if (!asset || ![[asset valueForProperty:ALAssetPropertyType] isEqual:ALAssetTypePhoto]) - return; - - writeAsset(asset); - *stopEnumerate = m_stop; - }]; - } failureBlock:^(NSError *error) { - NSLog(@"QIOSFileEngine: %@", error); - writeAsset(kNoAsset); - }]; - }); - } - -}; - -// ------------------------------------------------------------------------- - -class QIOSAssetData : public QObject -{ -public: - QIOSAssetData(const QString &assetUrl, QIOSFileEngineAssetsLibrary *engine) - : m_asset(0) - , m_assetUrl(assetUrl) - , m_assetLibrary(0) - { - if (!ensureAuthorizationDialogNotBlocked()) - return; - - if (QIOSAssetData *assetData = g_assetDataCache.localData()) { - // It's a common pattern that QFiles pointing to the same path are created and destroyed - // several times during a single event loop cycle. To avoid loading the same asset - // over and over, we check if the last loaded asset has not been destroyed yet, and try to - // reuse its data. - if (assetData->m_assetUrl == assetUrl) { - m_assetLibrary = [assetData->m_assetLibrary retain]; - m_asset = [assetData->m_asset retain]; - return; - } - } - - // We can only load images from the asset library async. And this might take time, since it - // involves showing the authorization dialog. But the QFile API is synchronuous, so we need to - // wait until we have access to the data. [ALAssetLibrary assetForUrl:] will shedule a block on - // the current thread. But instead of spinning the event loop to force the block to execute, we - // wrap the call inside a synchronuous dispatch queue so that it executes on another thread. - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSURL *url = [NSURL URLWithString:assetUrl.toNSString()]; - m_assetLibrary = [[ALAssetsLibrary alloc] init]; - [m_assetLibrary assetForURL:url resultBlock:^(ALAsset *asset) { - - if (!asset) { - // When an asset couldn't be loaded, chances are that it belongs to ALAssetsGroupPhotoStream. - // Such assets can be stored in the cloud and might need to be downloaded first. Unfortunately, - // forcing that to happen is hidden behind private APIs ([ALAsset requestDefaultRepresentation]). - // As a work-around, we search for it instead, since that will give us a pointer to the asset. - QIOSAssetEnumerator e(m_assetLibrary, ALAssetsGroupPhotoStream); - while (e.hasNext()) { - ALAsset *a = e.next(); - QString url = QUrl::fromNSURL([a valueForProperty:ALAssetPropertyAssetURL]).toString(); - if (url == assetUrl) { - asset = a; - break; - } - } - } - - if (!asset) - engine->setError(QFile::OpenError, QLatin1String("could not open image")); - - m_asset = [asset retain]; - dispatch_semaphore_signal(semaphore); - } failureBlock:^(NSError *error) { - engine->setError(QFile::OpenError, QString::fromNSString(error.localizedDescription)); - dispatch_semaphore_signal(semaphore); - }]; - }); - - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - dispatch_release(semaphore); - - g_assetDataCache.setLocalData(this); - } - - ~QIOSAssetData() - { - [m_assetLibrary release]; - [m_asset release]; - if (g_assetDataCache.localData() == this) - g_assetDataCache.setLocalData(0); - } - - ALAsset *m_asset; - -private: - QString m_assetUrl; - ALAssetsLibrary *m_assetLibrary; -}; - -// ------------------------------------------------------------------------- - -#ifndef QT_NO_FILESYSTEMITERATOR - -class QIOSFileEngineIteratorAssetsLibrary : public QAbstractFileEngineIterator -{ -public: - QIOSAssetEnumerator *m_enumerator; - - QIOSFileEngineIteratorAssetsLibrary( - QDir::Filters filters, const QStringList &nameFilters) - : QAbstractFileEngineIterator(filters, nameFilters) - , m_enumerator(new QIOSAssetEnumerator([[[ALAssetsLibrary alloc] init] autorelease], ALAssetsGroupAll)) - { - } - - ~QIOSFileEngineIteratorAssetsLibrary() - { - delete m_enumerator; - g_iteratorCurrentUrl.setLocalData(QString()); - } - - QString next() Q_DECL_OVERRIDE - { - // Cache the URL that we are about to return, since QDir will immediately create a - // new file engine on the file and ask if it exists. Unless we do this, we end up - // creating a new ALAsset just to verify its existence, which will be especially - // costly for assets belonging to ALAssetsGroupPhotoStream. - ALAsset *asset = m_enumerator->next(); - QString url = QUrl::fromNSURL([asset valueForProperty:ALAssetPropertyAssetURL]).toString(); - g_iteratorCurrentUrl.setLocalData(url); - return url; - } - - bool hasNext() const Q_DECL_OVERRIDE - { - return m_enumerator->hasNext(); - } - - QString currentFileName() const Q_DECL_OVERRIDE - { - return g_iteratorCurrentUrl.localData(); - } - - QFileInfo currentFileInfo() const Q_DECL_OVERRIDE - { - return QFileInfo(currentFileName()); - } -}; - -#endif - -// ------------------------------------------------------------------------- - -QIOSFileEngineAssetsLibrary::QIOSFileEngineAssetsLibrary(const QString &fileName) - : m_offset(0) - , m_data(0) -{ - setFileName(fileName); -} - -QIOSFileEngineAssetsLibrary::~QIOSFileEngineAssetsLibrary() -{ - close(); -} - -ALAsset *QIOSFileEngineAssetsLibrary::loadAsset() const -{ - if (!m_data) - m_data = new QIOSAssetData(m_assetUrl, const_cast(this)); - return m_data->m_asset; -} - -bool QIOSFileEngineAssetsLibrary::open(QIODevice::OpenMode openMode) -{ - if (openMode & (QIODevice::WriteOnly | QIODevice::Text)) - return false; - return loadAsset(); -} - -bool QIOSFileEngineAssetsLibrary::close() -{ - if (m_data) { - // Delete later, so that we can reuse the asset if a QFile is - // opened with the same path during the same event loop cycle. - m_data->deleteLater(); - m_data = 0; - } - return true; -} - -QAbstractFileEngine::FileFlags QIOSFileEngineAssetsLibrary::fileFlags(QAbstractFileEngine::FileFlags type) const -{ - QAbstractFileEngine::FileFlags flags = 0; - const bool isDir = (m_assetUrl == QLatin1String("assets-library://")); - const bool exists = isDir || m_assetUrl == g_iteratorCurrentUrl.localData() || loadAsset(); - - if (!exists) - return flags; - - if (type & FlagsMask) - flags |= ExistsFlag; - if (type & PermsMask) { - ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus]; - if (status != ALAuthorizationStatusRestricted && status != ALAuthorizationStatusDenied) - flags |= ReadOwnerPerm | ReadUserPerm | ReadGroupPerm | ReadOtherPerm; - } - if (type & TypesMask) - flags |= isDir ? DirectoryType : FileType; - - return flags; -} - -qint64 QIOSFileEngineAssetsLibrary::size() const -{ - if (ALAsset *asset = loadAsset()) - return [[asset defaultRepresentation] size]; - return 0; -} - -qint64 QIOSFileEngineAssetsLibrary::read(char *data, qint64 maxlen) -{ - ALAsset *asset = loadAsset(); - if (!asset) - return -1; - - qint64 bytesRead = qMin(maxlen, size() - m_offset); - if (!bytesRead) - return 0; - - NSError *error = 0; - [[asset defaultRepresentation] getBytes:(uint8_t *)data fromOffset:m_offset length:bytesRead error:&error]; - - if (error) { - setError(QFile::ReadError, QString::fromNSString(error.localizedDescription)); - return -1; - } - - m_offset += bytesRead; - return bytesRead; -} - -qint64 QIOSFileEngineAssetsLibrary::pos() const -{ - return m_offset; -} - -bool QIOSFileEngineAssetsLibrary::seek(qint64 pos) -{ - if (pos >= size()) - return false; - m_offset = pos; - return true; -} - -QString QIOSFileEngineAssetsLibrary::fileName(FileName file) const -{ - Q_UNUSED(file); - return m_fileName; -} - -void QIOSFileEngineAssetsLibrary::setFileName(const QString &file) -{ - if (m_data) - close(); - m_fileName = file; - // QUrl::fromLocalFile() will remove double slashes. Since the asset url is - // passed around as a file name in the app (and converted to/from a file url, e.g - // in QFileDialog), we need to ensure that m_assetUrl ends up being valid. - int index = file.indexOf(QLatin1String("/asset")); - if (index == -1) - m_assetUrl = QLatin1String("assets-library://"); - else - m_assetUrl = QLatin1String("assets-library:/") + file.mid(index); -} - -QStringList QIOSFileEngineAssetsLibrary::entryList(QDir::Filters filters, const QStringList &filterNames) const -{ - return QAbstractFileEngine::entryList(filters, filterNames); -} - -#ifndef QT_NO_FILESYSTEMITERATOR - -QAbstractFileEngine::Iterator *QIOSFileEngineAssetsLibrary::beginEntryList( - QDir::Filters filters, const QStringList &filterNames) -{ - return new QIOSFileEngineIteratorAssetsLibrary(filters, filterNames); -} - -QAbstractFileEngine::Iterator *QIOSFileEngineAssetsLibrary::endEntryList() -{ - return 0; -} - -#endif diff --git a/src/plugins/platforms/ios/qiosfileenginefactory.h b/src/plugins/platforms/ios/qiosfileenginefactory.h deleted file mode 100644 index a8604c77d1..0000000000 --- a/src/plugins/platforms/ios/qiosfileenginefactory.h +++ /dev/null @@ -1,55 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QIOSFILEENGINEFACTORY_H -#define QIOSFILEENGINEFACTORY_H - -#include -#include -#include "qiosfileengineassetslibrary.h" - -class QIOSFileEngineFactory : public QAbstractFileEngineHandler -{ -public: - QAbstractFileEngine* create(const QString &fileName) const - { - static QLatin1String assetsScheme("assets-library:"); - - if (fileName.toLower().startsWith(assetsScheme)) - return new QIOSFileEngineAssetsLibrary(fileName); - - return 0; - } -}; - -#endif // QIOSFILEENGINEFACTORY_H diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h index 7d23fe1d62..25eb8990cb 100644 --- a/src/plugins/platforms/ios/qiosintegration.h +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -38,8 +38,9 @@ #include #include +#include + #include "qiosapplicationstate.h" -#include "qiosfileenginefactory.h" QT_BEGIN_NAMESPACE @@ -91,6 +92,8 @@ public: void setDebugWindowManagement(bool); bool debugWindowManagement() const; + QFactoryLoader *optionalPlugins() { return m_optionalPlugins; } + private: QPlatformFontDatabase *m_fontDatabase; QPlatformClipboard *m_clipboard; @@ -99,7 +102,7 @@ private: QIOSApplicationState m_applicationState; QIOSServices *m_platformServices; mutable QPlatformAccessibility *m_accessibility; - QIOSFileEngineFactory m_fileEngineFactory; + QFactoryLoader *m_optionalPlugins; bool m_debugWindowManagement; }; diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 85b5c477cc..d33c8cf47c 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -43,6 +43,7 @@ #include "qiosinputcontext.h" #include "qiostheme.h" #include "qiosservices.h" +#include "qiosoptionalplugininterface.h" #include @@ -68,6 +69,7 @@ QIOSIntegration::QIOSIntegration() , m_inputContext(0) , m_platformServices(new QIOSServices) , m_accessibility(0) + , m_optionalPlugins(new QFactoryLoader(QIosOptionalPluginInterface_iid, QLatin1String("/platforms/darwin"))) , m_debugWindowManagement(false) { if (![UIApplication sharedApplication]) { @@ -112,6 +114,9 @@ QIOSIntegration::QIOSIntegration() m_touchDevice->setCapabilities(touchCapabilities); QWindowSystemInterface::registerTouchDevice(m_touchDevice); QMacInternalPasteboardMime::initializeMimeTypes(); + + for (int i = 0; i < m_optionalPlugins->metaData().size(); ++i) + qobject_cast(m_optionalPlugins->instance(i))->initPlugin(); } QIOSIntegration::~QIOSIntegration() @@ -134,6 +139,9 @@ QIOSIntegration::~QIOSIntegration() delete m_accessibility; m_accessibility = 0; + + delete m_optionalPlugins; + m_optionalPlugins = 0; } bool QIOSIntegration::hasCapability(Capability cap) const diff --git a/src/plugins/platforms/ios/qiosoptionalplugininterface.h b/src/plugins/platforms/ios/qiosoptionalplugininterface.h new file mode 100644 index 0000000000..bcb8978e02 --- /dev/null +++ b/src/plugins/platforms/ios/qiosoptionalplugininterface.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QIOPLUGININTERFACE_H +#define QIOPLUGININTERFACE_H + +#include + +#include "qiosfiledialog.h" + +QT_BEGIN_NAMESPACE + +Q_FORWARD_DECLARE_OBJC_CLASS(UIViewController); + +#define QIosOptionalPluginInterface_iid "org.qt-project.Qt.QPA.ios.optional" + +class QIosOptionalPluginInterface +{ +public: + virtual ~QIosOptionalPluginInterface() {} + virtual void initPlugin() const {}; + virtual UIViewController* createImagePickerController(QIOSFileDialog *) const { return Q_NULLPTR; }; +}; + +Q_DECLARE_INTERFACE(QIosOptionalPluginInterface, QIosOptionalPluginInterface_iid) + +QT_END_NAMESPACE + +#endif // QIOPLUGININTERFACE_H -- cgit v1.2.3 From 78ee77f49122ac525590715a0a034965e3e59adf Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Tue, 4 Oct 2016 15:04:04 +0200 Subject: iOS: link photo lib plugin based on Info.plist contents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the application's Info.plist contains the key 'NSPhotoLibraryUsageDescription', we know that we can safely link in qiosnsphotolibrarysupport without violating AppStore requirements. This is a simple feature that doesn't introduce additional qmake API for doing app deployment with optional iOS QPA plugins. [ChangeLog][iOS] Starting from iOS 10, Apple requires all apps that need access to photos to have the key 'NSPhotoLibraryUsageDescription' in the Info.plist. Therefore, to get the same support in Qt (when, e.g., using a file dialog), the Info.plist assigned to QMAKE_INFO_PLIST will need this key as well. Change-Id: I7a93afe24b589cad96d5a1d9e2a155ad1671178a Reviewed-by: Tor Arne Vestbø Reviewed-by: Jake Petroules --- src/widgets/dialogs/qfiledialog.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index 61f5f7b0d2..f8e4299397 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -896,6 +896,9 @@ void QFileDialogPrivate::_q_goToUrl(const QUrl &url) {QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).last()}, a native image picker dialog will be used for accessing the user's photo album. The filename returned can be loaded using QFile and related APIs. + For this to be enabled, the Info.plist assigned to QMAKE_INFO_PLIST in the + project file must contain the key \c NSPhotoLibraryUsageDescription. See + Info.plist documentation from Apple for more information regarding this key. This feature was added in Qt 5.5. */ void QFileDialog::setDirectory(const QString &directory) -- cgit v1.2.3 From aa2e2a8d5e4712d93879098675c0a46df0fd7a41 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 25 Oct 2016 11:56:33 +0200 Subject: Android: Fix synthesized oblique for non-latin scripts In 5e3e34731b7880ac775e8f1fa156ce016e6820f1, a default implementation of QPlatformFontDatabase::fallbacksForFamily() was added, but this implementation included only the fonts with a matching style in the returned list. The result of this was that if a font face for a specific language did not have e.g. an italic font, then we would show missing glyph boxes instead. On Android, it would be impossible to show any italic text in Chinese, Japanese, Hebrew, or Arabic. [ChangeLog][QtGui][Text] Fixed synthesized oblique for non-latin text on platforms using the basic font database, such as Android. Task-number: QTBUG-51223 Change-Id: I494d8ad87292b65d4380a2e600c1c0dc7fc8f937 Reviewed-by: Lars Knoll Reviewed-by: Konstantin Ritt --- src/gui/text/qfontdatabase.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 04081a5ca7..f1b2e84a1e 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -805,7 +805,8 @@ QStringList QPlatformFontDatabase::fallbacksForFamily(const QString &family, QFo Q_UNUSED(family); Q_UNUSED(styleHint); - QStringList retList; + QStringList preferredFallbacks; + QStringList otherFallbacks; size_t writingSystem = std::find(scriptForWritingSystem, scriptForWritingSystem + QFontDatabase::WritingSystemsCount, @@ -826,18 +827,18 @@ QStringList QPlatformFontDatabase::fallbacksForFamily(const QString &family, QFo QtFontFoundry *foundry = f->foundries[j]; for (int k = 0; k < foundry->count; ++k) { - if (style == foundry->styles[k]->key.style) { - if (foundry->name.isEmpty()) - retList.append(f->name); - else - retList.append(f->name + QLatin1String(" [") + foundry->name + QLatin1Char(']')); - break; - } + QString name = foundry->name.isEmpty() + ? f->name + : f->name + QLatin1String(" [") + foundry->name + QLatin1Char(']'); + if (style == foundry->styles[k]->key.style) + preferredFallbacks.append(name); + else + otherFallbacks.append(name); } } } - return retList; + return preferredFallbacks + otherFallbacks; } static void initializeDb(); -- cgit v1.2.3 From b9af823ef7a4131bc61b07620e7825d757df69db Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Tue, 18 Oct 2016 13:09:37 +0200 Subject: qiosfileengineassetslibrary: replace Q_DECL_OVERRIDE with override Change-Id: Iba67e1a1fa86dff3c82543597351b597be69ed1f Reviewed-by: Jake Petroules --- .../qiosfileengineassetslibrary.h | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h index 0e29a1003e..89696751c6 100644 --- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h +++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h @@ -45,20 +45,20 @@ public: QIOSFileEngineAssetsLibrary(const QString &fileName); ~QIOSFileEngineAssetsLibrary(); - bool open(QIODevice::OpenMode openMode) Q_DECL_OVERRIDE; - bool close() Q_DECL_OVERRIDE; - FileFlags fileFlags(FileFlags type) const Q_DECL_OVERRIDE; - qint64 size() const Q_DECL_OVERRIDE; - qint64 read(char *data, qint64 maxlen) Q_DECL_OVERRIDE; - qint64 pos() const Q_DECL_OVERRIDE; - bool seek(qint64 pos) Q_DECL_OVERRIDE; - QString fileName(FileName file) const Q_DECL_OVERRIDE; - void setFileName(const QString &file) Q_DECL_OVERRIDE; - QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const Q_DECL_OVERRIDE; + bool open(QIODevice::OpenMode openMode) override; + bool close() override; + FileFlags fileFlags(FileFlags type) const override; + qint64 size() const override; + qint64 read(char *data, qint64 maxlen) override; + qint64 pos() const override; + bool seek(qint64 pos) override; + QString fileName(FileName file) const override; + void setFileName(const QString &file) override; + QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const override; #ifndef QT_NO_FILESYSTEMITERATOR - Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) Q_DECL_OVERRIDE; - Iterator *endEntryList() Q_DECL_OVERRIDE; + Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override; + Iterator *endEntryList() override; #endif void setError(QFile::FileError error, const QString &str) { QAbstractFileEngine::setError(error, str); } -- cgit v1.2.3 From e8b55a6d2a6e44337b75dd0d116b1b020a437631 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Wed, 19 Oct 2016 20:22:10 +0200 Subject: Document qGuiApp and tweap qApp Change-Id: I2cd865da0e081251a2702c11cb83dde35444693a Reviewed-by: Edward Welbourne --- src/gui/kernel/qguiapplication.cpp | 10 ++++++++++ src/widgets/kernel/qapplication.cpp | 9 +++------ 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 9cbcd714ab..950385f0ce 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -344,6 +344,16 @@ void QWindowGeometrySpecification::applyTo(QWindow *window) const static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOMETRY_SPECIFICATION_INITIALIZER; +/*! + \macro qGuiApp + \relates QGuiApplication + + A global pointer referring to the unique application object. + Only valid for use when that object is a QGuiApplication. + + \sa QCoreApplication::instance(), qApp +*/ + /*! \class QGuiApplication \brief The QGuiApplication class manages the GUI application's control diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index b64d6e2159..93ee820c98 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -4229,13 +4229,10 @@ void QApplication::beep() \relates QApplication A global pointer referring to the unique application object. It is - equivalent to the pointer returned by the QCoreApplication::instance() - function except that, in GUI applications, it is a pointer to a - QApplication instance. + equivalent to QCoreApplication::instance(), but cast as a QApplication pointer, + so only valid when the unique application object is a QApplication. - Only one application object can be created. - - \sa QCoreApplication::instance() + \sa QCoreApplication::instance(), qGuiApp */ /*! -- cgit v1.2.3 From 4e196159077a820e99bc8bd306d31d0ccaa17c57 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Wed, 26 Oct 2016 11:19:19 +0200 Subject: qmacmime: convert UTF-16 using QTextCodec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current implementation of QMacPasteboardMimeUnicodeText didn't contain any logic to handle unicode text with a byte order mark (BOM). According to the docs (*), 'public.utf16-plain-text' can have an optional BOM. Because of that, Qt would fail encoding UTF-16 text from the pasteboard if it had a BOM. Additionally, perhaps because of a bug in iOS 10, UTF-16 text placed on the pasteboard by Qt ends up being encoded wrong by native apps, unless the text has a BOM. Rather than hard-coding UTF-16 encoding/decoding in qmacmime, we now leave it to QTextCodec. QTextCodec will add a BOM by default, and can handle decoding of UTF-16 both with, and without, a BOM. *: https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html Task-number: QTBUG-56229 Change-Id: I3a08deb0262350c67e5622cf23eb3c3a4907ec39 Reviewed-by: Tor Arne Vestbø --- src/platformsupport/clipboard/qmacmime.mm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/platformsupport/clipboard/qmacmime.mm b/src/platformsupport/clipboard/qmacmime.mm index 2e3257cfcf..cdb5d6b08e 100644 --- a/src/platformsupport/clipboard/qmacmime.mm +++ b/src/platformsupport/clipboard/qmacmime.mm @@ -405,8 +405,7 @@ QVariant QMacPasteboardMimeUnicodeText::convertToMime(const QString &mimetype, Q if (flavor == QLatin1String("public.utf8-plain-text")) { ret = QString::fromUtf8(firstData); } else if (flavor == QLatin1String("public.utf16-plain-text")) { - ret = QString(reinterpret_cast(firstData.constData()), - firstData.size() / sizeof(QChar)); + ret = QTextCodec::codecForName("UTF-16")->toUnicode(firstData); } else { qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); } @@ -420,7 +419,7 @@ QList QMacPasteboardMimeUnicodeText::convertFromMime(const QString & if (flavor == QLatin1String("public.utf8-plain-text")) ret.append(string.toUtf8()); else if (flavor == QLatin1String("public.utf16-plain-text")) - ret.append(QByteArray((char*)string.utf16(), string.length()*2)); + ret.append(QTextCodec::codecForName("UTF-16")->fromUnicode(string)); return ret; } -- cgit v1.2.3