diff --git a/doc/global/manifest-meta.qdocconf b/doc/global/manifest-meta.qdocconf index 6b55e0dbad..20419f1b29 100644 --- a/doc/global/manifest-meta.qdocconf +++ b/doc/global/manifest-meta.qdocconf @@ -32,7 +32,7 @@ # manifestmeta.global.names = * # manifestmeta.global.tags = qt5 -manifestmeta.filters = highlighted webkit1 webkit2 android +manifestmeta.filters = highlighted webkit1 webkit2 android thumbnail manifestmeta.highlighted.names = "QtQuick/Qt Quick Demo - Same Game" \ "QtQuick/Qt Quick Demo - Photo Surface" \ @@ -168,3 +168,28 @@ manifestmeta.android.names = "QtQuick/Qt Quick Demo - Maroon*" \ "QtWidgets/Address Book Example" manifestmeta.android.tags = android + +# add a generic thumbnail image to examples that do not have any images in their documentation +manifestmeta.thumbnail.attributes = "imageUrl:qthelp\://org.qt-project.qtdoc.$QT_VERSION_TAG/qtdoc/images/qt-codesample.png" + +manifestmeta.thumbnail.names = "QtConcurrent/Map Example" \ + "QtConcurrent/QtConcurrent Word Count Example" \ + "QtConcurrent/Run Function Example" \ + "QtGui/Raster Window Example" \ + "QtNetwork/Network Download*" \ + "QtWidgets/Dynamic Layouts Example" \ + "QtWidgets/Event Transitions Example" \ + "QtWidgets/Plug & Paint Basic Tools Example" \ + "QtWidgets/Plug & Paint Extra Filters Example" \ + "QtWidgets/Two-way Button Example" \ + "QtWidgets/Validators Example" \ + "ActiveQt/*" \ + "QtDbus/*" \ + "QtHelp/*" \ + "QtMultimedia/AudioEngine Example" \ + "QtMultimedia/Declarative Radio Example" \ + "QtMultimedia/Media Player Example" \ + "QtQml/Extending QML*" \ + "QtQuick/Qt Quick Examples - Accessibility" \ + "QtSensors/Qt Sensors - SensorGesture QML Type example" \ + "QtWinExtras/Icon Extractor" diff --git a/doc/global/qt-cpp-defines.qdocconf b/doc/global/qt-cpp-defines.qdocconf index 6966e2c4c1..406e37af08 100644 --- a/doc/global/qt-cpp-defines.qdocconf +++ b/doc/global/qt-cpp-defines.qdocconf @@ -26,6 +26,7 @@ Cpp.ignoretokens += \ Q_CORE_EXPORT_INLINE \ Q_DBUS_EXPORT \ Q_DECL_CONSTEXPR \ + Q_DECL_DEPRECATED \ Q_DECL_NOEXCEPT \ Q_DECL_NOTHROW \ Q_DECLARATIVE_EXPORT \ diff --git a/examples/widgets/animation/stickman/graphicsview.h b/examples/widgets/animation/stickman/graphicsview.h index 8042a65897..60abb6448f 100644 --- a/examples/widgets/animation/stickman/graphicsview.h +++ b/examples/widgets/animation/stickman/graphicsview.h @@ -39,7 +39,7 @@ ****************************************************************************/ #ifndef GRAPHICSVIEW_H -#define GRAPHICSVIEW +#define GRAPHICSVIEW_H #include diff --git a/src/concurrent/doc/src/qtconcurrent-index.qdoc b/src/concurrent/doc/src/qtconcurrent-index.qdoc index c884b0da07..d259516119 100644 --- a/src/concurrent/doc/src/qtconcurrent-index.qdoc +++ b/src/concurrent/doc/src/qtconcurrent-index.qdoc @@ -117,8 +117,8 @@ Random access iterators can be faster in cases where Qt Concurrent is iterating over a large number of lightweight items, since they allow skipping to any point in the container. In addition, using random access iterators allows Qt Concurrent - to provide progress information trough QFuture::progressValue() and QFutureWatcher:: - progressValueChanged(). + to provide progress information trough QFuture::progressValue() and + QFutureWatcher::progressValueChanged(). The non in-place modifying functions such as mapped() and filtered() makes a copy of the container when called. If you are using STL containers this copy operation diff --git a/src/concurrent/qtconcurrentfilter.cpp b/src/concurrent/qtconcurrentfilter.cpp index ef0229a695..7889fbff56 100644 --- a/src/concurrent/qtconcurrentfilter.cpp +++ b/src/concurrent/qtconcurrentfilter.cpp @@ -47,7 +47,7 @@ \brief The header provides concurrent Filter and Filter-Reduce. - These functions are a part of the \l {Concurrent Programming}{Qt Concurrent} framework. + These functions are a part of the \l {Qt Concurrent} framework. The QtConcurrent::filter(), QtConcurrent::filtered() and QtConcurrent::filteredReduced() functions filter items in a sequence such diff --git a/src/concurrent/qtconcurrentmap.cpp b/src/concurrent/qtconcurrentmap.cpp index 9718baf48f..da2a601ae2 100644 --- a/src/concurrent/qtconcurrentmap.cpp +++ b/src/concurrent/qtconcurrentmap.cpp @@ -47,8 +47,8 @@ possible to write multi-threaded programs without using low-level threading primitives. - See the \l {Concurrent Programming}{Qt Concurrent} chapter in - the \l{threads.html}{threading} documentation. + See the \l {Qt Concurrent} module documentation for an overview of available + functions, or see below for detailed information on each function. \inheaderfile QtConcurrent \ingroup thread @@ -74,7 +74,7 @@ \brief The header provides concurrent Map and MapReduce. - These functions are a part of the \l {Concurrent Programming}{Qt Concurrent} framework. + These functions are a part of the \l {Qt Concurrent} framework. The QtConcurrent::map(), QtConcurrent::mapped() and QtConcurrent::mappedReduced() functions run computations in parallel on diff --git a/src/concurrent/qtconcurrentrun.cpp b/src/concurrent/qtconcurrentrun.cpp index 4ab1399c0c..4398e1a91f 100644 --- a/src/concurrent/qtconcurrentrun.cpp +++ b/src/concurrent/qtconcurrentrun.cpp @@ -48,7 +48,7 @@ \ingroup thread - This function is a part of the \l {Concurrent Programming}{Qt Concurrent} framework. + This function is a part of the \l {Qt Concurrent} framework. The QtConcurrent::run() function runs a function in a separate thread. The return value of the function is made available through the QFuture API. diff --git a/src/corelib/doc/src/threads.qdoc b/src/corelib/doc/src/threads.qdoc index 9b80f0d200..c250f7466b 100644 --- a/src/corelib/doc/src/threads.qdoc +++ b/src/corelib/doc/src/threads.qdoc @@ -63,7 +63,6 @@ \li \l{Synchronizing Threads} \li \l{Reentrancy and Thread-Safety} \li \l{Threads and QObjects} - \li \l{Concurrent Programming} \li \l{Thread-Support in Qt Modules} \endlist @@ -449,7 +448,7 @@ \previouspage Reentrancy and Thread Safety \contentspage Thread Support in Qt - \nextpage Concurrent Programming + \nextpage Thread-Support in Qt Modules QThread inherits QObject. It emits signals to indicate that the thread started or finished executing, and provides a few slots as @@ -645,114 +644,11 @@ a TCP server asynchronously. */ -/*! - \page threads-qtconcurrent.html - \title Concurrent Programming - - \previouspage Threads and QObjects - \contentspage Thread Support in Qt - \nextpage Thread-Support in Qt Modules - - \target qtconcurrent intro - - The QtConcurrent namespace provides high-level APIs that make it - possible to write multi-threaded programs without using low-level - threading primitives such as mutexes, read-write locks, wait - conditions, or semaphores. Programs written with QtConcurrent - automatically adjust the number of threads used according to the - number of processor cores available. This means that applications - written today will continue to scale when deployed on multi-core - systems in the future. - - QtConcurrent includes functional programming style APIs for - parallel list processing, including a MapReduce and FilterReduce - implementation for shared-memory (non-distributed) systems, and - classes for managing asynchronous computations in GUI - applications: - - \list - - \li QtConcurrent::map() applies a function to every item in a container, - modifying the items in-place. - - \li QtConcurrent::mapped() is like map(), except that it returns a new - container with the modifications. - - \li QtConcurrent::mappedReduced() is like mapped(), except that the - modified results are reduced or folded into a single result. - - \li QtConcurrent::filter() removes all items from a container based on the - result of a filter function. - - \li QtConcurrent::filtered() is like filter(), except that it returns a new - container with the filtered results. - - \li QtConcurrent::filteredReduced() is like filtered(), except that the - filtered results are reduced or folded into a single result. - - \li QtConcurrent::run() runs a function in another thread. - - \li QFuture represents the result of an asynchronous computation. - - \li QFutureIterator allows iterating through results available via QFuture. - - \li QFutureWatcher allows monitoring a QFuture using signals-and-slots. - - \li QFutureSynchronizer is a convenience class that automatically - synchronizes several QFutures. - - \endlist - - Qt Concurrent supports several STL-compatible container and iterator types, - but works best with Qt containers that have random-access iterators, such as - QList or QVector. The map and filter functions accept both containers and begin/end iterators. - - STL Iterator support overview: - - \table - \header - \li Iterator Type - \li Example classes - \li Support status - \row - \li Input Iterator - \li - \li Not Supported - \row - \li Output Iterator - \li - \li Not Supported - \row - \li Forward Iterator - \li std::slist - \li Supported - \row - \li Bidirectional Iterator - \li QLinkedList, std::list - \li Supported - \row - \li Random Access Iterator - \li QList, QVector, std::vector - \li Supported and Recommended - \endtable - - Random access iterators can be faster in cases where Qt Concurrent is iterating - over a large number of lightweight items, since they allow skipping to any point - in the container. In addition, using random access iterators allows Qt Concurrent - to provide progress information trough QFuture::progressValue() and QFutureWatcher:: - progressValueChanged(). - - The non in-place modifying functions such as mapped() and filtered() makes a - copy of the container when called. If you are using STL containers this copy operation - might take some time, in this case we recommend specifying the begin and end iterators - for the container instead. -*/ - /*! \page threads-modules.html \title Thread-Support in Qt Modules - \previouspage Concurrent Programming + \previouspage Threads and QObjects \contentspage Thread Support in Qt \section1 Threads and the SQL Module diff --git a/src/corelib/io/qfilesystemwatcher_inotify.cpp b/src/corelib/io/qfilesystemwatcher_inotify.cpp index 024af79c33..c731f3d417 100644 --- a/src/corelib/io/qfilesystemwatcher_inotify.cpp +++ b/src/corelib/io/qfilesystemwatcher_inotify.cpp @@ -365,11 +365,11 @@ void QInotifyFileSystemWatcherEngine::readFromInotify() // qDebug() << "inotify event, wd" << event.wd << "mask" << hex << event.mask; int id = event.wd; - QString path = idToPath.value(id); + QString path = getPathFromID(id); if (path.isEmpty()) { // perhaps a directory? id = -id; - path = idToPath.value(id); + path = getPathFromID(id); if (path.isEmpty()) continue; } @@ -378,8 +378,9 @@ void QInotifyFileSystemWatcherEngine::readFromInotify() if ((event.mask & (IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT)) != 0) { pathToID.remove(path); - idToPath.remove(id); - inotify_rm_watch(inotifyFd, event.wd); + idToPath.remove(id, getPathFromID(id)); + if (!idToPath.contains(id)) + inotify_rm_watch(inotifyFd, event.wd); if (id < 0) emit directoryChanged(path, true); @@ -394,6 +395,18 @@ void QInotifyFileSystemWatcherEngine::readFromInotify() } } +QString QInotifyFileSystemWatcherEngine::getPathFromID(int id) const +{ + QHash::const_iterator i = idToPath.find(id); + while (i != idToPath.constEnd() && i.key() == id) { + if ((i + 1) == idToPath.constEnd() || (i + 1).key() != id) { + return i.value(); + } + ++i; + } + return QString(); +} + QT_END_NAMESPACE #endif // QT_NO_FILESYSTEMWATCHER diff --git a/src/corelib/io/qfilesystemwatcher_inotify_p.h b/src/corelib/io/qfilesystemwatcher_inotify_p.h index 959d9edc12..d02f04eed6 100644 --- a/src/corelib/io/qfilesystemwatcher_inotify_p.h +++ b/src/corelib/io/qfilesystemwatcher_inotify_p.h @@ -78,11 +78,14 @@ public: private Q_SLOTS: void readFromInotify(); +private: + QString getPathFromID(int id) const; + private: QInotifyFileSystemWatcherEngine(int fd, QObject *parent); int inotifyFd; QHash pathToID; - QHash idToPath; + QMultiHash idToPath; QSocketNotifier notifier; }; diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 9868ea624a..eab3890beb 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -121,17 +121,33 @@ static const int errorBufferMax = 512; static int qt_qprocess_deadChild_pipe[2]; static struct sigaction qt_sa_old_sigchld_handler; -static void qt_sa_sigchld_handler(int signum) +static void qt_sa_sigchld_sigaction(int signum, siginfo_t *info, void *context) { + // *Never* use the info or contect variables in this function + // (except for passing them to the next signal in the chain). + // We cannot be sure if another library or if the application + // installed a signal handler for SIGCHLD without SA_SIGINFO + // and fails to pass the arguments to us. If they do that, + // these arguments contain garbage and we'd most likely crash. + qt_safe_write(qt_qprocess_deadChild_pipe[1], "", 1); #if defined (QPROCESS_DEBUG) fprintf(stderr, "*** SIGCHLD\n"); #endif - // load it as volatile - void (*oldAction)(int) = ((volatile struct sigaction *)&qt_sa_old_sigchld_handler)->sa_handler; - if (oldAction && oldAction != SIG_IGN) - oldAction(signum); + // load as volatile + volatile struct sigaction *vsa = &qt_sa_old_sigchld_handler; + + if (qt_sa_old_sigchld_handler.sa_flags & SA_SIGINFO) { + void (*oldAction)(int, siginfo_t *, void *) = vsa->sa_sigaction; + + oldAction(signum, info, context); + } else { + void (*oldAction)(int) = vsa->sa_handler; + + if (oldAction && oldAction != SIG_IGN) + oldAction(signum); + } } static inline void add_fd(int &nfds, int fd, fd_set *fdset) @@ -197,10 +213,16 @@ QProcessManager::QProcessManager() // set up the SIGCHLD handler, which writes a single byte to the dead // child pipe every time a child dies. + struct sigaction action; - memset(&action, 0, sizeof(action)); - action.sa_handler = qt_sa_sigchld_handler; - action.sa_flags = SA_NOCLDSTOP; + // use the old handler as template, i.e., preserve the signal mask + // otherwise the original signal handler might be interrupted although it + // was marked to never be interrupted + ::sigaction(SIGCHLD, NULL, &action); + action.sa_sigaction = qt_sa_sigchld_sigaction; + // set the SA_SIGINFO flag such that we can use the three argument handler + // function + action.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; ::sigaction(SIGCHLD, &action, &qt_sa_old_sigchld_handler); processManagerInstance = this; @@ -225,7 +247,7 @@ QProcessManager::~QProcessManager() struct sigaction currentAction; ::sigaction(SIGCHLD, 0, ¤tAction); - if (currentAction.sa_handler == qt_sa_sigchld_handler) { + if (currentAction.sa_sigaction == qt_sa_sigchld_sigaction) { ::sigaction(SIGCHLD, &qt_sa_old_sigchld_handler, 0); } diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc index 421e683c4a..7ae3e4a87e 100644 --- a/src/corelib/thread/qfuture.qdoc +++ b/src/corelib/thread/qfuture.qdoc @@ -47,8 +47,7 @@ \ingroup thread - To start a computation, use one of the APIs in the - \l {Concurrent Programming}{Qt Concurrent} framework. + To start a computation, use one of the APIs in the \l {Qt Concurrent} framework. QFuture allows threads to be synchronized against one or more results which will be ready at a later point in time. The result can be of any type @@ -93,7 +92,7 @@ To interact with running tasks using signals and slots, use QFutureWatcher. - \sa QFutureWatcher, {Concurrent Programming}{Qt Concurrent} + \sa QFutureWatcher, {Qt Concurrent} */ /*! \fn QFuture::QFuture() diff --git a/src/corelib/thread/qfuturesynchronizer.qdoc b/src/corelib/thread/qfuturesynchronizer.qdoc index 7ad978f61d..cc31dff46d 100644 --- a/src/corelib/thread/qfuturesynchronizer.qdoc +++ b/src/corelib/thread/qfuturesynchronizer.qdoc @@ -66,7 +66,7 @@ You can query the status of the cancel-on-wait feature using the cancelOnWait() function. - \sa QFuture, QFutureWatcher, {Concurrent Programming}{Qt Concurrent} + \sa QFuture, QFutureWatcher, {Qt Concurrent} */ /*! diff --git a/src/corelib/thread/qfuturewatcher.cpp b/src/corelib/thread/qfuturewatcher.cpp index 53a00a8212..a7f2dd9f73 100644 --- a/src/corelib/thread/qfuturewatcher.cpp +++ b/src/corelib/thread/qfuturewatcher.cpp @@ -98,7 +98,7 @@ QT_BEGIN_NAMESPACE QFutureWatcher as well. This is useful if only status or progress information is needed; not the actual result data. - \sa QFuture, {Concurrent Programming}{Qt Concurrent} + \sa QFuture, {Qt Concurrent} */ /*! \fn QFutureWatcher::QFutureWatcher(QObject *parent) diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 7838b7df8e..3ee9e200de 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2013 Samuel Gaist ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** @@ -280,6 +281,15 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal image.setColorCount(2); image.setColor(1, qRgb(0,0,0)); image.setColor(0, qRgb(255,255,255)); + if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) { + const int g = trans_color_p->gray; + // the image has white in the first position of the color table, + // black in the second. g is 0 for black, 1 for white. + if (g == 0) + image.setColor(1, qRgba(0, 0, 0, 0)); + else if (g == 1) + image.setColor(0, qRgba(255, 255, 255, 0)); + } } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_expand(png_ptr); png_set_strip_16(png_ptr); @@ -406,14 +416,14 @@ static void read_image_scaled(QImage *outImage, png_structp png_ptr, png_infop i QPngHandlerPrivate::AllocatedMemoryPointers &, QSize scaledSize) { - png_uint_32 width; - png_uint_32 height; - png_int_32 offset_x; - png_int_32 offset_y; + png_uint_32 width = 0; + png_uint_32 height = 0; + png_int_32 offset_x = 0; + png_int_32 offset_y = 0; - int bit_depth; - int color_type; - int unit_type; + int bit_depth = 0; + int color_type = 0; + int unit_type = PNG_OFFSET_PIXEL; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, &unit_type); uchar *data = outImage->bits(); diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index 5088e9cdc8..a07b5def5d 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -2293,13 +2293,30 @@ bool qt_scaleForTransform(const QTransform &transform, qreal *scale) return qFuzzyCompare(xScale, yScale); } - const qreal xScale = transform.m11() * transform.m11() + // rotate then scale: compare columns + const qreal xScale1 = transform.m11() * transform.m11() + transform.m21() * transform.m21(); - const qreal yScale = transform.m12() * transform.m12() + const qreal yScale1 = transform.m12() * transform.m12() + transform.m22() * transform.m22(); - if (scale) - *scale = qSqrt(qMax(xScale, yScale)); - return type == QTransform::TxRotate && qFuzzyCompare(xScale, yScale); + + // scale then rotate: compare rows + const qreal xScale2 = transform.m11() * transform.m11() + + transform.m12() * transform.m12(); + const qreal yScale2 = transform.m21() * transform.m21() + + transform.m22() * transform.m22(); + + // decide the order of rotate and scale operations + if (qAbs(xScale1 - yScale1) > qAbs(xScale2 - yScale2)) { + if (scale) + *scale = qSqrt(qMax(xScale1, yScale1)); + + return type == QTransform::TxRotate && qFuzzyCompare(xScale1, yScale1); + } else { + if (scale) + *scale = qSqrt(qMax(xScale2, yScale2)); + + return type == QTransform::TxRotate && qFuzzyCompare(xScale2, yScale2); + } } QT_END_NAMESPACE diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index c3e3716b26..b6a3b5e3ab 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -509,7 +509,7 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated. if (channels[i].authMethod != QAuthenticatorPrivate::None) { - if (!(channels[i].authMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 401)) { + if ((channels[i].authMethod != QAuthenticatorPrivate::Ntlm && request.headerField("Authorization").isEmpty()) || channels[i].lastStatus == 401) { QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator); if (priv && priv->method != QAuthenticatorPrivate::None) { QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false)); diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 6e61eea5a4..da82fdf8d2 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -236,8 +236,8 @@ bool QHttpNetworkConnectionChannel::sendRequest() QAuthenticator &auth = authenticator; if (url.userName() != auth.user() || (!url.password().isEmpty() && url.password() != auth.password())) { - auth.setUser(url.userName()); - auth.setPassword(url.password()); + auth.setUser(url.userName(QUrl::FullyDecoded)); + auth.setPassword(url.password(QUrl::FullyDecoded)); connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false); } // clear the userinfo, since we use the same request for resending diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 91655ef485..101e1a8c25 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -1358,8 +1358,8 @@ void QNetworkAccessManagerPrivate::authenticationRequired(QAuthenticator *authen // if credentials are included in the url, then use them if (!url.userName().isEmpty() && !url.password().isEmpty()) { - authenticator->setUser(url.userName()); - authenticator->setPassword(url.password()); + authenticator->setUser(url.userName(QUrl::FullyDecoded)); + authenticator->setPassword(url.password(QUrl::FullyDecoded)); *urlForLastAuthentication = url; authenticationManager->cacheCredentials(url, authenticator); return; diff --git a/src/plugins/accessible/widgets/main.cpp b/src/plugins/accessible/widgets/main.cpp index 686a90ca96..36ee784aac 100644 --- a/src/plugins/accessible/widgets/main.cpp +++ b/src/plugins/accessible/widgets/main.cpp @@ -115,7 +115,7 @@ QAccessibleInterface *AccessibleFactory::create(const QString &classname, QObjec QToolButton *tb = qobject_cast(widget); if (!tb->menu()) role = tb->isCheckable() ? QAccessible::CheckBox : QAccessible::PushButton; - else if (!tb->popupMode() != QToolButton::DelayedPopup) + else if (tb->popupMode() == QToolButton::DelayedPopup) role = QAccessible::ButtonDropDown; else #endif diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index 7831888da1..8620ef4267 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -125,7 +125,7 @@ public: QList possibleKeys(const QKeyEvent *event) const; void updateScreens(); - QCocoaScreen *screenAtIndex(int index) const { return mScreens.at(index); } + QCocoaScreen *screenAtIndex(int index); private: diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 6d1882f622..365fa92470 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -343,6 +343,14 @@ void QCocoaIntegration::updateScreens() screen->setVirtualSiblings(siblings); } +QCocoaScreen *QCocoaIntegration::screenAtIndex(int index) +{ + if (index >= mScreens.count()) + updateScreens(); + + return mScreens.at(index); +} + bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) const { switch (cap) { diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index ab48d18af4..dd292fd2a3 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -157,12 +157,122 @@ private: QByteArray format_atoms; }; +class INCRTransaction; +typedef QMap TransactionMap; +static TransactionMap *transactions = 0; + +//#define INCR_DEBUG + +class INCRTransaction : public QObject +{ + Q_OBJECT +public: + INCRTransaction(QXcbConnection *c, xcb_window_t w, xcb_atom_t p, + QByteArray d, uint i, xcb_atom_t t, int f, int to) : + conn(c), win(w), property(p), data(d), increment(i), + target(t), format(f), timeout(to), offset(0) + { + const quint32 values[] = { XCB_EVENT_MASK_PROPERTY_CHANGE }; + xcb_change_window_attributes(conn->xcb_connection(), win, + XCB_CW_EVENT_MASK, values); + if (!transactions) { +#ifdef INCR_DEBUG + qDebug("INCRTransaction: creating the TransactionMap"); +#endif + transactions = new TransactionMap; + conn->clipboard()->setProcessIncr(true); + } + transactions->insert(win, this); + abort_timer = startTimer(timeout); + } + + ~INCRTransaction() + { + if (abort_timer) + killTimer(abort_timer); + abort_timer = 0; + transactions->remove(win); + if (transactions->isEmpty()) { +#ifdef INCR_DEBUG + qDebug("INCRTransaction: no more INCR transactions left in the TransactionMap"); +#endif + delete transactions; + transactions = 0; + conn->clipboard()->setProcessIncr(false); + } + } + + void updateIncrProperty(xcb_property_notify_event_t *event, bool &accepted) + { + xcb_connection_t *c = conn->xcb_connection(); + if (event->atom == property && event->state == XCB_PROPERTY_DELETE) { + accepted = true; + // restart the timer + if (abort_timer) + killTimer(abort_timer); + abort_timer = startTimer(timeout); + + unsigned int bytes_left = data.size() - offset; + if (bytes_left > 0) { + unsigned int bytes_to_send = qMin(increment, bytes_left); +#ifdef INCR_DEBUG + qDebug("INCRTransaction: sending %d bytes, %d remaining (INCR transaction %p)", + bytes_to_send, bytes_left - bytes_to_send, this); +#endif + int dataSize = bytes_to_send / (format / 8); + xcb_change_property(c, XCB_PROP_MODE_REPLACE, win, property, + target, format, dataSize, data.constData() + offset); + offset += bytes_to_send; + } else { +#ifdef INCR_DEBUG + qDebug("INCRTransaction: INCR transaction %p completed", this); +#endif + xcb_change_property(c, XCB_PROP_MODE_REPLACE, win, property, + target, format, 0, (const void *)0); + const quint32 values[] = { XCB_EVENT_MASK_NO_EVENT }; + xcb_change_window_attributes(conn->xcb_connection(), win, + XCB_CW_EVENT_MASK, values); + // self destroy + delete this; + } + } + } + +protected: + void timerEvent(QTimerEvent *ev) + { + if (ev->timerId() == abort_timer) { + // this can happen when the X client we are sending data + // to decides to exit (normally or abnormally) +#ifdef INCR_DEBUG + qDebug("INCRTransaction: Timed out while sending data to %p", this); +#endif + delete this; + } + } + +private: + QXcbConnection *conn; + xcb_window_t win; + xcb_atom_t property; + QByteArray data; + uint increment; + xcb_atom_t target; + int format; + int timeout; + uint offset; + int abort_timer; +}; + const int QXcbClipboard::clipboard_timeout = 5000; QXcbClipboard::QXcbClipboard(QXcbConnection *c) : QXcbObject(c), QPlatformClipboard() , m_requestor(XCB_NONE) , m_owner(XCB_NONE) + , m_incr_active(false) + , m_clipboard_closing(false) + , m_incr_receive_time(0) { Q_ASSERT(QClipboard::Clipboard == 0); Q_ASSERT(QClipboard::Selection == 1); @@ -200,6 +310,7 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c) QXcbClipboard::~QXcbClipboard() { + m_clipboard_closing = true; // Transfer the clipboard content to the clipboard manager if we own a selection if (m_timestamp[QClipboard::Clipboard] != XCB_CURRENT_TIME || m_timestamp[QClipboard::Selection] != XCB_CURRENT_TIME) { @@ -224,6 +335,17 @@ QXcbClipboard::~QXcbClipboard() } } +void QXcbClipboard::incrTransactionPeeker(xcb_generic_event_t *ge, bool &accepted) +{ + uint response_type = ge->response_type & ~0x80; + if (response_type == XCB_PROPERTY_NOTIFY) { + xcb_property_notify_event_t *event = (xcb_property_notify_event_t *)ge; + TransactionMap::Iterator it = transactions->find(event->window); + if (it != transactions->end()) { + (*it)->updateIncrProperty(event, accepted); + } + } +} xcb_window_t QXcbClipboard::getSelectionOwner(xcb_atom_t atom) const { @@ -415,16 +537,17 @@ xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_win // Motif clients (since Motif doesn't support INCR) static xcb_atom_t motif_clip_temporary = atom(QXcbAtom::CLIP_TEMPORARY); bool allow_incr = property != motif_clip_temporary; - + // This 'bool' can be removed once there is a proper fix for QTBUG-32853 + if (m_clipboard_closing) + allow_incr = false; // X_ChangeProperty protocol request is 24 bytes const int increment = (xcb_get_maximum_request_length(xcb_connection()) * 4) - 24; if (data.size() > increment && allow_incr) { long bytes = data.size(); xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property, atom(QXcbAtom::INCR), 32, 1, (const void *)&bytes); - -// (void)new QClipboardINCRTransaction(window, property, atomFormat, dataFormat, data, increment); - qWarning("QXcbClipboard: INCR is unimplemented"); + new INCRTransaction(connection(), window, property, data, increment, + atomFormat, dataFormat, clipboard_timeout); return property; } @@ -611,7 +734,7 @@ static inline int maxSelectionIncr(xcb_connection_t *c) return (l > 65536 ? 65536*4 : l*4) - 100; } -bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format) const +bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format) { int maxsize = maxSelectionIncr(xcb_connection()); ulong bytes_left; // bytes_after @@ -687,7 +810,8 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, // correct size, not 0-term. if (size) *size = buffer_offset; - + if (*type == atom(QXcbAtom::INCR)) + m_incr_receive_time = connection()->getTimestamp(); if (deleteProperty) xcb_delete_property(xcb_connection(), win, property); @@ -791,6 +915,7 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb bool alloc_error = false; int length; int offset = 0; + xcb_timestamp_t prev_time = m_incr_receive_time; if (nbytes > 0) { // Reserve buffer + zero-terminator (for text data) @@ -805,10 +930,14 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY, clipboard_timeout); if (!ge) break; - xcb_property_notify_event_t *event = (xcb_property_notify_event_t *)ge; - if (event->atom != property || event->state != XCB_PROPERTY_NEW_VALUE) + + if (event->atom != property + || event->state != XCB_PROPERTY_NEW_VALUE + || event->time < prev_time) continue; + prev_time = event->time; + if (clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0)) { if (length == 0) { // no more data, we're done if (nullterm) { diff --git a/src/plugins/platforms/xcb/qxcbclipboard.h b/src/plugins/platforms/xcb/qxcbclipboard.h index 61cdce3d1d..03021aa606 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.h +++ b/src/plugins/platforms/xcb/qxcbclipboard.h @@ -78,11 +78,15 @@ public: void handleSelectionClearRequest(xcb_selection_clear_event_t *event); void handleXFixesSelectionRequest(xcb_xfixes_selection_notify_event_t *event); - bool clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format) const; + bool clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format); QByteArray clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm); QByteArray getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtatom); + void setProcessIncr(bool process) { m_incr_active = process; } + bool processIncr() { return m_incr_active; } + void incrTransactionPeeker(xcb_generic_event_t *ge, bool &accepted); + xcb_window_t getSelectionOwner(xcb_atom_t atom) const; QByteArray getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t = 0); @@ -107,6 +111,9 @@ private: static const int clipboard_timeout; + bool m_incr_active; + bool m_clipboard_closing; + xcb_timestamp_t m_incr_receive_time; }; #endif // QT_NO_CLIPBOARD diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index ff5b36c448..d6f64d29fd 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -1164,6 +1164,12 @@ void QXcbConnection::processXcbEvents() continue; } + bool accepted = false; + if (clipboard()->processIncr()) + clipboard()->incrTransactionPeeker(event, accepted); + if (accepted) + continue; + QVector::iterator it = m_peekFuncs.begin(); while (it != m_peekFuncs.end()) { // These callbacks return true if the event is what they were diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 46c3fdce5c..1f5169e8d9 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -648,7 +648,6 @@ void QXcbWindow::show() if (!transientXcbParent) transientXcbParent = static_cast(screen())->clientLeader(); if (transientXcbParent) { // ICCCM 4.1.2.6 - m_gravity = XCB_GRAVITY_CENTER; Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32, 1, &transientXcbParent)); @@ -1304,9 +1303,6 @@ QRect QXcbWindow::windowToWmGeometry(QRect r) const r.translate(m_frameMargins.left(), m_frameMargins.top()); } else if (!frameInclusive && m_gravity == XCB_GRAVITY_NORTH_WEST) { r.translate(-m_frameMargins.left(), -m_frameMargins.top()); - } else if (!frameInclusive && m_gravity == XCB_GRAVITY_CENTER) { - r.translate(-(m_frameMargins.left() - m_frameMargins.right())/2, - -(m_frameMargins.top() - m_frameMargins.bottom())/2); } return r; } diff --git a/src/printsupport/dialogs/qprintdialog_mac.mm b/src/printsupport/dialogs/qprintdialog_mac.mm index 9839268af4..37333f2593 100644 --- a/src/printsupport/dialogs/qprintdialog_mac.mm +++ b/src/printsupport/dialogs/qprintdialog_mac.mm @@ -197,7 +197,7 @@ void QPrintDialogPrivate::openCocoaPrintPanel(Qt::WindowModality modality) // close down during the cleanup (QTBUG-17913): qApp->processEvents(QEventLoop::ExcludeUserInputEvents, QEventLoop::ExcludeSocketNotifiers); - QT_MANGLE_NAMESPACE(QCocoaPrintPanelDelegate) *delegate = [[QT_MANGLE_NAMESPACE(QCocoaPrintPanelDelegate) alloc] init]; + QT_MANGLE_NAMESPACE(QCocoaPrintPanelDelegate) *delegate = [[QT_MANGLE_NAMESPACE(QCocoaPrintPanelDelegate) alloc] initWithNSPrintInfo:printInfo]; if (modality == Qt::ApplicationModal || !q->parentWidget()) { if (modality == Qt::NonModal) qWarning("QPrintDialog is required to be modal on OS X"); diff --git a/src/sql/doc/snippets/code/src_sql_kernel_qsqldriver.cpp b/src/sql/doc/snippets/code/src_sql_kernel_qsqldriver.cpp index 55b17c05eb..d0b825ac21 100644 --- a/src/sql/doc/snippets/code/src_sql_kernel_qsqldriver.cpp +++ b/src/sql/doc/snippets/code/src_sql_kernel_qsqldriver.cpp @@ -52,12 +52,12 @@ if (v.isValid() && qstrcmp(v.typeName(), "sqlite3*")==0) { //! [1] -if (v.typeName() == "PGconn*") { +if (qstrcmp(v.typeName(), "PGconn*")) { PGconn *handle = *static_cast(v.data()); if (handle != 0) ... } -if (v.typeName() == "MYSQL*") { +if (qstrcmp(v.typeName(), "MYSQL*")) { MYSQL *handle = *static_cast(v.data()); if (handle != 0) ... } diff --git a/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp b/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp index 4105a43735..20ef7b5a76 100644 --- a/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp +++ b/tests/auto/corelib/io/qfilesystemwatcher/tst_qfilesystemwatcher.cpp @@ -79,6 +79,8 @@ private slots: void QTBUG2331(); void QTBUG2331_data() { basicTest_data(); } + void signalsEmittedAfterFileMoved(); + private: QString m_tempDirPattern; }; @@ -596,5 +598,84 @@ void tst_QFileSystemWatcher::QTBUG2331() QCOMPARE(watcher.directories(), QStringList()); } +class SignalReceiver : public QObject +{ + Q_OBJECT +public: + SignalReceiver(const QDir &moveSrcDir, + const QString &moveDestination, + QFileSystemWatcher *watcher, + QObject *parent = 0) + : QObject(parent), + added(false), + moveSrcDir(moveSrcDir), + moveDestination(QDir(moveDestination)), + watcher(watcher) + {} + +public slots: + void fileChanged(const QString &path) + { + QFileInfo finfo(path); + + QCOMPARE(finfo.absolutePath(), moveSrcDir.absolutePath()); + + if (!added) { + foreach (const QFileInfo &fi, moveDestination.entryInfoList(QDir::Files | QDir::NoSymLinks)) + watcher->addPath(fi.absoluteFilePath()); + added = true; + } + } + +private: + bool added; + QDir moveSrcDir; + QDir moveDestination; + QFileSystemWatcher *watcher; +}; + +// regression test for QTBUG-33211. +// using inotify backend if a file is moved and then added to the watcher +// before all the fileChanged signals are emitted the remaining signals are +// emitted with the destination path instead of the starting path +void tst_QFileSystemWatcher::signalsEmittedAfterFileMoved() +{ + QTemporaryDir temporaryDirectory(m_tempDirPattern); + QVERIFY(temporaryDirectory.isValid()); + QDir testDir(temporaryDirectory.path()); + QVERIFY(testDir.mkdir("movehere")); + QString movePath = testDir.filePath("movehere"); + + for (int i = 0; i < 10; i++) { + QFile f(testDir.filePath(QString("test%1.txt").arg(i))); + QVERIFY(f.open(QIODevice::WriteOnly)); + f.write(QByteArray("i am " + i)); + f.close(); + } + + QFileSystemWatcher watcher; + QVERIFY(watcher.addPath(testDir.path())); + QVERIFY(watcher.addPath(movePath)); + + // add files to watcher + QFileInfoList files = testDir.entryInfoList(QDir::Files | QDir::NoSymLinks); + foreach (const QFileInfo &finfo, files) + QVERIFY(watcher.addPath(finfo.absoluteFilePath())); + + // create the signal receiver + SignalReceiver signalReceiver(testDir, movePath, &watcher); + connect(&watcher, SIGNAL(fileChanged(QString)), &signalReceiver, SLOT(fileChanged(QString))); + + // watch signals + QSignalSpy changedSpy(&watcher, SIGNAL(fileChanged(QString))); + QVERIFY(changedSpy.isValid()); + + // move files to second directory + foreach (const QFileInfo &finfo, files) + QVERIFY(testDir.rename(finfo.fileName(), QString("movehere/%2").arg(finfo.fileName()))); + + QTRY_COMPARE(changedSpy.count(), 10); +} + QTEST_MAIN(tst_QFileSystemWatcher) #include "tst_qfilesystemwatcher.moc" diff --git a/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp index 6c56d3c2fa..90137079f3 100644 --- a/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp +++ b/tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp @@ -1587,6 +1587,8 @@ void tst_QTextLayout::testTabDPIScale() case QPaintDevice::PdmPhysicalDpiX: case QPaintDevice::PdmPhysicalDpiY: return 72; + case QPaintDevice::PdmDevicePixelRatio: + ; // fall through } return 0; } diff --git a/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp index c3faa93309..0425db3098 100644 --- a/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp +++ b/tests/auto/widgets/dialogs/qmessagebox/tst_qmessagebox.cpp @@ -114,6 +114,7 @@ private slots: void about(); void detailsText(); void detailsButtonText(); + void expandDetails_QTBUG_32473(); #ifndef Q_OS_MAC void shortcut(); @@ -137,6 +138,19 @@ private: QTimer keySendTimer; }; +class tst_ResizingMessageBox : public QMessageBox +{ +public: + tst_ResizingMessageBox() : QMessageBox(), resized(false) { } + bool resized; + +protected: + void resizeEvent ( QResizeEvent * event ) { + resized = true; + QMessageBox::resizeEvent(event); + } +}; + tst_QMessageBox::tst_QMessageBox() : keyToSend(-1) { } @@ -603,6 +617,37 @@ void tst_QMessageBox::detailsButtonText() } } +void tst_QMessageBox::expandDetails_QTBUG_32473() +{ + tst_ResizingMessageBox box; + box.setDetailedText("bla"); + box.show(); + QApplication::postEvent(&box, new QEvent(QEvent::LanguageChange)); + QApplication::processEvents(); + QDialogButtonBox* bb = box.findChild("qt_msgbox_buttonbox"); + QVERIFY(bb); + + QList list = bb->buttons(); + QAbstractButton* moreButton = NULL; + foreach (QAbstractButton* btn, list) + if (btn && bb->buttonRole(btn) == QDialogButtonBox::ActionRole) + moreButton = btn; + QVERIFY(moreButton); + QVERIFY(QTest::qWaitForWindowExposed(&box)); + QRect geom = box.geometry(); + box.resized = false; + moreButton->click(); + QTRY_VERIFY(box.resized); + // After we receive the expose event for a second widget, it's likely + // that the window manager is also done manipulating the first QMessageBox. + QWidget fleece; + fleece.show(); + QTest::qWaitForWindowExposed(&fleece); + if (geom.topLeft() == box.geometry().topLeft()) + QTest::qWait(500); + QCOMPARE(geom.topLeft(), box.geometry().topLeft()); +} + void tst_QMessageBox::incorrectDefaultButton() { keyToSend = Qt::Key_Escape;