From 3ae0164687186b68b530fe8d30f5a3434163c1ee Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 5 Mar 2012 10:52:34 +0200 Subject: Update QTouchEvent docs with regards to raw positions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I2c955e42605c442793095d5ca27c34d7d87e08fb Reviewed-by: Samuel Rødal --- src/gui/kernel/qevent.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 60e754b289..52cb799b39 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3863,11 +3863,16 @@ QTouchEvent::TouchPoint::InfoFlags QTouchEvent::TouchPoint::flags() const } /*! - Returns the raw, unfiltered positions for the touch point. The positions are in screen coordinates. + Returns the raw, unfiltered positions for the touch point. The positions are in native screen coordinates. To get local coordinates you can use mapFromGlobal() of the QWindow returned by QTouchEvent::window(). \note Returns an empty list if the touch device's capabilities do not include QTouchDevice::RawPositions. + \note Native screen coordinates refer to the native orientation of the screen which, in case of + mobile devices, is typically portrait. This means that on systems capable of screen orientation + changes the positions in this list will not reflect the current orientation (unlike pos(), + screenPos(), etc.) and will always be reported in the native orientation. + \sa QTouchDevice::capabilities(), device(), window() */ QList QTouchEvent::TouchPoint::rawScreenPositions() const -- cgit v1.2.3 From e8b46d6f69c80e1e041403b1d45d548b2c3e5add Mon Sep 17 00:00:00 2001 From: Leonard Lee Date: Mon, 5 Mar 2012 12:01:45 +0100 Subject: Enable name of threads in release mode. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Naming threads is very useful for release builds. Enabling only on Linux/Mac for now. The Windows port is using debugger specific API for setting thread names, so it has to remain debug mode only. Change-Id: I179521f65f215ff038e8230f958f6aa728ea4cbe Reviewed-by: Lars Knoll Reviewed-by: João Abecasis --- src/corelib/thread/qthread_unix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index d458ee9472..a0913e5dbc 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -287,7 +287,7 @@ void *QThreadPrivate::start(void *arg) else createEventDispatcher(data); -#if !defined(QT_NO_DEBUG) && (defined(Q_OS_LINUX) || defined(Q_OS_MAC)) +#if (defined(Q_OS_LINUX) || defined(Q_OS_MAC)) // sets the name of the current thread. QByteArray objectName = thr->objectName().toLocal8Bit(); -- cgit v1.2.3 From 79e64ede698bdfa1b21314c445db616db246229b Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 1 Mar 2012 13:18:53 +0100 Subject: Remove #ifdef Q_WS_MAC code from QPrinter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have a QPlatformPrinterSupportPlugin on Mac OS X to return the QPrintEngine/QPaintEngine, no need to keep this code. Change-Id: Ie24dcfd157810ede69790fc7b27c12e24766efce Reviewed-by: Morten Johan Sørvig --- src/printsupport/kernel/qprinter.cpp | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src') diff --git a/src/printsupport/kernel/qprinter.cpp b/src/printsupport/kernel/qprinter.cpp index 447c535420..f56d34975d 100644 --- a/src/printsupport/kernel/qprinter.cpp +++ b/src/printsupport/kernel/qprinter.cpp @@ -58,8 +58,6 @@ #if defined (Q_WS_WIN) #include -#elif defined (Q_WS_MAC) -#include #elif defined (QTOPIA_PRINTENGINE) #include #endif @@ -176,11 +174,6 @@ void QPrinterPrivate::createDefaultEngines() paintEngine = pdfEngine; printEngine = pdfEngine; } -#if defined (Q_WS_MAC) - QMacPrintEngine *macEngine = new QMacPrintEngine(printerMode); - paintEngine = macEngine; - printEngine = macEngine; -#endif } break; case QPrinter::PdfFormat: { -- cgit v1.2.3 From 0cb58c7c6d0143b3ebb7527fccf74c750b9e32be Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 1 Mar 2012 13:19:15 +0100 Subject: Replace Q_WS_MAC with Q_OS_MAC in QtPrintSupport Change-Id: Ib19c87a72f74e28412a6060a83bf17d1d16a83ac Reviewed-by: John Layt --- src/printsupport/dialogs/qprintpreviewdialog.cpp | 6 +++--- src/printsupport/widgets/qprintpreviewwidget.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/printsupport/dialogs/qprintpreviewdialog.cpp b/src/printsupport/dialogs/qprintpreviewdialog.cpp index c7b450786d..ce2362b4fe 100644 --- a/src/printsupport/dialogs/qprintpreviewdialog.cpp +++ b/src/printsupport/dialogs/qprintpreviewdialog.cpp @@ -271,7 +271,7 @@ void QPrintPreviewDialogPrivate::init(QPrinter *_printer) QWidget *pageEdit = new QWidget(toolbar); QVBoxLayout *vboxLayout = new QVBoxLayout; vboxLayout->setContentsMargins(0, 0, 0, 0); -#ifdef Q_WS_MAC +#ifdef Q_OS_MAC // We query the widgets about their size and then we fix the size. // This should do the trick for the laying out part... QSize pageNumEditSize, pageNumLabelSize; @@ -281,7 +281,7 @@ void QPrintPreviewDialogPrivate::init(QPrinter *_printer) pageNumLabel->resize(pageNumLabelSize); #endif QFormLayout *formLayout = new QFormLayout; -#ifdef Q_WS_MAC +#ifdef Q_OS_MAC // We have to change the growth policy in Mac. formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); #endif @@ -560,7 +560,7 @@ void QPrintPreviewDialogPrivate::_q_print() { Q_Q(QPrintPreviewDialog); -#if defined(Q_OS_WIN) || defined(Q_WS_MAC) +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) if (printer->outputFormat() != QPrinter::NativeFormat) { QString title; QString suffix; diff --git a/src/printsupport/widgets/qprintpreviewwidget.cpp b/src/printsupport/widgets/qprintpreviewwidget.cpp index 16aea238b1..abed05292e 100644 --- a/src/printsupport/widgets/qprintpreviewwidget.cpp +++ b/src/printsupport/widgets/qprintpreviewwidget.cpp @@ -152,7 +152,7 @@ public: GraphicsView(QWidget* parent = 0) : QGraphicsView(parent) { -#ifdef Q_WS_MAC +#ifdef Q_OS_MAC setFrameStyle(QFrame::NoFrame); #endif } -- cgit v1.2.3 From e1ec8727ea94153ebdf2b06184de20901f1facb9 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 1 Mar 2012 08:22:57 +0100 Subject: Add API to convert QSizeF<->PaperSize in QPlatformPrinterSupport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These static public functions give printsupport plugins access to the non-exported qt_paperSizeToQSizeF() and qSizeFTopaperSize() functions in qprinter.cpp to aid implementing QPrintEngine and QPlatformPrinterSupport::supportedPaperSizes(). Change-Id: I3ebcdcd17e863b06ceb135e096e630b37882a293 Reviewed-by: Morten Johan Sørvig --- .../kernel/qplatformprintersupport_qpa.cpp | 20 ++++++++++++++++++++ .../kernel/qplatformprintersupport_qpa.h | 3 +++ 2 files changed, 23 insertions(+) (limited to 'src') diff --git a/src/printsupport/kernel/qplatformprintersupport_qpa.cpp b/src/printsupport/kernel/qplatformprintersupport_qpa.cpp index aeb4599955..b6f65ee893 100644 --- a/src/printsupport/kernel/qplatformprintersupport_qpa.cpp +++ b/src/printsupport/kernel/qplatformprintersupport_qpa.cpp @@ -124,6 +124,26 @@ void QPlatformPrinterSupport::setPrinterInfoCupsPrinterIndex(QPrinterInfo *p, in #endif } +/* + Converts QSizeF in millimeters to a predefined PaperSize (returns Custom if + the size isn't a standard size) +*/ +QPrinter::PaperSize QPlatformPrinterSupport::convertQSizeFToPaperSize(const QSizeF &sizef) +{ + extern QPrinter::PaperSize qSizeFTopaperSize(const QSizeF &); + return qSizeFTopaperSize(sizef); +} + +/* + Converts a predefined PaperSize to a QSizeF in millimeters (returns + QSizeF(0.0, 0.0) if PaperSize is Custom) +*/ +QSizeF QPlatformPrinterSupport::convertPaperSizeToQSizeF(QPrinter::PaperSize paperSize) +{ + extern QSizeF qt_paperSizeToQSizeF(QPrinter::PaperSize size); + return qt_paperSizeToQSizeF(paperSize); +} + QT_END_NAMESPACE #endif // QT_NO_PRINTER diff --git a/src/printsupport/kernel/qplatformprintersupport_qpa.h b/src/printsupport/kernel/qplatformprintersupport_qpa.h index 53f5900cce..5dba56579c 100644 --- a/src/printsupport/kernel/qplatformprintersupport_qpa.h +++ b/src/printsupport/kernel/qplatformprintersupport_qpa.h @@ -66,6 +66,9 @@ public: virtual QList availablePrinters(); virtual QPrinterInfo defaultPrinter(); + static QPrinter::PaperSize convertQSizeFToPaperSize(const QSizeF &sizef); + static QSizeF convertPaperSizeToQSizeF(QPrinter::PaperSize paperSize); + protected: static QPrinterInfo printerInfo(const QString &printerName, bool isDefault = false); static void setPrinterInfoDefault(QPrinterInfo *p, bool isDefault); -- cgit v1.2.3 From b7ba68515023995e998039d92780dded8bbfd325 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 2 Mar 2012 11:10:23 +0100 Subject: Don't leak from QPlatformPrinterSupportPlugin::get() Cache the first QPlatformPrinterSupport returned from the first QPlatformPrinterSupportPlugin, and treat it as an persistent singelton. Change-Id: Ic1c83d7c1cdf4a09723a74e0b9fd485e0b0b3acb Reviewed-by: Lars Knoll --- src/printsupport/kernel/qplatformprintplugin.cpp | 26 +++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/printsupport/kernel/qplatformprintplugin.cpp b/src/printsupport/kernel/qplatformprintplugin.cpp index 409de3eb4a..2c87fcc5e6 100644 --- a/src/printsupport/kernel/qplatformprintplugin.cpp +++ b/src/printsupport/kernel/qplatformprintplugin.cpp @@ -58,15 +58,27 @@ QPlatformPrinterSupportPlugin::~QPlatformPrinterSupportPlugin() { } +/*! + \internal + + Returns a lazily-initialized singleton. Ownership is granted to the + QPlatformPrinterSupportPlugin, which is never unloaded or destroyed until + application exit, i.e. you can expect this pointer to always be valid and + multiple calls to this function will always return the same pointer. +*/ QPlatformPrinterSupport *QPlatformPrinterSupportPlugin::get() { - QStringList k = loader()->keys(); - if (k.isEmpty()) - return 0; - QPlatformPrinterSupportPlugin *plugin = qobject_cast(loader()->instance(k.first())); - if (!plugin) - return 0; - return plugin->create(k.first()); + static QPlatformPrinterSupport *singleton = 0; + if (!singleton) { + QStringList k = loader()->keys(); + if (k.isEmpty()) + return 0; + QPlatformPrinterSupportPlugin *plugin = qobject_cast(loader()->instance(k.first())); + if (!plugin) + return 0; + singleton = plugin->create(k.first()); + } + return singleton; } QT_END_NAMESPACE -- cgit v1.2.3 From f449cefc27fc321f96aadbcb0f0a46e6f7a2b0b4 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Sun, 12 Feb 2012 12:35:12 +0100 Subject: Remove AccessibilityPrepare event. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This event was completely unused. In addition it leads to crashes on linux when sending the Destroy accessibility update. The Destroy event on linux would still query an accessible interface. That in turn would trigger the event to be sent. Change-Id: I8915527de067b8b70ba41b1361e3ef5d12866d7d Reviewed-by: Frederik Gladhorn Reviewed-by: Jan-Arve Sæther --- src/corelib/kernel/qcoreevent.h | 1 - src/gui/accessible/qaccessible.cpp | 4 ---- src/widgets/kernel/qapplication.cpp | 1 - src/widgets/statemachine/qguistatemachine.cpp | 2 -- 4 files changed, 8 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index cac89f2b13..1d54b32dfa 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -127,7 +127,6 @@ public: DeactivateControl = 81, // ActiveX deactivation ContextMenu = 82, // context popup menu InputMethod = 83, // input method - AccessibilityPrepare = 86, // accessibility information is requested TabletMove = 87, // Wacom tablet event LocaleChange = 88, // the system locale changed LanguageChange = 89, // the application language changed diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp index aa47616161..e5b290ae1f 100644 --- a/src/gui/accessible/qaccessible.cpp +++ b/src/gui/accessible/qaccessible.cpp @@ -571,10 +571,6 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object) if (!object) return 0; - QEvent e(QEvent::AccessibilityPrepare); - - QCoreApplication::sendEvent(object, &e); - const QMetaObject *mo = object->metaObject(); while (mo) { const QLatin1String cn(mo->className()); diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 095b58eeaf..53f4942310 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -3124,7 +3124,6 @@ bool QApplication::notify(QObject *receiver, QEvent *e) case QEvent::ChildRemoved: case QEvent::UpdateRequest: case QEvent::UpdateLater: - case QEvent::AccessibilityPrepare: case QEvent::LocaleChange: case QEvent::Style: case QEvent::IconDrag: diff --git a/src/widgets/statemachine/qguistatemachine.cpp b/src/widgets/statemachine/qguistatemachine.cpp index 465da1e2ba..7c8de05466 100644 --- a/src/widgets/statemachine/qguistatemachine.cpp +++ b/src/widgets/statemachine/qguistatemachine.cpp @@ -181,8 +181,6 @@ static QEvent *cloneEvent(QEvent *e) #endif case QEvent::InputMethod: return new QInputMethodEvent(*static_cast(e)); - case QEvent::AccessibilityPrepare: - return new QEvent(*e); case QEvent::LocaleChange: return new QEvent(*e); case QEvent::LanguageChange: -- cgit v1.2.3 From 50ca45f0595242937908854cd37428c397b9cf60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Mon, 5 Mar 2012 11:04:48 +0100 Subject: Worked around Metacity crashes in xcb plugin. Setting the user time before mapping the window seems to prevent the crasher from happening. We used to set the user time before mapping in Qt 4.8 too, so it's probably the right thing to do. Task-number: QTBUG-24462 Change-Id: Ia670b799bd1ed7a7e6399631d5242e57324918b3 Reviewed-by: Jan Arne Petersen Reviewed-by: Lars Knoll --- src/plugins/platforms/xcb/qxcbwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 739426a92a..542d7ab69f 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -548,6 +548,8 @@ void QXcbWindow::show() updateNetWmStateBeforeMap(); } + updateNetWmUserTime(connection()->time()); + Q_XCB_CALL(xcb_map_window(xcb_connection(), m_window)); xcb_flush(xcb_connection()); -- cgit v1.2.3 From 50f6cf6e4852ff39a79bde074204b498ee9ce21d Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Tue, 14 Feb 2012 16:51:20 +0100 Subject: QSqlTableModel: remove unnecessary parameter from setSubmitted() Change-Id: I6d23788163ffd6ba7a8f01ed40910d861ff92703 Reviewed-by: Honglei Zhang --- src/sql/models/qsqltablemodel.cpp | 2 +- src/sql/models/qsqltablemodel_p.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 571c28f515..075e32ff6e 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -684,7 +684,7 @@ bool QSqlTableModel::submitAll() Q_ASSERT_X(false, "QSqlTableModel::submitAll()", "Invalid cache operation"); break; } - it.value().setSubmitted(true); + it.value().setSubmitted(); } return select(); diff --git a/src/sql/models/qsqltablemodel_p.h b/src/sql/models/qsqltablemodel_p.h index 0ae6b53742..168b689441 100644 --- a/src/sql/models/qsqltablemodel_p.h +++ b/src/sql/models/qsqltablemodel_p.h @@ -115,7 +115,7 @@ public: m_rec.setGenerated(c, true); } inline bool submitted() const { return m_submitted; } - inline void setSubmitted(bool b) { m_submitted = b; } + inline void setSubmitted() { m_submitted = true; } private: Op m_op; QSqlRecord m_rec; -- cgit v1.2.3 From 463bd32fe5120d3954d93b6792050ac47796467f Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Tue, 14 Feb 2012 22:50:40 +0100 Subject: QSqlTableModel: mirror database values in change cache Between submitting and the next select, these values will be more up-to-date than those that could be obtained from the query. This will be useful for constructing primary values and reverting changes made after submitting. Change-Id: I8317617f3e7043ad0b79b333731c55fb88aef171 Reviewed-by: Honglei Zhang --- src/sql/models/qsqltablemodel.cpp | 6 +++--- src/sql/models/qsqltablemodel_p.h | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 075e32ff6e..d31e4bc335 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -510,7 +510,7 @@ bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, in if (row.op() == QSqlTableModelPrivate::None) row = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Update, - d->rec); + record(index.row())); row.setValue(index.column(), value); emit dataChanged(index, index); @@ -1004,7 +1004,7 @@ bool QSqlTableModel::removeRows(int row, int count, const QModelIndex &parent) // so fake this by adjusting row --row; } else { - d->cache[idx] = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Delete); + d->cache[idx] = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Delete, record(idx)); if (d->strategy == OnManualSubmit) emit headerDataChanged(Qt::Vertical, idx, idx); } @@ -1235,7 +1235,7 @@ bool QSqlTableModel::setRecord(int row, const QSqlRecord &values) QSqlTableModelPrivate::ModifiedRow &mrow = d->cache[row]; if (mrow.op() == QSqlTableModelPrivate::None) mrow = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Update, - d->rec); + record(row)); Map::const_iterator i = map.constBegin(); const Map::const_iterator e = map.constEnd(); diff --git a/src/sql/models/qsqltablemodel_p.h b/src/sql/models/qsqltablemodel_p.h index 168b689441..7266810c40 100644 --- a/src/sql/models/qsqltablemodel_p.h +++ b/src/sql/models/qsqltablemodel_p.h @@ -102,10 +102,7 @@ public: public: inline ModifiedRow(Op o = None, const QSqlRecord &r = QSqlRecord()) : m_op(o), m_rec(r), m_submitted(false) - { - for (int i = m_rec.count() - 1; i >= 0; --i) - m_rec.setGenerated(i, false); - } + { init_rec(); } inline Op op() const { return m_op; } inline QSqlRecord rec() const { return m_rec; } inline QSqlRecord& recRef() { return m_rec; } @@ -117,8 +114,15 @@ public: inline bool submitted() const { return m_submitted; } inline void setSubmitted() { m_submitted = true; } private: + void init_rec() + { + for (int i = m_rec.count() - 1; i >= 0; --i) + m_rec.setGenerated(i, false); + m_db_values = m_rec; + } Op m_op; QSqlRecord m_rec; + QSqlRecord m_db_values; bool m_submitted; }; -- cgit v1.2.3 From 698e620aad68d702c32b62e8114fe12a7b3e2ca5 Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Tue, 14 Feb 2012 23:45:44 +0100 Subject: QSqlTableModel: derive primary values from database values Primary values are used to map a row in the model to a row in the database table. It is critically important between submitting a change and the following select (which refreshes the query) to have updated primary values. Otherwise, if the change affected the primary values, additional changes before select will misbehave. Change-Id: I5d08dd70ac5d3f06cd9d3186a439f4c80a037c2d Reviewed-by: Yunqiao Yin --- src/sql/models/qsqltablemodel.cpp | 11 +---------- src/sql/models/qsqltablemodel_p.h | 12 ++++++++++++ 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index d31e4bc335..a2999b30c4 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -201,16 +201,7 @@ bool QSqlTableModelPrivate::exec(const QString &stmt, bool prepStatement, QSqlRecord QSqlTableModelPrivate::primaryValues(int row) const { - Q_Q(const QSqlTableModel); - if (cache.value(row).op() == Insert) - return QSqlRecord(); - - QSqlRecord values(primaryIndex.isEmpty() ? rec : primaryIndex); - - for (int i = 0; i < values.count(); ++i) - values.setValue(i, q->QSqlQueryModel::data(createIndex(row, rec.indexOf(values.fieldName(i))), Qt::EditRole)); - - return values; + return cache.value(row).primaryValues(primaryIndex.isEmpty() ? rec : primaryIndex); } /*! diff --git a/src/sql/models/qsqltablemodel_p.h b/src/sql/models/qsqltablemodel_p.h index 7266810c40..57051a0e9c 100644 --- a/src/sql/models/qsqltablemodel_p.h +++ b/src/sql/models/qsqltablemodel_p.h @@ -113,6 +113,18 @@ public: } inline bool submitted() const { return m_submitted; } inline void setSubmitted() { m_submitted = true; } + inline QSqlRecord primaryValues(const QSqlRecord& pi) const + { + if (m_op == None || m_op == Insert) + return QSqlRecord(); + + QSqlRecord values(pi); + + for (int i = values.count() - 1; i >= 0; --i) + values.setValue(i, m_db_values.value(values.fieldName(i))); + + return values; + } private: void init_rec() { -- cgit v1.2.3 From ea6d1fde1b5ce53691cc9b9154bd992a16be035f Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Wed, 15 Feb 2012 02:07:59 +0100 Subject: QSqlTableModel::data(): use cached values when available Simplify logic. If the record is in the cache, even untouched values should be there. This is also necessary for getting the most up-to-date values between submitting and the next select. Change-Id: I8578d96229797ce9fb0d07fe456301358f2be071 Reviewed-by: Honglei Zhang --- src/sql/models/qsqltablemodel.cpp | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index a2999b30c4..24668dd946 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -402,29 +402,9 @@ QVariant QSqlTableModel::data(const QModelIndex &index, int role) const if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::EditRole)) return QVariant(); - if (d->cache.contains(index.row())) { - const QSqlTableModelPrivate::ModifiedRow row = d->cache.value(index.row()); - - switch (d->strategy) { - case OnFieldChange: - case OnRowChange: - if (row.op() == QSqlTableModelPrivate::Insert) { - if (index.column() < 0 || index.column() >= row.rec().count()) - return QVariant(); - return row.rec().value(index.column()); - } else if (row.op() == QSqlTableModelPrivate::Update) { - if (row.rec().isGenerated(index.column())) - return row.rec().value(index.column()); - } - break; - case OnManualSubmit: - if (row.op() == QSqlTableModelPrivate::Insert - || (row.op() != QSqlTableModelPrivate::None - && row.rec().isGenerated(index.column()))) - return row.rec().value(index.column()); - break; - } - } + const QSqlTableModelPrivate::ModifiedRow mrow = d->cache.value(index.row()); + if (mrow.op() != QSqlTableModelPrivate::None) + return mrow.rec().value(index.column()); return QSqlQueryModel::data(index, role); } -- cgit v1.2.3 From eb150a51cd1e439fd719fe3cd3f30c53f9a1348a Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Mon, 5 Mar 2012 22:11:05 +0100 Subject: Remove unused QThreadPoolPrivate::startFrontRunnable(). Change-Id: Ie079aea3412a53cf9dccaa770fa64ff5b6b7b3b1 Reviewed-by: Stephen Kelly --- src/corelib/thread/qthreadpool.cpp | 25 ------------------------- src/corelib/thread/qthreadpool_p.h | 1 - 2 files changed, 26 deletions(-) (limited to 'src') diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp index af8c99197e..b7021817c5 100644 --- a/src/corelib/thread/qthreadpool.cpp +++ b/src/corelib/thread/qthreadpool.cpp @@ -307,31 +307,6 @@ bool QThreadPoolPrivate::waitForDone(int msecs) return queue.isEmpty() && activeThreads == 0; } -/*! \internal - Pulls a runnable from the front queue and runs it in the current thread. Blocks - until the runnable has completed. Returns true if a runnable was found. -*/ -bool QThreadPoolPrivate::startFrontRunnable() -{ - QMutexLocker locker(&mutex); - if (queue.isEmpty()) - return false; - - QRunnable *runnable = queue.takeFirst().first; - const bool autoDelete = runnable->autoDelete(); - bool del = autoDelete && !--runnable->ref; - - locker.unlock(); - runnable->run(); - locker.relock(); - - if (del) { - delete runnable; - } - - return true; -} - /*! \internal Seaches for \a runnable in the queue, removes it from the queue and runs it if found. This functon does not return until the runnable diff --git a/src/corelib/thread/qthreadpool_p.h b/src/corelib/thread/qthreadpool_p.h index 9a7c09695f..910e0b0714 100644 --- a/src/corelib/thread/qthreadpool_p.h +++ b/src/corelib/thread/qthreadpool_p.h @@ -83,7 +83,6 @@ public: void startThread(QRunnable *runnable = 0); void reset(); bool waitForDone(int msecs = -1); - bool startFrontRunnable(); void stealRunnable(QRunnable *); mutable QMutex mutex; -- cgit v1.2.3 From 95550c8f1292c93ea42d59283394fc4f4d63bf2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Mon, 5 Mar 2012 15:27:40 +0100 Subject: Mark QMetaType constructor as explicit. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implicit conversion from an int would look strange in this case. Change-Id: I2222a045c293595d7b83a2fb75ca646f5cf79bca Reviewed-by: Stephen Kelly Reviewed-by: João Abecasis --- src/corelib/kernel/qmetatype.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index f969875455..7f6eaf2230 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -255,7 +255,7 @@ public: static bool load(QDataStream &stream, int type, void *data); #endif - QMetaType(const int type); + explicit QMetaType(const int type); inline ~QMetaType(); inline bool isValid() const; -- cgit v1.2.3 From 9c75d0a91327868b2131d11caea2cd0ccd437711 Mon Sep 17 00:00:00 2001 From: Alex Wilson Date: Mon, 5 Mar 2012 18:16:58 +1000 Subject: Support new-style plugins without a "Keys" json property As per discussion with Lars, intent here was to allow plugins without a "Keys" property to still function correctly, but this particular if statement was blocking any such plugins from being detected. Change-Id: Icb343ca8bd95a508d62565cd816fe2a57a4f82bd Reviewed-by: Lars Knoll --- src/corelib/plugin/qfactoryloader.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index 566ece77c9..cdc72cf35d 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -128,6 +128,7 @@ void QFactoryLoader::update() QLibraryPrivate *library = 0; for (int j = 0; j < plugins.count(); ++j) { QString fileName = QDir::cleanPath(path + QLatin1Char('/') + plugins.at(j)); + if (qt_debug_component()) { qDebug() << "QFactoryLoader::QFactoryLoader() looking at" << fileName; } @@ -140,7 +141,9 @@ void QFactoryLoader::update() library->release(); continue; } + QStringList keys; + bool metaDataOk = false; if (library->compatPlugin) { qWarning("Qt plugin loader: Compatibility plugin '%s', need to load for accessing meta data.", qPrintable(QDir::toNativeSeparators(fileName))); @@ -164,10 +167,17 @@ void QFactoryLoader::update() QFactoryInterface *factory = qobject_cast(instance); if (instance && factory && instance->qt_metacast(d->iid)) keys = factory->keys(); + + if (!keys.isEmpty()) + metaDataOk = true; + } else { QString iid = library->metaData.value(QLatin1String("IID")).toString(); if (iid == QLatin1String(d->iid.constData(), d->iid.size())) { QJsonObject object = library->metaData.value(QLatin1String("MetaData")).toObject(); + if (!object.isEmpty()) + metaDataOk = true; + QJsonArray k = object.value(QLatin1String("Keys")).toArray(); for (int i = 0; i < k.size(); ++i) { QString s = k.at(i).toString(); @@ -178,11 +188,12 @@ void QFactoryLoader::update() qDebug() << "Got keys from plugin meta data" << keys; } - if (keys.isEmpty()) { + if (!metaDataOk) { library->unload(); library->release(); continue; } + d->libraryList += library; for (int k = 0; k < keys.count(); ++k) { // first come first serve, unless the first -- cgit v1.2.3 From ff25691d00d634068c6389f8f1607d7cc95ac5be Mon Sep 17 00:00:00 2001 From: Martin Petersson Date: Thu, 1 Mar 2012 10:36:38 +0100 Subject: QNam: only init channels when needed. Each channel will create a socket that will allocate memory for the read and write buffers. QNam generaly inits 6 sockets for each connection. That means that by default 12 such buffers are created. This will instead initialize channels when they are needed. Change-Id: Ie3f2cf789e084fd3d17d3b2a9bb3d3a4370b3da4 Reviewed-by: Shane Kearns --- src/network/access/qhttpnetworkconnection.cpp | 106 ++++++++++++--------- .../access/qhttpnetworkconnectionchannel.cpp | 67 ++++++++++++- .../access/qhttpnetworkconnectionchannel_p.h | 11 +++ 3 files changed, 138 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 6aa3a5a5f4..890072eb7e 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -123,8 +123,8 @@ void QHttpNetworkConnectionPrivate::init() //push session down to channels channels[i].networkSession = networkSession; #endif - channels[i].init(); } + delayedConnectionTimer.setSingleShot(true); QObject::connect(&delayedConnectionTimer, SIGNAL(timeout()), q, SLOT(_q_connectDelayedChannel())); } @@ -135,12 +135,14 @@ void QHttpNetworkConnectionPrivate::pauseConnection() // Disable all socket notifiers for (int i = 0; i < channelCount; i++) { + if (channels[i].socket) { #ifndef QT_NO_SSL - if (encrypt) - QSslSocketPrivate::pauseSocketNotifiers(static_cast(channels[i].socket)); - else + if (encrypt) + QSslSocketPrivate::pauseSocketNotifiers(static_cast(channels[i].socket)); + else #endif - QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket); + QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket); + } } } @@ -149,16 +151,18 @@ void QHttpNetworkConnectionPrivate::resumeConnection() state = RunningState; // Enable all socket notifiers for (int i = 0; i < channelCount; i++) { + if (channels[i].socket) { #ifndef QT_NO_SSL - if (encrypt) - QSslSocketPrivate::resumeSocketNotifiers(static_cast(channels[i].socket)); - else + if (encrypt) + QSslSocketPrivate::resumeSocketNotifiers(static_cast(channels[i].socket)); + else #endif - QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket); + QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket); - // Resume pending upload if needed - if (channels[i].state == QHttpNetworkConnectionChannel::WritingState) - QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection); + // Resume pending upload if needed + if (channels[i].state == QHttpNetworkConnectionChannel::WritingState) + QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection); + } } // queue _q_startNextRequest @@ -346,11 +350,15 @@ void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket, QNetworkReply::NetworkError errorCode) { Q_Q(QHttpNetworkConnection); - if (socket && reply) { + + int i = 0; + if (socket) + i = indexOf(socket); + + if (reply) { // this error matters only to this reply reply->d_func()->errorString = errorDetail(errorCode, socket); emit reply->finishedWithError(errorCode, reply->d_func()->errorString); - int i = indexOf(socket); // remove the corrupt data if any reply->d_func()->eraseData(); @@ -358,7 +366,8 @@ void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket, channels[i].close(); channels[i].reply = 0; channels[i].request = QHttpNetworkRequest(); - channels[i].requeueCurrentlyPipelinedRequests(); + if (socket) + channels[i].requeueCurrentlyPipelinedRequests(); // send the next request QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); @@ -582,9 +591,9 @@ void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair) bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket) { - Q_ASSERT(socket); - - int i = indexOf(socket); + int i = 0; + if (socket) + i = indexOf(socket); if (!highPriorityQueue.isEmpty()) { // remove from queue before sendRequest! else we might pipeline the same request again @@ -740,15 +749,15 @@ bool QHttpNetworkConnectionPrivate::fillPipeline(QList &queue, } -QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket* socket, - const QString &extraDetail) +QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket, const QString &extraDetail) { - Q_ASSERT(socket); - QString errorString; switch (errorCode) { case QNetworkReply::HostNotFoundError: - errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(socket->peerName()); + if (socket) + errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(socket->peerName()); + else + errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(hostName); break; case QNetworkReply::ConnectionRefusedError: errorString = QCoreApplication::translate("QHttp", "Connection refused"); @@ -891,9 +900,11 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() return; // try to get a free AND connected socket for (int i = 0; i < channelCount; ++i) { - if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) { - if (dequeueRequest(channels[i].socket)) - channels[i].sendRequest(); + if (channels[i].socket) { + if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) { + if (dequeueRequest(channels[i].socket)) + channels[i].sendRequest(); + } } } @@ -908,7 +919,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty()) return; for (int i = 0; i < channelCount; i++) - if (channels[i].socket->state() == QAbstractSocket::ConnectedState) + if (channels[i].socket && channels[i].socket->state() == QAbstractSocket::ConnectedState) fillPipeline(channels[i].socket); // If there is not already any connected channels we need to connect a new one. @@ -916,11 +927,19 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() // connected or not. This is to reuse connected channels before we connect new once. int queuedRequest = highPriorityQueue.count() + lowPriorityQueue.count(); for (int i = 0; i < channelCount; ++i) { - if ((channels[i].socket->state() == QAbstractSocket::ConnectingState) || (channels[i].socket->state() == QAbstractSocket::HostLookupState)) - queuedRequest--; - if ( queuedRequest <=0 ) - break; - if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) { + bool connectChannel = false; + if (channels[i].socket) { + if ((channels[i].socket->state() == QAbstractSocket::ConnectingState) || (channels[i].socket->state() == QAbstractSocket::HostLookupState)) + queuedRequest--; + if ( queuedRequest <=0 ) + break; + if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) + connectChannel = true; + } else { // not previously used channel + connectChannel = true; + } + + if (connectChannel) { if (networkLayerState == IPv4) channels[i].networkLayerPreference = QAbstractSocket::IPv4Protocol; else if (networkLayerState == IPv6) @@ -928,6 +947,9 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest() channels[i].ensureConnection(); queuedRequest--; } + + if ( queuedRequest <=0 ) + break; } } @@ -958,8 +980,8 @@ void QHttpNetworkConnectionPrivate::startHostInfoLookup() #ifndef QT_NO_NETWORKPROXY if (networkProxy.capabilities() & QNetworkProxy::HostNameLookupCapability) { lookupHost = networkProxy.hostName(); - } else if (channels[0].socket->proxy().capabilities() & QNetworkProxy::HostNameLookupCapability) { - lookupHost = channels[0].socket->proxy().hostName(); + } else if (channels[0].proxy.capabilities() & QNetworkProxy::HostNameLookupCapability) { + lookupHost = channels[0].proxy.hostName(); } #endif QHostAddress temp; @@ -1169,7 +1191,7 @@ void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkPro { Q_D(QHttpNetworkConnection); for (int i = 0; i < d->channelCount; ++i) - d->channels[i].socket->setProxy(networkProxy); + d->channels[i].setProxy(networkProxy); } QNetworkProxy QHttpNetworkConnection::transparentProxy() const @@ -1190,7 +1212,7 @@ void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config // set the config on all channels for (int i = 0; i < d->channelCount; ++i) - static_cast(d->channels[i].socket)->setSslConfiguration(config); + d->channels[i].setSslConfiguration(config); } void QHttpNetworkConnection::ignoreSslErrors(int channel) @@ -1201,13 +1223,11 @@ void QHttpNetworkConnection::ignoreSslErrors(int channel) if (channel == -1) { // ignore for all channels for (int i = 0; i < d->channelCount; ++i) { - static_cast(d->channels[i].socket)->ignoreSslErrors(); - d->channels[i].ignoreAllSslErrors = true; + d->channels[i].ignoreSslErrors(); } } else { - static_cast(d->channels[channel].socket)->ignoreSslErrors(); - d->channels[channel].ignoreAllSslErrors = true; + d->channels[channel].ignoreSslErrors(); } } @@ -1219,13 +1239,11 @@ void QHttpNetworkConnection::ignoreSslErrors(const QList &errors, int if (channel == -1) { // ignore for all channels for (int i = 0; i < d->channelCount; ++i) { - static_cast(d->channels[i].socket)->ignoreSslErrors(errors); - d->channels[i].ignoreSslErrorsList = errors; + d->channels[i].ignoreSslErrors(errors); } } else { - static_cast(d->channels[channel].socket)->ignoreSslErrors(errors); - d->channels[channel].ignoreSslErrorsList = errors; + d->channels[channel].ignoreSslErrors(errors); } } diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 3991bffa47..a009222bd5 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -65,6 +65,7 @@ QT_BEGIN_NAMESPACE QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel() : socket(0) , ssl(false) + , isInitialized(false) , state(IdleState) , reply(0) , written(0) @@ -152,19 +153,38 @@ void QHttpNetworkConnectionChannel::init() QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)), this, SLOT(_q_encryptedBytesWritten(qint64)), Qt::DirectConnection); + + if (ignoreAllSslErrors) + sslSocket->ignoreSslErrors(); + + if (!ignoreSslErrorsList.isEmpty()) + sslSocket->ignoreSslErrors(ignoreSslErrorsList); + + if (!sslConfiguration.isNull()) + sslSocket->setSslConfiguration(sslConfiguration); } + #endif + +#ifndef QT_NO_NETWORKPROXY + if (proxy.type() != QNetworkProxy::NoProxy) + socket->setProxy(proxy); +#endif + isInitialized = true; } void QHttpNetworkConnectionChannel::close() { - if (socket->state() == QAbstractSocket::UnconnectedState) + if (!socket) + state = QHttpNetworkConnectionChannel::IdleState; + else if (socket->state() == QAbstractSocket::UnconnectedState) state = QHttpNetworkConnectionChannel::IdleState; else state = QHttpNetworkConnectionChannel::ClosingState; - socket->close(); + if (socket) + socket->close(); } @@ -527,6 +547,9 @@ void QHttpNetworkConnectionChannel::handleUnexpectedEOF() bool QHttpNetworkConnectionChannel::ensureConnection() { + if (!isInitialized) + init(); + QAbstractSocket::SocketState socketState = socket->state(); // resend this request after we receive the disconnected signal @@ -835,6 +858,46 @@ bool QHttpNetworkConnectionChannel::resetUploadData() } } +#ifndef QT_NO_NETWORKPROXY + +void QHttpNetworkConnectionChannel::setProxy(const QNetworkProxy &networkProxy) +{ + if (socket) + socket->setProxy(networkProxy); + + proxy = networkProxy; +} + +#endif + +#ifndef QT_NO_SSL + +void QHttpNetworkConnectionChannel::ignoreSslErrors() +{ + if (socket) + static_cast(socket)->ignoreSslErrors(); + + ignoreAllSslErrors = true; +} + + +void QHttpNetworkConnectionChannel::ignoreSslErrors(const QList &errors) +{ + if (socket) + static_cast(socket)->ignoreSslErrors(errors); + + ignoreSslErrorsList = errors; +} + +void QHttpNetworkConnectionChannel::setSslConfiguration(const QSslConfiguration &config) +{ + if (socket) + static_cast(socket)->setSslConfiguration(config); + + sslConfiguration = config; +} + +#endif void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair) { diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index 7da9b514d6..2648cba2a5 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -72,6 +72,7 @@ #ifndef QT_NO_SSL # include # include +# include #else # include #endif @@ -100,6 +101,7 @@ public: }; QAbstractSocket *socket; bool ssl; + bool isInitialized; ChannelState state; QHttpNetworkRequest request; // current request QHttpNetworkReply *reply; // current reply for this request @@ -118,6 +120,10 @@ public: #ifndef QT_NO_SSL bool ignoreAllSslErrors; QList ignoreSslErrorsList; + QSslConfiguration sslConfiguration; + void ignoreSslErrors(); + void ignoreSslErrors(const QList &errors); + void setSslConfiguration(const QSslConfiguration &config); #endif #ifndef QT_NO_BEARERMANAGEMENT QSharedPointer networkSession; @@ -144,6 +150,11 @@ public: void setConnection(QHttpNetworkConnection *c); QPointer connection; +#ifndef QT_NO_NETWORKPROXY + QNetworkProxy proxy; + void setProxy(const QNetworkProxy &networkProxy); +#endif + void init(); void close(); -- cgit v1.2.3 From 95fa88abe7441f49b64a0b398e16b04b3b6bdbc9 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Wed, 1 Feb 2012 00:33:30 +0200 Subject: Remove codecForTr(). Similarly to change id I2f429fa7ef93bd75bb93a7f64c56db15b7283388, the capability to arbitrarily alter the encoding of literals is very destructive, especially in a world with libraries and plugins. Change-Id: If0d4cd8dcf89792e39c1984cbde6b036cebfc02f Reviewed-by: Thiago Macieira Reviewed-by: Lars Knoll --- src/corelib/codecs/qtextcodec.cpp | 37 --------------------------------- src/corelib/codecs/qtextcodec.h | 7 ------- src/corelib/global/qglobal.cpp | 4 +--- src/corelib/kernel/qcoreapplication.cpp | 14 +++++-------- src/corelib/kernel/qcoreapplication.h | 6 +++--- src/corelib/kernel/qmetaobject.cpp | 2 +- src/corelib/kernel/qobject.cpp | 7 +------ 7 files changed, 11 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp index f6f0cd8699..13f0ec8ce5 100644 --- a/src/corelib/codecs/qtextcodec.cpp +++ b/src/corelib/codecs/qtextcodec.cpp @@ -145,8 +145,6 @@ static bool destroying_is_ok = false; #endif static QTextCodec *localeMapper = 0; -QTextCodec *QTextCodec::cftr = 0; - class QTextCodecCleanup { @@ -1463,41 +1461,6 @@ QString QTextDecoder::toUnicode(const QByteArray &ba) return c->toUnicode(ba.constData(), ba.length(), &state); } - -/*! - \fn QTextCodec* QTextCodec::codecForTr() - - Returns the codec used by QObject::tr() on its argument. If this - function returns 0 (the default), tr() assumes Latin-1. - - \sa setCodecForTr() -*/ - -/*! - \fn void QTextCodec::setCodecForTr(QTextCodec *c) - \nonreentrant - - Sets the codec used by QObject::tr() on its argument to \a c. If - \a c is 0 (the default), tr() assumes Latin-1. - - If the literal quoted text in the program is not in the Latin-1 - encoding, this function can be used to set the appropriate - encoding. For example, software developed by Korean programmers - might use eucKR for all the text in the program, in which case the - main() function might look like this: - - \snippet doc/src/snippets/code/src_corelib_codecs_qtextcodec.cpp 3 - - Note that this is not the way to select the encoding that the \e - user has chosen. For example, to convert an application containing - literal English strings to Korean, all that is needed is for the - English strings to be passed through tr() and for translation - files to be loaded. For details of internationalization, see - \l{Internationalization with Qt}. - - \sa codecForTr() -*/ - /*! \since 4.4 diff --git a/src/corelib/codecs/qtextcodec.h b/src/corelib/codecs/qtextcodec.h index ad37005e92..b4b170f7d7 100644 --- a/src/corelib/codecs/qtextcodec.h +++ b/src/corelib/codecs/qtextcodec.h @@ -72,9 +72,6 @@ public: static QTextCodec* codecForLocale(); static void setCodecForLocale(QTextCodec *c); - static QTextCodec* codecForTr(); - static void setCodecForTr(QTextCodec *c); - static QTextCodec *codecForHtml(const QByteArray &ba); static QTextCodec *codecForHtml(const QByteArray &ba, QTextCodec *defaultCodec); @@ -129,14 +126,10 @@ protected: private: friend class QTextCodecCleanup; - static QTextCodec *cftr; static bool validCodecs(); }; Q_DECLARE_OPERATORS_FOR_FLAGS(QTextCodec::ConversionFlags) - inline QTextCodec* QTextCodec::codecForTr() { return validCodecs() ? cftr : 0; } -inline void QTextCodec::setCodecForTr(QTextCodec *c) { cftr = c; } - class Q_CORE_EXPORT QTextEncoder { Q_DISABLE_COPY(QTextEncoder) public: diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index a30250df81..09d178639d 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -2233,9 +2233,7 @@ int qrand() The macro QT_TR_NOOP_UTF8() is identical except that it tells lupdate that the source string is encoded in UTF-8. Corresponding variants - exist in the QT_TRANSLATE_NOOP() family of macros, too. Note that - using these macros is not required if \c CODECFORTR is already set to - UTF-8 in the qmake project file. + exist in the QT_TRANSLATE_NOOP() family of macros, too. \sa QT_TRANSLATE_NOOP(), {Internationalization with Qt} */ diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 515732bc68..184743e865 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -1438,11 +1438,9 @@ bool QCoreApplication::event(QEvent *e) This enum type defines the 8-bit encoding of character string arguments to translate(): - \value CodecForTr The encoding specified by - QTextCodec::codecForTr() (Latin-1 if none has - been set). - \value UnicodeUTF8 UTF-8. - \value DefaultCodec (Obsolete) Use CodecForTr instead. + \value UnicodeUTF8 UTF-8. + \value Latin1 Latin-1. + \value DefaultCodec Latin-1. \sa QObject::tr(), QObject::trUtf8(), QString::fromUtf8() */ @@ -1617,7 +1615,7 @@ static void replacePercentN(QString *result, int n) If none of the translation files contain a translation for \a sourceText in \a context, this function returns a QString equivalent of \a sourceText. The encoding of \a sourceText is - specified by \e encoding; it defaults to CodecForTr. + specified by \e encoding; it defaults to DefaultCodec. This function is not virtual. You can use alternative translation techniques by subclassing \l QTranslator. @@ -1628,7 +1626,7 @@ static void replacePercentN(QString *result, int n) so will most likely result in crashes or other undesirable behavior. - \sa QObject::tr() installTranslator() QTextCodec::codecForTr() + \sa QObject::tr() installTranslator() */ @@ -1657,8 +1655,6 @@ QString QCoreApplication::translate(const char *context, const char *sourceText, #else if (encoding == UnicodeUTF8) result = QString::fromUtf8(sourceText); - else if (QTextCodec::codecForTr() != 0) - result = QTextCodec::codecForTr()->toUnicode(sourceText); else #endif result = QString::fromLatin1(sourceText); diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h index 18266a9a2c..0a5181a508 100644 --- a/src/corelib/kernel/qcoreapplication.h +++ b/src/corelib/kernel/qcoreapplication.h @@ -137,11 +137,11 @@ public: static void installTranslator(QTranslator * messageFile); static void removeTranslator(QTranslator * messageFile); #endif - enum Encoding { CodecForTr, UnicodeUTF8, DefaultCodec = CodecForTr }; + enum Encoding { UnicodeUTF8, Latin1, DefaultCodec = Latin1 }; static QString translate(const char * context, const char * key, const char * disambiguation = 0, - Encoding encoding = CodecForTr, + Encoding encoding = DefaultCodec, int n = -1); static void flush(); @@ -240,7 +240,7 @@ inline QString QCoreApplication::translate(const char *, const char *sourceText, public: \ static inline QString tr(const char *sourceText, const char *disambiguation = 0, int n = -1) \ { return QCoreApplication::translate(#context, sourceText, disambiguation, \ - QCoreApplication::CodecForTr, n); } \ + QCoreApplication::DefaultCodec, n); } \ static inline QString trUtf8(const char *sourceText, const char *disambiguation = 0, int n = -1) \ { return QCoreApplication::translate(#context, sourceText, disambiguation, \ QCoreApplication::UnicodeUTF8, n); } \ diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index f962fb7831..cacd999869 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -307,7 +307,7 @@ const QObject *QMetaObject::cast(const QObject *obj) const */ QString QMetaObject::tr(const char *s, const char *c, int n) const { - return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::CodecForTr, n); + return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::DefaultCodec, n); } /*! diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 19440e9368..3a4d1da592 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -1859,7 +1859,7 @@ void QObject::deleteLater() translators while performing translations is not supported. Doing so will probably result in crashes or other undesirable behavior. - \sa trUtf8(), QApplication::translate(), QTextCodec::setCodecForTr(), {Internationalization with Qt} + \sa trUtf8(), QApplication::translate(), {Internationalization with Qt} */ /*! @@ -1871,11 +1871,6 @@ void QObject::deleteLater() version. It is otherwise identical to tr(\a sourceText, \a disambiguation, \a n). - Note that using the Utf8 variants of the translation functions - is not required if \c CODECFORTR is already set to UTF-8 in the - qmake project file and QTextCodec::setCodecForTr("UTF-8") is - used. - \warning This method is reentrant only if all translators are installed \e before calling this method. Installing or removing translators while performing translations is not supported. Doing -- cgit v1.2.3 From cf785e0419f48dc24c35308d6dc61f1103980b3b Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Mon, 5 Mar 2012 17:48:22 +0900 Subject: Add missing Q_OBJECT macro to QCoreTextFontEngine Change-Id: I5d6b4742265a026d404d5ffa48f2c554d5483f30 Reviewed-by: Jiang Jiang --- src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h b/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h index bfa2841158..d32f05022e 100644 --- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h +++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext_p.h @@ -55,6 +55,7 @@ QT_BEGIN_NAMESPACE class QRawFontPrivate; class QCoreTextFontEngine : public QFontEngine { + Q_OBJECT public: QCoreTextFontEngine(CTFontRef font, const QFontDef &def); QCoreTextFontEngine(CGFontRef font, const QFontDef &def); -- cgit v1.2.3 From e3027377f0e7598c00b56962f08cb15b568f2356 Mon Sep 17 00:00:00 2001 From: Tasuku Suzuki Date: Fri, 2 Mar 2012 08:53:03 +0900 Subject: QWindow: fix crash on Mac MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a crash when QWindow::baseSize() is invalid size. Change-Id: I4e41f63d69ad0f218bfd35db8f30f18f92d4e9d5 Reviewed-by: Morten Johan Sørvig --- src/plugins/platforms/cocoa/qcocoawindow.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index b2e93470c9..3f566ccb44 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -201,7 +201,7 @@ void QCocoaWindow::propagateSizeHints() [m_nsWindow setResizeIncrements : qt_mac_toNSSize(window()->sizeIncrement())]; QSize baseSize = window()->baseSize(); - if (!baseSize.isNull()) { + if (!baseSize.isNull() && baseSize.isValid()) { [m_nsWindow setFrameSize : NSMakeSize(baseSize.width(), baseSize.height()) display : YES]; } } -- cgit v1.2.3 From e6f84312a5352c742f240385e2814ef9b865db22 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Mon, 20 Feb 2012 10:30:07 +0100 Subject: qpa: Document a requirement of the backing store implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docuent the requirement that the alpha channels need to be properly initialized by the implementation. Change-Id: I03db81b44b43ea75feb1b983fb0725c65a3bd9f4 Reviewed-by: Samuel Rødal --- src/gui/painting/qplatformbackingstore_qpa.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/gui/painting/qplatformbackingstore_qpa.cpp b/src/gui/painting/qplatformbackingstore_qpa.cpp index 485190d301..ff7d91ccea 100644 --- a/src/gui/painting/qplatformbackingstore_qpa.cpp +++ b/src/gui/painting/qplatformbackingstore_qpa.cpp @@ -114,6 +114,9 @@ QWindow* QPlatformBackingStore::window() const This function is called before painting onto the surface begins, with the \a region in which the painting will occur. + \note A platform providing a backing store with an alpha channel + needs to properly initialize the region to be painted. + \sa endPaint(), paintDevice() */ -- cgit v1.2.3 From c78957766a5adba45289a0f7afe22949a183b34b Mon Sep 17 00:00:00 2001 From: David Faure Date: Fri, 2 Mar 2012 20:33:55 +0100 Subject: QMimeDatabase: Fix crash on empty filename This is due to the search in the suffix tree starting at position fileName.length() - 1. Change-Id: I98501c1724c7dde2626351ace8ba19faa0d2e1e1 Reviewed-by: Ivan Komissarov Reviewed-by: Wolf-Michael Bolle --- src/corelib/mimetypes/qmimeprovider.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/corelib/mimetypes/qmimeprovider.cpp b/src/corelib/mimetypes/qmimeprovider.cpp index 8ef0ee8881..0c2f25a1f9 100644 --- a/src/corelib/mimetypes/qmimeprovider.cpp +++ b/src/corelib/mimetypes/qmimeprovider.cpp @@ -283,6 +283,8 @@ QMimeType QMimeBinaryProvider::mimeTypeForName(const QString &name) QStringList QMimeBinaryProvider::findByFileName(const QString &fileName, QString *foundSuffix) { checkCache(); + if (fileName.isEmpty()) + return QStringList(); const QString lowerFileName = fileName.toLower(); QMimeGlobMatchResult result; // TODO this parses in the order (local, global). Check that it handles "NOGLOBS" correctly. -- cgit v1.2.3 From 00821ec710ec8d9549b9f7e68837a7393a954754 Mon Sep 17 00:00:00 2001 From: Shane Kearns Date: Wed, 29 Feb 2012 15:53:24 +0000 Subject: QSslCertificate - make lazy initialisation thread safe QSslCertificate can be copied around into multiple threads, without detaching. For example, the https worker threads inside QNetworkAccessManager. There are const methods, which lazily initialise members of the private class without detaching (i.e. caching results of expensive function calls) These functions now lock the d pointer using QMutexPool to avoid concurrency related crashes. autotest crashes 20% of the time in release builds without the fix, passes 100 times in a row with the fix. Task-number: QTBUG-20452 Change-Id: I64a01af8159216f2dd6215a08669890f6c029ca8 Reviewed-by: Thiago Macieira Reviewed-by: Richard J. Moore --- src/network/ssl/qsslcertificate.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index 966497be2e..a9573bf1d8 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -123,6 +123,7 @@ #include #include #include +#include #include #include #include @@ -263,6 +264,7 @@ void QSslCertificate::clear() */ QByteArray QSslCertificate::version() const { + QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); if (d->versionString.isEmpty() && d->x509) d->versionString = QByteArray::number(qlonglong(q_ASN1_INTEGER_get(d->x509->cert_info->version)) + 1); @@ -275,6 +277,7 @@ QByteArray QSslCertificate::version() const */ QByteArray QSslCertificate::serialNumber() const { + QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); if (d->serialNumberString.isEmpty() && d->x509) { ASN1_INTEGER *serialNumber = d->x509->cert_info->serialNumber; QByteArray hexString; @@ -327,6 +330,7 @@ static QByteArray _q_SubjectInfoToString(QSslCertificate::SubjectInfo info) */ QStringList QSslCertificate::issuerInfo(SubjectInfo info) const { + QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); // lazy init if (d->issuerInfo.isEmpty() && d->x509) d->issuerInfo = @@ -344,6 +348,7 @@ QStringList QSslCertificate::issuerInfo(SubjectInfo info) const */ QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const { + QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); // lazy init if (d->issuerInfo.isEmpty() && d->x509) d->issuerInfo = @@ -363,6 +368,7 @@ QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const */ QStringList QSslCertificate::subjectInfo(SubjectInfo info) const { + QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); // lazy init if (d->subjectInfo.isEmpty() && d->x509) d->subjectInfo = @@ -379,6 +385,7 @@ QStringList QSslCertificate::subjectInfo(SubjectInfo info) const */ QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const { + QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); // lazy init if (d->subjectInfo.isEmpty() && d->x509) d->subjectInfo = @@ -398,6 +405,7 @@ QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const */ QList QSslCertificate::subjectInfoAttributes() const { + QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); // lazy init if (d->subjectInfo.isEmpty() && d->x509) d->subjectInfo = @@ -417,6 +425,7 @@ QList QSslCertificate::subjectInfoAttributes() const */ QList QSslCertificate::issuerInfoAttributes() const { + QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); // lazy init if (d->issuerInfo.isEmpty() && d->x509) d->issuerInfo = -- cgit v1.2.3 From 930a90d97835223587c2c8df1213c64295f3af97 Mon Sep 17 00:00:00 2001 From: Morten Johan Sorvig Date: Fri, 24 Feb 2012 09:17:00 +0100 Subject: Cocoa: Implement widget palettes. Add roles to QPLatformTheme::Palette, map QWidget subclasses to those. Port Qt4 widget palette creation code to use the QPLatformTheme::Palette roles. Palette entries are disabled in this commit, this will be fixed later. Change-Id: I07babe3d7c76d306efc4ea4813c7161fdf36227f Reviewed-by: Friedemann Kleint --- src/gui/kernel/qplatformtheme_qpa.h | 13 ++++ src/plugins/platforms/cocoa/qcocoasystemsettings.h | 4 +- .../platforms/cocoa/qcocoasystemsettings.mm | 86 +++++++++++++++++++++- src/plugins/platforms/cocoa/qcocoatheme.h | 4 +- src/plugins/platforms/cocoa/qcocoatheme.mm | 5 +- src/widgets/kernel/qapplication.cpp | 5 +- src/widgets/kernel/qapplication_qpa.cpp | 25 +++++++ 7 files changed, 136 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qplatformtheme_qpa.h b/src/gui/kernel/qplatformtheme_qpa.h index 3610a3c1c8..be18e4fe38 100644 --- a/src/gui/kernel/qplatformtheme_qpa.h +++ b/src/gui/kernel/qplatformtheme_qpa.h @@ -86,6 +86,19 @@ public: enum Palette { SystemPalette, ToolTipPalette, + ToolButtonPalette, + ButtonPalette, + HeaderPalette, + ComboBoxPalette, + ItemViewPalette, + MessageBoxLabelPelette, + TabBarPalette, + LabelPalette, + GroupBoxPalette, + MenuPalette, + MenuBarPalette, + TextEditPalette, + TextLineEditPalette, NPalettes }; diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.h b/src/plugins/platforms/cocoa/qcocoasystemsettings.h index 84a66d7193..2ed6f766aa 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.h +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.h @@ -42,12 +42,14 @@ #ifndef QCOCOASYSTEMSETTINGS_H #define QCOCOASYSTEMSETTINGS_H -#include +#include #include +#include QT_BEGIN_NAMESPACE QPalette * qt_mac_createSystemPalette(); +QHash qt_mac_createRolePalettes(); QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm index 5170c0bc8a..eea2fb6f5d 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm @@ -41,9 +41,10 @@ #include "qcocoasystemsettings.h" -#include #include +#include + QColor qt_mac_colorFromCGColor(CGColorRef cgcolor) { QColor pc; @@ -143,3 +144,86 @@ QPalette * qt_mac_createSystemPalette() return palette; } + +struct QMacPaletteMap { + inline QMacPaletteMap(QPlatformTheme::Palette p, ThemeBrush a, ThemeBrush i) : + paletteRole(p), active(a), inactive(i) { } + + QPlatformTheme::Palette paletteRole; + ThemeBrush active, inactive; +}; + +static QMacPaletteMap mac_widget_colors[] = { +// TODO (msorvig): Fix/match palette behavior with Qt 4 and enable. +// +// QMacPaletteMap(QPlatformTheme::ToolButtonPalette, kThemeTextColorBevelButtonActive, kThemeTextColorBevelButtonInactive), +// QMacPaletteMap(QPlatformTheme::ButtonPalette, kThemeTextColorPushButtonActive, kThemeTextColorPushButtonInactive), +// QMacPaletteMap(QPlatformTheme::HeaderPalette, kThemeTextColorPushButtonActive, kThemeTextColorPushButtonInactive), +// QMacPaletteMap(QPlatformTheme::ComboBoxPalette, kThemeTextColorPopupButtonActive, kThemeTextColorPopupButtonInactive), +// QMacPaletteMap(QPlatformTheme::ItemViewPalette, kThemeTextColorListView, kThemeTextColorDialogInactive), +// QMacPaletteMap(QPlatformTheme::MessageBoxLabelPelette, kThemeTextColorAlertActive, kThemeTextColorAlertInactive), +// QMacPaletteMap(QPlatformTheme::TabBarPalette, kThemeTextColorTabFrontActive, kThemeTextColorTabFrontInactive), +// QMacPaletteMap(QPlatformTheme::LabelPalette, kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive), +// QMacPaletteMap(QPlatformTheme::GroupBoxPalette, kThemeTextColorPlacardActive, kThemeTextColorPlacardInactive), +// QMacPaletteMap(QPlatformTheme::MenuPalette, kThemeTextColorPopupLabelActive, kThemeTextColorPopupLabelInactive), +// ### TODO: The zeros below gives white-on-black text. +// QMacPaletteMap(QPlatformTheme::TextEditPalette, 0, 0), +// QMacPaletteMap(QPlatformTheme::TextLineEditPalette, 0, 0), + QMacPaletteMap(QPlatformTheme::NPalettes, 0, 0) }; + +QHash qt_mac_createRolePalettes() +{ + QHash palettes; + QColor qc; + for (int i = 0; mac_widget_colors[i].paletteRole != QPlatformTheme::NPalettes; i++) { + QPalette pal; + if (mac_widget_colors[i].active != 0) { + qc = qt_mac_colorForThemeTextColor(mac_widget_colors[i].active); + pal.setColor(QPalette::Active, QPalette::Text, qc); + pal.setColor(QPalette::Active, QPalette::WindowText, qc); + pal.setColor(QPalette::Active, QPalette::HighlightedText, qc); + qc = qt_mac_colorForThemeTextColor(mac_widget_colors[i].inactive); + pal.setColor(QPalette::Inactive, QPalette::Text, qc); + pal.setColor(QPalette::Disabled, QPalette::Text, qc); + pal.setColor(QPalette::Inactive, QPalette::WindowText, qc); + pal.setColor(QPalette::Disabled, QPalette::WindowText, qc); + pal.setColor(QPalette::Inactive, QPalette::HighlightedText, qc); + pal.setColor(QPalette::Disabled, QPalette::HighlightedText, qc); + } + if (mac_widget_colors[i].paletteRole == QPlatformTheme::MenuPalette) { + qc = qt_mac_colorForThemeTextColor(kThemeTextColorMenuItemActive); + pal.setBrush(QPalette::ButtonText, qc); + qc = qt_mac_colorForThemeTextColor(kThemeTextColorMenuItemSelected); + pal.setBrush(QPalette::HighlightedText, qc); + qc = qt_mac_colorForThemeTextColor(kThemeTextColorMenuItemDisabled); + pal.setBrush(QPalette::Disabled, QPalette::Text, qc); + } else if ((mac_widget_colors[i].paletteRole == QPlatformTheme::ButtonPalette) + || (mac_widget_colors[i].paletteRole == QPlatformTheme::HeaderPalette)) { + pal.setColor(QPalette::Disabled, QPalette::ButtonText, + pal.color(QPalette::Disabled, QPalette::Text)); + pal.setColor(QPalette::Inactive, QPalette::ButtonText, + pal.color(QPalette::Inactive, QPalette::Text)); + pal.setColor(QPalette::Active, QPalette::ButtonText, + pal.color(QPalette::Active, QPalette::Text)); + } else if (mac_widget_colors[i].paletteRole == QPlatformTheme::ItemViewPalette) { + pal.setBrush(QPalette::Active, QPalette::Highlight, + qt_mac_colorForTheme(kThemeBrushAlternatePrimaryHighlightColor)); + qc = qt_mac_colorForThemeTextColor(kThemeTextColorMenuItemSelected); + pal.setBrush(QPalette::Active, QPalette::HighlightedText, qc); + pal.setBrush(QPalette::Inactive, QPalette::Text, + pal.brush(QPalette::Active, QPalette::Text)); + pal.setBrush(QPalette::Inactive, QPalette::HighlightedText, + pal.brush(QPalette::Active, QPalette::Text)); + } else if (mac_widget_colors[i].paletteRole == QPlatformTheme::TextEditPalette) { + pal.setBrush(QPalette::Inactive, QPalette::Text, + pal.brush(QPalette::Active, QPalette::Text)); + pal.setBrush(QPalette::Inactive, QPalette::HighlightedText, + pal.brush(QPalette::Active, QPalette::Text)); + } else if (mac_widget_colors[i].paletteRole == QPlatformTheme::TextLineEditPalette) { + pal.setBrush(QPalette::Disabled, QPalette::Base, + pal.brush(QPalette::Active, QPalette::Base)); + } + palettes.insert(mac_widget_colors[i].paletteRole, new QPalette(pal)); + } + return palettes; +} diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h index fa235b6be0..dccda2ce3b 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.h +++ b/src/plugins/platforms/cocoa/qcocoatheme.h @@ -42,8 +42,7 @@ #ifndef QPLATFORMTHEME_COCOA_H #define QPLATFORMTHEME_COCOA_H -#include - +#include #include QT_BEGIN_NAMESPACE @@ -66,6 +65,7 @@ public: QVariant themeHint(ThemeHint hint) const; private: mutable QPalette *m_systemPalette; + mutable QHash m_palettes; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index 6b0e04acf8..71d7c9e294 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -109,8 +109,11 @@ const QPalette *QCocoaTheme::palette(Palette type) const if (type == SystemPalette) { if (!m_systemPalette) m_systemPalette = qt_mac_createSystemPalette(); - return m_systemPalette; + } else { + if (m_palettes.isEmpty()) + m_palettes = qt_mac_createRolePalettes(); + return m_palettes.value(type, 0); } return 0; } diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 53f4942310..bf2729a6d8 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -131,8 +131,11 @@ QApplicationPrivate *QApplicationPrivate::self = 0; static void initSystemPalette() { if (!QApplicationPrivate::sys_pal) - if (const QPalette *themePalette = QGuiApplicationPrivate::platformTheme()->palette()) + if (const QPalette *themePalette = QGuiApplicationPrivate::platformTheme()->palette()) { QApplicationPrivate::setSystemPalette(*themePalette); + QApplicationPrivate::initializeWidgetPaletteHash(); + } + if (!QApplicationPrivate::sys_pal && QApplicationPrivate::app_style) QApplicationPrivate::setSystemPalette(QApplicationPrivate::app_style->standardPalette()); } diff --git a/src/widgets/kernel/qapplication_qpa.cpp b/src/widgets/kernel/qapplication_qpa.cpp index 54c5c39396..6c91b89674 100644 --- a/src/widgets/kernel/qapplication_qpa.cpp +++ b/src/widgets/kernel/qapplication_qpa.cpp @@ -289,8 +289,33 @@ void QApplicationPrivate::cleanupMultitouch_sys() { } +static void setPossiblePalette(const QPalette *palette, const char *className) +{ + if (palette == 0) + return; + QApplicationPrivate::setPalette_helper(*palette, className, false); +} + + void QApplicationPrivate::initializeWidgetPaletteHash() { + QPlatformTheme *platformTheme = QGuiApplicationPrivate::platformTheme(); + if (!platformTheme) + return; + setPossiblePalette(platformTheme->palette(QPlatformTheme::ToolButtonPalette), "QToolButton"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::ButtonPalette), "QAbstractButton"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::HeaderPalette), "QHeaderView"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::HeaderPalette), "Q3Header"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::ItemViewPalette), "QAbstractItemView"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::MessageBoxLabelPelette), "QMessageBoxLabel"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::TabBarPalette), "QTabBar"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::LabelPalette), "QLabel"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::GroupBoxPalette), "QGroupBox"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::MenuPalette), "QMenu"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::MenuBarPalette), "QMenuBar"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::TextEditPalette), "QTextEdit"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::TextEditPalette), "QTextControl"); + setPossiblePalette(platformTheme->palette(QPlatformTheme::TextLineEditPalette), "QLineEdit"); } #ifndef QT_NO_WHEELEVENT -- cgit v1.2.3 From e5dabe8338cb3dacf24079e315e07f2705fd0a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Mon, 5 Mar 2012 15:40:03 +0100 Subject: Improve safeness of QMetaType::registerType. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This internal function is abused by some modules to create dynamic types in metatype system. In Qt5 more non-optional arguments were added to the function and to keep temporary source compatibility an overload was created. QMetaType code assumes that every known type has properly defined basic operations like creation and destruction. Setting a helper function pointer to null value is asking for a crash, because the code doesn't check for that value, the null pointer may be called. Change-Id: I5ca7454a70c308e01de26fab23481b3c94c22371 Reviewed-by: João Abecasis --- src/corelib/kernel/qmetatype.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 003ad1c32d..410a5cc712 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -456,7 +456,7 @@ static int qMetaTypeCustomType_unlocked(const char *typeName, int length) int QMetaType::registerType(const char *typeName, Deleter deleter, Creator creator) { - return registerType(typeName, deleter, creator, 0, 0, 0, TypeFlags()); + return registerType(typeName, deleter, creator, qMetaTypeDestructHelper, qMetaTypeConstructHelper, 0, TypeFlags()); } /*! \internal @@ -474,7 +474,7 @@ int QMetaType::registerType(const char *typeName, Deleter deleter, int size, TypeFlags flags) { QVector *ct = customTypes(); - if (!ct || !typeName || !deleter || !creator) + if (!ct || !typeName || !deleter || !creator || !destructor || !constructor) return -1; #ifdef QT_NO_QOBJECT @@ -1331,6 +1331,7 @@ private: return; deleter = ct->at(type - QMetaType::User).deleter; } + Q_ASSERT_X(deleter, "void QMetaType::destroy(int type, void *data)", "The type was not properly registered"); deleter(where); } @@ -1393,6 +1394,7 @@ private: return 0; ctor = ct->at(type - QMetaType::User).constructor; } + Q_ASSERT_X(ctor, "void *QMetaType::construct(int type, void *where, const void *copy)", "The type was not properly registered"); return ctor(where, copy); } @@ -1481,6 +1483,7 @@ private: return; dtor = ct->at(type - QMetaType::User).destructor; } + Q_ASSERT_X(dtor, "void QMetaType::destruct(int type, void *where)", "The type was not properly registered"); dtor(where); } -- cgit v1.2.3 From 9f13a7d020749e936dfe0b4c0a1d46f4dbee810f Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Fri, 2 Mar 2012 16:20:55 +0100 Subject: Make cache of opentype tables in Harfbuzz face lazy The mechanism in fontconfig which determines if a certain character is available (FcCharSetHasChar()) may give false positives, in which case we would load and unload those fonts per every char for which FC gave us a false positive. This was a major performance regression. Specifically the false positives happened when looking at e.g. italic variants of certain multilingual fonts, since we only check the charset of the font family as a whole and not of the specific variant, which may only support a subset of the chars. To optimize this, we remove the deletion of the font engines after loading them, but also wait with loading the opentype tables until they are actually needed. This means that for the false positives, we will load the font, but the cached data for each unused font will be much smaller. Change-Id: Idfc794401a2080da5946bf65204eb947aeb635ed Reviewed-by: Lars Knoll --- src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp | 26 +++++++++++++++++++++++++- src/3rdparty/harfbuzz/src/harfbuzz-shaper.h | 8 +++++++- src/corelib/tools/qharfbuzz.cpp | 7 ++++++- src/corelib/tools/qharfbuzz_p.h | 1 + src/gui/text/qfontengine.cpp | 15 ++++++++++----- src/gui/text/qfontengine_p.h | 1 + src/gui/text/qtextengine.cpp | 2 +- 7 files changed, 51 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp b/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp index af0ee52e9a..f6900325bc 100644 --- a/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp +++ b/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp @@ -995,7 +995,7 @@ static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Ta return stream; } -HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc) +HB_Face HB_AllocFace(void *font, HB_GetFontTableFunc tableFunc) { HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec)); if (!face) @@ -1012,6 +1012,30 @@ HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc) face->tmpLogClusters = 0; face->glyphs_substituted = false; face->buffer = 0; + face->font_for_init = font; + face->get_font_table_func = tableFunc; + + return face; +} + +HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc) +{ + HB_Face face = HB_AllocFace(font, tableFunc); + if (face) + face = HB_LoadFace(face); + return face; +} + +HB_Face HB_LoadFace(HB_Face face) +{ + void *font = face->font_for_init; + if (!font) + return face; + + HB_GetFontTableFunc tableFunc = face->get_font_table_func; + + face->get_font_table_func = 0; + face->font_for_init = 0; HB_Error error = HB_Err_Ok; HB_Stream stream; diff --git a/src/3rdparty/harfbuzz/src/harfbuzz-shaper.h b/src/3rdparty/harfbuzz/src/harfbuzz-shaper.h index 470e27b6f9..f225a86525 100644 --- a/src/3rdparty/harfbuzz/src/harfbuzz-shaper.h +++ b/src/3rdparty/harfbuzz/src/harfbuzz-shaper.h @@ -201,6 +201,8 @@ typedef struct { hb_bitfield combiningClass :8; } HB_GlyphAttributes; +typedef HB_Error (*HB_GetFontTableFunc)(void *font, HB_Tag tag, HB_Byte *buffer, HB_UInt *length); + typedef struct HB_FaceRec_ { HB_Bool isSymbolFont; @@ -217,11 +219,15 @@ typedef struct HB_FaceRec_ { unsigned int *tmpLogClusters; int length; int orig_nglyphs; + void *font_for_init; + HB_GetFontTableFunc get_font_table_func; } HB_FaceRec; -typedef HB_Error (*HB_GetFontTableFunc)(void *font, HB_Tag tag, HB_Byte *buffer, HB_UInt *length); + HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc); +HB_Face HB_AllocFace(void *font, HB_GetFontTableFunc tableFunc); +HB_Face HB_LoadFace(HB_Face face); void HB_FreeFace(HB_Face face); typedef struct { diff --git a/src/corelib/tools/qharfbuzz.cpp b/src/corelib/tools/qharfbuzz.cpp index 7d08547ab8..11126b814d 100644 --- a/src/corelib/tools/qharfbuzz.cpp +++ b/src/corelib/tools/qharfbuzz.cpp @@ -122,7 +122,12 @@ HB_Bool qShapeItem(HB_ShaperItem *item) HB_Face qHBNewFace(void *font, HB_GetFontTableFunc tableFunc) { - return HB_NewFace(font, tableFunc); + return HB_AllocFace(font, tableFunc); +} + +HB_Face qHBLoadFace(HB_Face face) +{ + return HB_LoadFace(face); } void qHBFreeFace(HB_Face face) diff --git a/src/corelib/tools/qharfbuzz_p.h b/src/corelib/tools/qharfbuzz_p.h index cc575ddffa..3cef3a55dd 100644 --- a/src/corelib/tools/qharfbuzz_p.h +++ b/src/corelib/tools/qharfbuzz_p.h @@ -68,6 +68,7 @@ Q_CORE_EXPORT HB_Bool qShapeItem(HB_ShaperItem *item); // ### temporary Q_CORE_EXPORT HB_Face qHBNewFace(void *font, HB_GetFontTableFunc tableFunc); Q_CORE_EXPORT void qHBFreeFace(HB_Face); +Q_CORE_EXPORT HB_Face qHBLoadFace(HB_Face face); Q_DECLARE_TYPEINFO(HB_GlyphAttributes, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(HB_FixedPoint, Q_PRIMITIVE_TYPE); diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 4bceb28ef7..142d627100 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -227,6 +227,15 @@ HB_Face QFontEngine::harfbuzzFace() const return hbFace; } +HB_Face QFontEngine::initializedHarfbuzzFace() const +{ + HB_Face face = harfbuzzFace(); + if (face != 0 && face->font_for_init != 0) + face = qHBLoadFace(face); + + return face; +} + glyph_metrics_t QFontEngine::boundingBox(glyph_t glyph, const QTransform &matrix) { glyph_metrics_t metrics = boundingBox(glyph); @@ -1364,15 +1373,13 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, if (glyphs->glyphs[glyph_pos] == 0 && str[i].category() != QChar::Separator_Line) { QGlyphLayoutInstance tmp = glyphs->instance(glyph_pos); for (int x=1; x < engines.size(); ++x) { - if (!shouldLoadFontEngineForCharacter(x, ucs4)) + if (engines.at(x) == 0 && !shouldLoadFontEngineForCharacter(x, ucs4)) continue; QFontEngine *engine = engines.at(x); - bool deleteThisEngine = false; if (!engine) { const_cast(this)->loadEngine(x); engine = engines.at(x); - deleteThisEngine = true; } Q_ASSERT(engine != 0); if (engine->type() == Box) @@ -1388,8 +1395,6 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, // set the high byte to indicate which engine the glyph came from glyphs->glyphs[glyph_pos] |= (x << 24); break; - } else if (deleteThisEngine) { - const_cast(this)->unloadEngine(x); } } diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index 44464ee788..660e3be459 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -235,6 +235,7 @@ public: HB_Font harfbuzzFont() const; HB_Face harfbuzzFace() const; + HB_Face initializedHarfbuzzFace() const; virtual HB_Error getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints); diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index 0460db14d5..dae02def07 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -1112,7 +1112,7 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const si.leading = qMax(actualFontEngine->leading(), si.leading); shaper_item.font = actualFontEngine->harfbuzzFont(); - shaper_item.face = actualFontEngine->harfbuzzFace(); + shaper_item.face = actualFontEngine->initializedHarfbuzzFace(); shaper_item.glyphIndicesPresent = true; -- cgit v1.2.3 From 38d566f7131c3f7a1016ab5fe768e6e9a5c8e54e Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 2 Mar 2012 15:56:19 +0100 Subject: Cleanup QThread::initialize and QThread::cleanup The qt_global_mutexpool was private API deprecated long time ago. And there is no reason to call qt_create_tls because it is called in QThreadData::current that is called from the QObject constructor, even before QCoreApplication::init can be called. Change-Id: Idf3576d8591377811b727b12edc43dc898570ba4 Reviewed-by: Bradley T. Hughes --- src/corelib/kernel/qcoreapplication.cpp | 5 ----- src/corelib/thread/qmutexpool.cpp | 3 --- src/corelib/thread/qmutexpool_p.h | 2 -- src/corelib/thread/qthread.cpp | 28 ---------------------------- src/corelib/thread/qthread.h | 3 --- 5 files changed, 41 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 184743e865..967ed447d5 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -543,10 +543,6 @@ void QCoreApplication::init() Q_ASSERT_X(!self, "QCoreApplication", "there should be only one application object"); QCoreApplication::self = this; -#ifndef QT_NO_THREAD - QThread::initialize(); -#endif - // use the event dispatcher created by the app programmer (if any) if (!QCoreApplicationPrivate::eventDispatcher) QCoreApplicationPrivate::eventDispatcher = d->threadData->eventDispatcher; @@ -602,7 +598,6 @@ QCoreApplication::~QCoreApplication() } if (globalThreadPool) globalThreadPool->waitForDone(); - QThread::cleanup(); #endif d_func()->threadData->eventDispatcher = 0; diff --git a/src/corelib/thread/qmutexpool.cpp b/src/corelib/thread/qmutexpool.cpp index b102770d23..6b6674ccdf 100644 --- a/src/corelib/thread/qmutexpool.cpp +++ b/src/corelib/thread/qmutexpool.cpp @@ -46,9 +46,6 @@ QT_BEGIN_NAMESPACE -// qt_global_mutexpool is here for backwards compatibility only, -// use QMutexpool::instance() in new clode. -Q_CORE_EXPORT QMutexPool *qt_global_mutexpool = 0; Q_GLOBAL_STATIC_WITH_ARGS(QMutexPool, globalMutexPool, (QMutex::Recursive)) /*! diff --git a/src/corelib/thread/qmutexpool_p.h b/src/corelib/thread/qmutexpool_p.h index ce55a40bb8..f5428bed52 100644 --- a/src/corelib/thread/qmutexpool_p.h +++ b/src/corelib/thread/qmutexpool_p.h @@ -84,8 +84,6 @@ private: QMutex::RecursionMode recursionMode; }; -extern Q_CORE_EXPORT QMutexPool *qt_global_mutexpool; - QT_END_NAMESPACE #endif // QT_NO_THREAD diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 64fd8776ce..ea6760a1b9 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -42,7 +42,6 @@ #include "qthread.h" #include "qthreadstorage.h" #include "qmutex.h" -#include "qmutexpool_p.h" #include "qreadwritelock.h" #include "qabstracteventdispatcher.h" @@ -537,33 +536,6 @@ void QThread::run() (void) exec(); } -/*! \internal - Initializes the QThread system. -*/ -#if defined (Q_OS_WIN) -void qt_create_tls(); -#endif - -void QThread::initialize() -{ - if (qt_global_mutexpool) - return; - qt_global_mutexpool = QMutexPool::instance(); - -#if defined (Q_OS_WIN) - qt_create_tls(); -#endif -} - - -/*! \internal - Cleans up the QThread system. -*/ -void QThread::cleanup() -{ - qt_global_mutexpool = 0; -} - /*! \fn void QThread::setPriority(Priority priority) \since 4.1 diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h index ba119afb5d..953632c6fc 100644 --- a/src/corelib/thread/qthread.h +++ b/src/corelib/thread/qthread.h @@ -128,9 +128,6 @@ private: Q_OBJECT Q_DECLARE_PRIVATE(QThread) - static void initialize(); - static void cleanup(); - friend class QCoreApplication; friend class QThreadData; }; -- cgit v1.2.3 From 1e6514a714c1f55b9cb57d2b8b65bc2305c2e2c6 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 2 Mar 2012 16:18:52 +0100 Subject: Don't use QMutexPool from the animation framework Use a plain QBasicMutex instead Change-Id: I1abd35b4fe4e9f0401e73c7c3f503b00bba2baa9 Reviewed-by: Bradley T. Hughes --- src/corelib/animation/qpropertyanimation.cpp | 5 +++-- src/corelib/animation/qvariantanimation.cpp | 22 ++++++++++------------ 2 files changed, 13 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/corelib/animation/qpropertyanimation.cpp b/src/corelib/animation/qpropertyanimation.cpp index fc51a20a15..83ae7bafaa 100644 --- a/src/corelib/animation/qpropertyanimation.cpp +++ b/src/corelib/animation/qpropertyanimation.cpp @@ -92,7 +92,7 @@ #include "qanimationgroup.h" #include "qpropertyanimation_p.h" -#include +#include #ifndef QT_NO_ANIMATION @@ -268,7 +268,8 @@ void QPropertyAnimation::updateState(QAbstractAnimation::State newState, QPropertyAnimation *animToStop = 0; { #ifndef QT_NO_THREAD - QMutexLocker locker(QMutexPool::globalInstanceGet(&staticMetaObject)); + static QBasicMutex mutex; + QMutexLocker locker(&mutex); #endif typedef QPair QPropertyAnimationPair; typedef QHash QPropertyAnimationHash; diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index 2262a3836e..59b2d6abf3 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -45,7 +45,6 @@ #include #include #include -#include #ifndef QT_NO_ANIMATION @@ -399,6 +398,7 @@ void QVariantAnimation::setEasingCurve(const QEasingCurve &easing) typedef QVector QInterpolatorVector; Q_GLOBAL_STATIC(QInterpolatorVector, registeredInterpolators) +static QBasicMutex registeredInterpolatorsMutex; /*! \fn void qRegisterAnimationInterpolator(QVariant (*func)(const T &from, const T &to, qreal progress)) @@ -435,9 +435,7 @@ void QVariantAnimation::registerInterpolator(QVariantAnimation::Interpolator fun // in such an order that we get here with interpolators == NULL, // to continue causes the app to crash on exit with a SEGV if (interpolators) { -#ifndef QT_NO_THREAD - QMutexLocker locker(QMutexPool::globalInstanceGet(interpolators)); -#endif + QMutexLocker locker(®isteredInterpolatorsMutex); if (int(interpolationType) >= interpolators->count()) interpolators->resize(int(interpolationType) + 1); interpolators->replace(interpolationType, func); @@ -452,14 +450,14 @@ template static inline QVariantAnimation::Interpolator castToInterpo QVariantAnimation::Interpolator QVariantAnimationPrivate::getInterpolator(int interpolationType) { - QInterpolatorVector *interpolators = registeredInterpolators(); -#ifndef QT_NO_THREAD - QMutexLocker locker(QMutexPool::globalInstanceGet(interpolators)); -#endif - QVariantAnimation::Interpolator ret = 0; - if (interpolationType < interpolators->count()) { - ret = interpolators->at(interpolationType); - if (ret) return ret; + { + QInterpolatorVector *interpolators = registeredInterpolators(); + QMutexLocker locker(®isteredInterpolatorsMutex); + QVariantAnimation::Interpolator ret = 0; + if (interpolationType < interpolators->count()) { + ret = interpolators->at(interpolationType); + if (ret) return ret; + } } switch(interpolationType) -- cgit v1.2.3 From 8bf6d6a6caadd4db36bb36d7de7ccb76dd031452 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 2 Mar 2012 15:39:50 +0100 Subject: Don't use QMutexPool in QEventDispatcher Use a QBasicMutex, there is no extra cost of having a mutex for this. Change-Id: Ib5b01338649002c0c21f018b2c931a8cc68027f6 Reviewed-by: Bradley T. Hughes --- src/corelib/kernel/qeventdispatcher_glib.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/corelib/kernel/qeventdispatcher_glib.cpp b/src/corelib/kernel/qeventdispatcher_glib.cpp index 4adcb7678c..3f272a2512 100644 --- a/src/corelib/kernel/qeventdispatcher_glib.cpp +++ b/src/corelib/kernel/qeventdispatcher_glib.cpp @@ -42,7 +42,6 @@ #include "qeventdispatcher_glib_p.h" #include "qeventdispatcher_unix_p.h" -#include #include #include "qcoreapplication.h" @@ -295,8 +294,8 @@ QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context) : mainContext(context) { if (qgetenv("QT_NO_THREADED_GLIB").isEmpty()) { - static int dummyValue = 0; // only used for its address - QMutexLocker locker(QMutexPool::instance()->get(&dummyValue)); + static QBasicMutex mutex; + QMutexLocker locker(&mutex); if (!g_thread_supported()) g_thread_init(NULL); } -- cgit v1.2.3 From 3ff7bc086b9ae3d7288e6a4e1f96974b77105ae0 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 6 Mar 2012 10:18:14 +0100 Subject: QProcess/Win: pointless Sleep call removed Change-Id: I634c62d3a0f96bc074e815dfd4106b6187f4ba85 Reviewed-by: Friedemann Kleint Reviewed-by: Oswald Buddenhagen --- src/corelib/io/qprocess_win.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index a52fd46c97..f7c2f965d5 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -530,8 +530,6 @@ void QProcessPrivate::startProcess() notifier->start(NOTIFYTIMEOUT); } - // give the process a chance to start ... - Sleep(SLEEPMIN * 2); _q_startupNotification(); } -- cgit v1.2.3 From 2cf8e487a5c546382daaa954c23d3926668b0a85 Mon Sep 17 00:00:00 2001 From: Shane Kearns Date: Wed, 22 Feb 2012 21:23:48 +0000 Subject: Windows - fix QWindowsSystemProxy global static race Loser of the race would try to delete an uninitialised pointer Task-number: QTBUG-15765 Change-Id: Ie184ee2306e102aa8fbad752ef09b95c3ede00c2 Reviewed-by: Thiago Macieira --- src/network/kernel/qnetworkproxy_win.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp index 6969a9effd..33ae4a6a14 100644 --- a/src/network/kernel/qnetworkproxy_win.cpp +++ b/src/network/kernel/qnetworkproxy_win.cpp @@ -256,7 +256,7 @@ public: Q_GLOBAL_STATIC(QWindowsSystemProxy, systemProxy) QWindowsSystemProxy::QWindowsSystemProxy() - : initialized(false), functional(false), isAutoConfig(false) + : hHttpSession(0), initialized(false), functional(false), isAutoConfig(false) { defaultResult << QNetworkProxy::NoProxy; } -- cgit v1.2.3 From c93f7b69486a0d12b475e31eeb699ae07aa928c2 Mon Sep 17 00:00:00 2001 From: Shane Kearns Date: Mon, 5 Mar 2012 16:54:12 +0000 Subject: Fix tst_QNetworkReply::httpWithNoCredentialUsage autotest The test was testing the wrong thing, and passing even though QNetworkRequest::AuthenticationReuseAttribute was not being respected, until recently when I fixed username/password in URLs Now the cache is properly bypassed when this attribute is set to manual, and the autotest is updated to check this. Change-Id: I87943515562d0b16b03504f0758ba265758d1c22 Reviewed-by: Martin Petersson --- src/network/access/qnetworkaccessmanager.cpp | 10 ++++++---- src/network/access/qnetworkaccessmanager_p.h | 3 ++- src/network/access/qnetworkreplyhttpimpl.cpp | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 60c28274c6..c65edb6673 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -1089,15 +1089,16 @@ void QNetworkAccessManagerPrivate::authenticationRequired(QAuthenticator *authen QNetworkReply *reply, bool synchronous, QUrl &url, - QUrl *urlForLastAuthentication) + QUrl *urlForLastAuthentication, + bool allowAuthenticationReuse) { Q_Q(QNetworkAccessManager); // don't try the cache for the same URL twice in a row // being called twice for the same URL means the authentication failed // also called when last URL is empty, e.g. on first call - if (urlForLastAuthentication->isEmpty() - || url != *urlForLastAuthentication) { + if (allowAuthenticationReuse && (urlForLastAuthentication->isEmpty() + || url != *urlForLastAuthentication)) { // if credentials are included in the url, then use them if (!url.userName().isEmpty() && !url.password().isEmpty()) { @@ -1124,7 +1125,8 @@ void QNetworkAccessManagerPrivate::authenticationRequired(QAuthenticator *authen *urlForLastAuthentication = url; emit q->authenticationRequired(reply, authenticator); - authenticationManager->cacheCredentials(url, authenticator); + if (allowAuthenticationReuse) + authenticationManager->cacheCredentials(url, authenticator); } #ifndef QT_NO_NETWORKPROXY diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h index 0733756be4..b0bcaabacc 100644 --- a/src/network/access/qnetworkaccessmanager_p.h +++ b/src/network/access/qnetworkaccessmanager_p.h @@ -98,7 +98,8 @@ public: QNetworkReply *reply, bool synchronous, QUrl &url, - QUrl *urlForLastAuthentication); + QUrl *urlForLastAuthentication, + bool allowAuthenticationReuse = true); void cacheCredentials(const QUrl &url, const QAuthenticator *auth); QNetworkAuthenticationCredential *fetchCachedCredentials(const QUrl &url, const QAuthenticator *auth = 0); diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 2124395de3..1f456746ae 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -1179,10 +1179,10 @@ void QNetworkReplyHttpImplPrivate::replyDownloadProgressSlot(qint64 bytesReceive emit q->downloadProgress(bytesDownloaded, bytesTotal); } -void QNetworkReplyHttpImplPrivate::httpAuthenticationRequired(const QHttpNetworkRequest &, +void QNetworkReplyHttpImplPrivate::httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth) { - managerPrivate->authenticationRequired(auth, q_func(), synchronous, url, &urlForLastAuthentication); + managerPrivate->authenticationRequired(auth, q_func(), synchronous, url, &urlForLastAuthentication, request.withCredentials()); } #ifndef QT_NO_NETWORKPROXY -- cgit v1.2.3 From 092a270afde4fade3dbe36fde7156e5a462a13cb Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Mon, 5 Mar 2012 21:03:09 +0100 Subject: fix relative default examples path copy&pasto ... Change-Id: I73ab90f31f2a2250abe1ec9aeea975122ff319cb Reviewed-by: Joerg Bornemann Reviewed-by: Mark Brand --- src/corelib/global/qlibraryinfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index 4caacece2d..cb4e0e753f 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -226,7 +226,7 @@ static const struct { { "Imports", "imports" }, { "Data", "" }, { "Translations", "translations" }, - { "Examples", "" }, + { "Examples", "examples" }, { "Tests", "tests" }, #ifdef QT_BUILD_QMAKE { "Sysroot", "" }, -- cgit v1.2.3 From c7cb455a47c42e8e658e3433defee613f8643cd2 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 23 Jan 2012 22:47:59 +0000 Subject: QRegularExpression: add QRegularExpression* set of classes Added QRegularExpression, QRegularExpressionMatch and QRegularExpressionMatchIterator as PCRE-enabled, regexp classes. Documentation is included, as well as a first round of autotests. Task-number: QTBUG-23489 Change-Id: Id47031b80602c913ccd2fd740070e3024ea06abc Reviewed-by: Thiago Macieira Reviewed-by: Lars Knoll --- src/corelib/tools/qregularexpression.cpp | 2022 ++++++++++++++++++++++++++++++ src/corelib/tools/qregularexpression.h | 245 ++++ src/corelib/tools/tools.pri | 8 + 3 files changed, 2275 insertions(+) create mode 100644 src/corelib/tools/qregularexpression.cpp create mode 100644 src/corelib/tools/qregularexpression.h (limited to 'src') diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp new file mode 100644 index 0000000000..488a454aaa --- /dev/null +++ b/src/corelib/tools/qregularexpression.cpp @@ -0,0 +1,2022 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Giuseppe D'Angelo . +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qregularexpression.h" + +#include +#include +#include +#include +#include + +#include + +// after how many usages we optimize the regexp +static const unsigned int OPTIMIZE_AFTER_USE_COUNT = 10; + +QT_BEGIN_NAMESPACE + +/*! + \class QRegularExpression + \reentrant + + \brief The QRegularExpression class provides pattern matching using regular + expressions. + + \since 5.0 + + \ingroup tools + \ingroup shared + + \keyword regular expression + + Regular expressions, or \e{regexps}, are a very powerful tool to handle + strings and texts. This is useful in many contexts, e.g., + + \table + \row \i Validation + \i A regexp can test whether a substring meets some criteria, + e.g. is an integer or contains no whitespace. + \row \i Searching + \i A regexp provides more powerful pattern matching than + simple substring matching, e.g., match one of the words + \e{mail}, \e{letter} or \e{correspondence}, but none of the + words \e{email}, \e{mailman}, \e{mailer}, \e{letterbox}, etc. + \row \i Search and Replace + \i A regexp can replace all occurrences of a substring with a + different substring, e.g., replace all occurrences of \e{&} + with \e{\&} except where the \e{&} is already followed by + an \e{amp;}. + \row \i String Splitting + \i A regexp can be used to identify where a string should be + split apart, e.g. splitting tab-delimited strings. + \endtable + + This document is by no means a complete reference to pattern matching using + regular expressions, and the following parts will require the reader to + have some basic knowledge about Perl-like regular expressions and their + pattern syntax. + + Good references about regular expressions include: + + \list + \o \e {Mastering Regular Expressions} (Third Edition) by Jeffrey E. F. + Friedl, ISBN 0-596-52812-4; + \o the \l{http://pcre.org/pcre.txt} {pcrepattern(3)} man page, describing + the pattern syntax supported by PCRE (the reference implementation of + Perl-compatible regular expressions); + \o the \l{http://perldoc.perl.org/perlre.html} {Perl's regular expression + documentation} and the \l{http://perldoc.perl.org/perlretut.html} {Perl's + regular expression tutorial}. + \endlist + + \tableofcontents + + \section1 Introduction + + QRegularExpression implements Perl-compatible regular expressions. It fully + supports Unicode. For an overview of the regular expression syntax + supported by QRegularExpression, please refer to the aforementioned + pcrepattern(3) man page. A regular expression is made up of two things: a + \bold{pattern string} and a set of \bold{pattern options} that change the + meaning of the pattern string. + + You can set the pattern string by passing a string to the QRegularExpression + constructor: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 0 + + This sets the pattern string to \c{a pattern}. You can also use the + setPattern() function to set a pattern on an existing QRegularExpression + object: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 1 + + Note that due to C++ literal strings rules, you must escape all backslashes + inside the pattern string with another backslash: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 2 + + The pattern() function returns the pattern that it's currently set for a + QRegularExpression object: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 3 + + \section1 Pattern options + + The meaning of the pattern string can be modified by setting one or more + \e{pattern options}. For instance, it is possible to set a pattern to match + case insensitively by setting the QRegularExpression::CaseInsensitiveOption. + + You can set the options by passing them to the QRegularExpression + constructor, as in: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 4 + + Alternatively, you can use the setPatternOptions() function on an existing + QRegularExpressionObject: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 5 + + It is possible to get the pattern options currently set on a + QRegularExpression object by using the patternOptions() function: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 6 + + Please refer to the QRegularExpression::PatternOption enum documentation for + more information about each pattern option. + + \section1 Match type and match options + + The last two arguments of the match() and the globalMatch() functions set + the match type and the match options. The match type is a value of the + QRegularExpression::MatchType enum; the "traditional" matching algorithm is + chosen by using the NormalMatch match type (the default). It is also + possible to enable partial matching of the regular expression against a + subject string: see the \l{partial matching} section for more details. + + The match options are a set of one or more QRegularExpression::MatchOption + values. They change the way a specific match of a regular expression + against a subject string is done. Please refer to the + QRegularExpression::MatchOption enum documentation for more details. + + \target normal matching + \section1 Normal matching + + In order to perform a match you can simply invoke the match() function + passing a string to match against. We refer to this string as the + \e{subject string}. The result of the match() function is a + QRegularExpressionMatch object that can be used to inspect the results of + the match. For instance: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 7 + + If a match is successful, the (implicit) capturing group number 0 can be + used to retrieve the substring matched by the entire pattern (see also the + section about \l{extracting captured substrings}): + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 8 + + It's also possible to start a match at an arbitrary offset inside the + subject string by passing the offset as an argument of the + match() function. In the following example \c{"12 abc"} + is not matched because the match is started at offset 1: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 9 + + \target extracting captured substrings + \section2 Extracting captured substrings + + The QRegularExpressionMatch object contains also information about the + substrings captured by the capturing groups in the pattern string. The + \l{QRegularExpressionMatch::}{captured()} function will return the string + captured by the n-th capturing group: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 10 + + Capturing groups in the pattern are numbered starting from 1, and the + implicit capturing group 0 is used to capture the substring that matched + the entire pattern. + + It's also possible to retrieve the starting and the ending offsets (inside + the subject string) of each captured substring, by using the + \l{QRegularExpressionMatch::}{capturedStart()} and the + \l{QRegularExpressionMatch::}{capturedEnd()} functions: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 11 + + All of these functions have an overload taking a QString as a parameter + in order to extract \e{named} captured substrings. For instance: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 12 + + \target global matching + \section1 Global matching + + \e{Global matching} is useful to find all the occurrences of a given + regular expression inside a subject string. Suppose that we want to extract + all the words from a given string, where a word is a substring matching + the pattern \c{\w+}. + + QRegularExpression::globalMatch returns a QRegularExpressionMatchIterator, + which is a Java-like forward iterator that can be used to iterate over the + results. For instance: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 13 + + Since it's a Java-like iterator, the QRegularExpressionMatchIterator will + point immediately before the first result. Every result is returned as a + QRegularExpressionMatch object. The + \l{QRegularExpressionMatchIterator::}{hasNext()} function will return true + if there's at least one more result, and + \l{QRegularExpressionMatchIterator::}{next()} will return the next result + and advance the iterator. Continuing from the previous example: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 14 + + You can also use \l{QRegularExpressionMatchIterator::}{peekNext()} to get + the next result without advancing the iterator. + + It is possible to pass a starting offset and one or more match options to + the globalMatch() function, exactly like normal matching with match(). + + \target partial matching + \section1 Partial matching + + A \e{partial match} is obtained when the end of the subject string is + reached, but more characters are needed to successfully complete the match. + Note that a partial match is usually much more inefficient than a normal + match because many optimizations of the matching algorithm cannot be + employed. + + A partial match must be explicitly requested by specifying a match type of + PartialPreferCompleteMatch or PartialPreferFirstMatch when calling + QRegularExpression::match or QRegularExpression::globalMatch. If a partial + match is found, then calling the \l{QRegularExpressionMatch::}{hasMatch()} + function on the QRegularExpressionMatch object returned by match() will + return \c{false}, but \l{QRegularExpressionMatch::}{hasPartialMatch()} will return + \c{true}. + + When a partial match is found, no captured substrings are returned, and the + (implicit) capturing group 0 corresponding to the whole match captures the + partially matched substring of the subject string. + + Note that asking for a partial match can still lead to a complete match, if + one is found; in this case, \l{QRegularExpressionMatch::}{hasMatch()} will + return \c{true} and \l{QRegularExpressionMatch::}{hasPartialMatch()} + \c{false}. It never happens that a QRegularExpressionMatch reports both a + partial and a complete match. + + Partial matching is mainly useful in two scenarios: validating user input + in real time and incremental/multi-segment matching. + + \target + \section2 Validating user input + + Suppose that we would like the user to input a date in a specific + format, for instance "MMM dd, yyyy". We can check the input validity with + a pattern like: + + \c{^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d\d?, \d\d\d\d$} + + (This pattern doesn't catch invalid days, but let's keep it for the + example's purposes). + + We would like to validate the input with this regular expression \e{while} + the user is typing it, so that we can report an error in the input as soon + as it is committed (for instance, the user typed the wrong key). In order + to do so we must distinguish three cases: + + \list + \o the input cannot possibly match the regular expression; + \o the input does match the regular expression; + \o the input does not match the regular expression right now, + but it will if more charaters will be added to it. + \endlist + + Note that these three cases represent exactly the possible states of a + QValidator (see the QValidator::State enum). + + In particular, in the last case we want the regular expression engine to + report a partial match: we are successfully matching the pattern against + the subject string but the matching cannot continue because the end of the + subject is encountered. Notice, however, that the matching algorithm should + continue and try all possibilities, and in case a complete (non-partial) + match is found, then this one should be reported, and the input string + accepted as fully valid. + + This behaviour is implemented by the PartialPreferCompleteMatch match type. + For instance: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 15 + + If matching the same regular expression against the subject string leads to + a complete match, it is reported as usual: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 16 + + Another example with a different pattern, showing the behaviour of + preferring a complete match over a partial one: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 17 + + In this case, the subpattern \c{abc\\w+X} partially matches the subject + string; however, the subpattern \c{def} matches the subject string + completely, and therefore a complete match is reported. + + In case multiple partial matches are found when matching (but no complete + match), then the QRegularExpressionMatch will report the first one that it + is found. For instance: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 18 + + \section2 Incremental/multi-segment matching + + Incremental matching is another use case of partial matching. Suppose that + we want to find the occurrences of a regular expression inside a large text + (that is, substrings matching the regular expression). In order to do so we + would like to "feed" the large text to the regular expression engines in + smaller chunks. The obvious problem is what happens if the substring that + matches the regular expression spans across two or more chunks. + + In this case, the regular expression engine should report a partial match, + so that we can match again adding new data and (eventually) get a complete + match. This implies that the regular expression engine may assume that + there are other characters \e{beyond the end} of the subject string. This + is not to be taken literally -- the engine will never try to access + any character after the last one in the subject. + + QRegularExpression implements this behaviour when using the + PartialPreferFirstMatch match type. This match type reports a partial match + as soon as it is found, and other match alternatives are not tried + (even if they could lead to a complete match). For instance: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 19 + + This happens because when matching the first branch of the alternation + operator a partial match is found, and therefore matching stops, without + trying the second branch. Another example: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 20 + + This shows what could seem a counterintuitve behaviour of quantifiers: + since \c{?} is greedy, then the engine tries first to continue the match + after having matched \c{"abc"}; but then the matching reaches the end of the + subject string, and therefore a partial match is reported. This is + even more surprising in the following example: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 21 + + It's easy to understand this behaviour if we remember that the engine + expects the subject string to be only a substring of the whole text we're + looking for a match into (that is, how we said before, that the engine + assumes that there are other characters beyond the end of the subject + string). + + Since the \c{*} quantifier is greedy, then reporting a complete match could + be an error, because after the current subject \c{"abc"} there may be other + occurrences of \c{"abc"}. For instance, the complete text could have been + "abcabcX", and therefore the \e{right} match to report (in the complete + text) would have been \c{"abcabc"}; by matching only against the leading + \c{"abc"} we instead get a partial match. + + \section1 Error handling + + It is possible for a QRegularExpression object to be invalid because of + syntax errors in the pattern string. The isValid() function will return + true if the regular expression is valid, or false otherwise: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 22 + + You can get more information about the specific error by calling the + errorString() function; moreover, the patternErrorOffset() function + will return the offset inside the pattern string + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 23 + + If a match is attempted with an invalid QRegularExpression, then the + returned QRegularExpressionMatch object will be invalid as well (that is, + its \l{QRegularExpressionMatch::}{isValid()} function will return false). + The same applies for attempting a global match. + + \section1 Unsupported Perl-compatible regular expressions features + + QRegularExpression does not support all the features available in + Perl-compatible regular expressions. The most notable one is the fact that + duplicated names for capturing groups are not supported, and using them can + lead to undefined behaviour. + + This may change in a future version of Qt. + + \section1 Notes for QRegExp users + + The QRegularExpression class introduced in Qt 5 is a big improvement upon + QRegExp, in terms of APIs offered, supported pattern syntax and speed of + execution. The biggest difference is that QRegularExpression simply holds a + regular expression, and it's \e{not} modified when a match is requested. + Instead, a QRegularExpressionMatch object is returned, in order to check + the result of a match and extract the captured substring. The same applies + with global matching and QRegularExpressionMatchIterator. + + Other differences are outlined below. + + \section2 Exact matching + + QRegExp::exactMatch in Qt 4 served for two purposes: it exactly matched + a regular expression against a subject string, and it implemented partial + matching. In fact, if an exact match was not found, one could still find + out how much of the subject string was matched by the regular expression + by calling QRegExp::matchedLength(). If the returned length was equal + to the subject string's length, then one could desume that a partial match + was found. + + QRegularExpression supports partial matching explicitly by means of the + appropriate MatchType. If instead you simply want to be sure that the + subject string matches the regular expression exactly, you can wrap the + pattern between a couple of anchoring expressions. Simply + putting the pattern between the \c{^} and the \c{$} anchors is enough + in most cases: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 24 + + However, remember that the \c{$} anchor not only matches at the end of the + string, but also at a newline character right before the end of the string; + that is, the previous pattern matches against the string "this pattern must + match exactly\n". Also, the behaviour of both the \c{^} and the \c{$} + anchors changes if the MultiLineOption is set either explicitely (as a + pattern option) or implicitly (as a directive inside the pattern string). + + Therefore, in the most general case, you should wrap the pattern between + the \c{\A} and the \c{\z} anchors: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 25 + + Note the usage of the non-capturing group in order to preserve the meaning + of the branch operator inside the pattern. + + \section2 Global matching + + Due to limitations of the QRegExp API it was impossible to implement global + matching correctly (that is, like Perl does). In particular, patterns that + can match 0 characters (like \c{"a*"}) are problematic. + + QRegularExpression::globalMatch implements Perl global match correctly, and + the returned iterator can be used to examine each result. + + \section2 Wildcard matching + + There is no equivalent of wildcard matching in QRegularExpression. + Nevertheless, rewriting a regular expression in wildcard syntax to a + Perl-compatible regular expression is a very easy task, given the fact + that wildcard syntax supported by QRegExp is very simple. + + \section2 Other pattern syntaxes + + QRegularExpression supports only Perl-compatible regular expressions. + + \section2 Minimal matching + + QRegExp::setMinimal implemented minimal matching by simply reversing the + greediness of the quantifiers (QRegExp did not support lazy quantifiers, + like \c{*?}, \c{+?}, etc.). QRegularExpression instead does support greedy, + lazy and possessive quantifiers. The InvertedGreedinessOption + pattern option can be useful to emulate the effects of QRegExp::setMinimal: + if enabled, it inverts the greediness of quantifiers (greedy ones become + lazy and vice versa). + + \section2 Caret modes + + The AnchoredMatchOption match option can be used to emulate the + QRegExp::CaretAtOffset behaviour. There is no equivalent for the other + QRegExp::CaretMode modes. + + \sa QRegularExpressionMatch, QRegularExpressionMatchIterator +*/ + +/*! + \class QRegularExpressionMatch + \reentrant + + \brief The QRegularExpressionMatch class provides the results of a matching + a QRegularExpression against a string. + + \since 5.0 + + \ingroup tools + \ingroup shared + + \keyword regular expression match + + A QRegularExpressionMatch object can be obtained by calling the + QRegularExpression::match() function, or as a single result of a global + match from a QRegularExpressionMatchIterator. + + The success or the failure of a match attempt can be inspected by calling + the hasMatch() function. QRegularExpressionMatch also reports a successful + partial match through the hasPartialMatch() function. + + In addition, QRegularExpressionMatch returns the substrings captured by the + capturing groups in the pattern string. The implicit capturing group with + index 0 captures the result of the whole match. The captured() function + returns each substring captured, either by the capturing group's index or + by its name: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 29 + + For each captured substring it is possible to query its starting and ending + offsets in the subject string by calling the capturedStart() and the + capturedEnd() function, respectively. The length of each captured + substring is available using the capturedLength() function. + + The convenience function capturedTexts() will return \e{all} the captured + substrings at once (including the substring matched by the entire pattern) + in the order they have been captured by captring groups; that is, + \c{captured(i) == capturedTexts().at(i)}. + + You can retrieve the QRegularExpression object the subject string was + matched against by calling the regularExpression() function; the + match type and the match options are available as well by calling + the matchType() and the matchOptions() respectively. + + Please refer to the QRegularExpression documentation for more information + about the Qt regular expression classes. + + \sa QRegularExpression +*/ + +/*! + \class QRegularExpressionMatchIterator + \reentrant + + \brief The QRegularExpressionMatchIterator class provides an iterator on + the results of a global match of a QRegularExpression object against a string. + + \since 5.0 + + \ingroup tools + \ingroup shared + + \keyword regular expression iterator + + A QRegularExpressionMatchIterator object is a forward only Java-like + iterator; it can be obtained by calling the + QRegularExpression::globalMatch() function. A new + QRegularExpressionMatchIterator will be positioned before the first result. + You can then call the hasNext() function to check if there are more + results available; if so, the next() function will return the next + result and advance the iterator. + + Each result is a QRegularExpressionMatch object holding all the information + for that result (including captured substrings). + + For instance: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 30 + + Moreover, QRegularExpressionMatchIterator offers a peekNext() function + to get the next result \e{without} advancing the iterator. + + You can retrieve the QRegularExpression object the subject string was + matched against by calling the regularExpression() function; the + match type and the match options are available as well by calling + the matchType() and the matchOptions() respectively. + + Please refer to the QRegularExpression documentation for more information + about the Qt regular expression classes. + + \sa QRegularExpression, QRegularExpressionMatch +*/ + + +/*! + \enum QRegularExpression::PatternOption + + The PatternOption enum defines modifiers to the way the pattern string + should be interpreted, and therefore the way the pattern matches against a + subject string. + + \value NoPatternOption + No pattern options are set. + + \value CaseInsensitiveOption + The pattern should match against the subject string in a case + insensitive way. This option corresponds to the /i modifier in Perl + regular expressions. + + \value DotMatchesEverythingOption + The dot metacharacter (\c{.}) in the pattern string is allowed to match + any character in the subject string, including newlines (normally, the + dot does not match newlines). This option corresponds to the \c{/s} + modifier in Perl regular expressions. + + \value MultilineOption + The caret (\c{^}) and the dollar (\c{$}) metacharacters in the pattern + string are allowed to match, respectively, immediately after and + immediately before any newline in the subject string, as well as at the + very beginning and at the very end of the subject string. This option + corresponds to the \c{/m} modifier in Perl regular expressions. + + \value ExtendedPatternSyntaxOption + Any whitespace in the pattern string which is not escaped and outside a + character class is ignored. Moreover, an unescaped sharp (\bold{#}) + outside a character class causes all the following characters, until + the first newline (included), to be ignored. This can be used to + increase the readability of a pattern string as well as put comments + inside regular expressions; this is particulary useful if the pattern + string is loaded from a file or written by the user, because in C++ + code it is always possible to use the rules for string literals to put + comments outside the pattern string. This option corresponds to the \c{/x} + modifier in Perl regular expressions. + + \value InvertedGreedinessOption + The greediness of the quantifiers is inverted: \c{*}, \c{+}, \c{?}, + \c{{m,n}}, etc. become lazy, while their lazy versions (\c{*?}, + \c{+?}, \c{??}, \c{{m,n}?}, etc.) become greedy. There is no equivalent + for this option in Perl regular expressions. + + \value DontCaptureOption + The non-named capturing groups do not capture substrings; named + capturing groups still work as intended, as well as the implicit + capturing group number 0 corresponding to the entire match. There is no + equivalent for this option in Perl regular expressions. + + \value UseUnicodePropertiesOption + The meaning of the \c{\w}, \c{\d}, etc., character types, as well as + the meaning of their counterparts (\c{\W}, \c{\D}, etc.), is changed + from matching ASCII charaters only to matching any character with the + corresponding Unicode property. For instance, \c{\d} is changed to + match any character with the Unicode Nd (decimal digit) property; + \c{\w} to match any character with either the Unicode L (letter) or N + (digit) property, plus underscore, and so on. This option corresponds + to the \c{/u} modifier in Perl regular expressions. +*/ + +/*! + \enum QRegularExpression::MatchType + + The MatchType enum defines the type of the match that should be attempted + against the subject string. + + \value NormalMatch + A normal match is done. + + \value PartialPreferCompleteMatch + The pattern string is matched partially against the subject string. If + a partial match is found, then it is recorded, and other matching + alternatives are tried as usual. If a complete match is then found, + then it's preferred to the partial match; in this case only the + complete match is reported. If instead no complete match is found (but + only the partial one), then the partial one is reported. + + \value PartialPreferFirstMatch + The pattern string is matched partially against the subject string. If + a partial match is found, then matching stops and the partial match is + reported. In this case, other matching alternatives (potentially + leading to a complete match) are not tried. Moreover, this match type + assumes that the subject string only a substring of a larger text, and + that (in this text) there are other characters beyond the end of the + subject string. This can lead to surprising results; see the discussion + in the \l{partial matching} section for more details. +*/ + +/*! + \enum QRegularExpression::MatchOption + + \value NoMatchOption + No match options are set. + + \value AnchoredMatchOption + The match is constrained to start exactly at the offset passed to + match() in order to be successful, even if the pattern string does not + contain any metacharacter that anchors the match at that point. +*/ + +/*! + \internal +*/ +static int convertToPcreOptions(QRegularExpression::PatternOptions patternOptions) +{ + int options = 0; + + if (patternOptions & QRegularExpression::CaseInsensitiveOption) + options |= PCRE_CASELESS; + if (patternOptions & QRegularExpression::DotMatchesEverythingOption) + options |= PCRE_DOTALL; + if (patternOptions & QRegularExpression::MultilineOption) + options |= PCRE_MULTILINE; + if (patternOptions & QRegularExpression::ExtendedPatternSyntaxOption) + options |= PCRE_EXTENDED; + if (patternOptions & QRegularExpression::InvertedGreedinessOption) + options |= PCRE_UNGREEDY; + if (patternOptions & QRegularExpression::DontCaptureOption) + options |= PCRE_NO_AUTO_CAPTURE; + if (patternOptions & QRegularExpression::UseUnicodePropertiesOption) + options |= PCRE_UCP; + + return options; +} + +/*! + \internal +*/ +static int convertToPcreOptions(QRegularExpression::MatchOptions matchOptions) +{ + int options = 0; + + if (matchOptions & QRegularExpression::AnchoredMatchOption) + options |= PCRE_ANCHORED; + + return options; +} + +struct QRegularExpressionPrivate : QSharedData +{ + QRegularExpressionPrivate(); + ~QRegularExpressionPrivate(); + QRegularExpressionPrivate(const QRegularExpressionPrivate &other); + + void cleanCompiledPattern(); + void compilePattern(); + void getPatternInfo(); + void optimizePattern(); + + QRegularExpressionMatchPrivate *doMatch(const QString &subject, + int offset, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + const QRegularExpressionMatchPrivate *previous = 0) const; + + int captureIndexForName(const QString &name) const; + + QString pattern; + QRegularExpression::PatternOptions patternOptions; + + // *All* of the following members are set managed while holding this mutex, + // except for isDirty which is set to true by QRegularExpression setters + // (right after a detach happened). + // On the other hand, after the compilation and studying, + // it's safe to *use* (i.e. read) them from multiple threads at the same time. + // Therefore, doMatch doesn't need to lock this mutex. + QMutex mutex; + + // The PCRE pointers are reference-counted by the QRegularExpressionPrivate + // objects themselves; when the private is copied (i.e. a detach happened) + // they are set to 0 + pcre16 *compiledPattern; + pcre16_extra *studyData; + const char *errorString; + int errorOffset; + int capturingCount; + unsigned int usedCount; + bool usingCrLfNewlines; + bool isDirty; +}; + +struct QRegularExpressionMatchPrivate : QSharedData +{ + QRegularExpressionMatchPrivate(const QRegularExpression &re, + const QString &subject, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + int capturingCount); + + QRegularExpressionMatch nextMatch() const; + + QRegularExpression regularExpression; + QString subject; + // the capturedOffsets vector contains pairs of (start, end) positions + // for each captured substring + QVector capturedOffsets; + + QRegularExpression::MatchType matchType; + QRegularExpression::MatchOptions matchOptions; + + int capturedCount; + + bool hasMatch; + bool hasPartialMatch; + bool isValid; +}; + +struct QRegularExpressionMatchIteratorPrivate : QSharedData +{ + QRegularExpressionMatchIteratorPrivate(const QRegularExpression re, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + const QRegularExpressionMatch &next); + + bool hasNext() const; + QRegularExpressionMatch next; + QRegularExpression regularExpression; + QRegularExpression::MatchType matchType; + QRegularExpression::MatchOptions matchOptions; +}; + +/*! + \internal +*/ +QRegularExpression::QRegularExpression(QRegularExpressionPrivate &dd) + : d(&dd) +{ +} + +/*! + \internal +*/ +QRegularExpressionPrivate::QRegularExpressionPrivate() + : pattern(), patternOptions(0), + mutex(), + compiledPattern(0), studyData(0), + errorString(0), errorOffset(-1), + capturingCount(0), + usedCount(0), + usingCrLfNewlines(false), + isDirty(true) +{ +} + +/*! + \internal +*/ +QRegularExpressionPrivate::~QRegularExpressionPrivate() +{ + cleanCompiledPattern(); +} + +/*! + \internal + + Copies the private, which means copying only the pattern and the pattern + options. The compiledPattern and the studyData pointers are NOT copied (we + do not own them any more), and in general all the members set when + compiling a pattern are set to default values. isDirty is set back to true + so that the pattern has to be recompiled again. +*/ +QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPrivate &other) + : QSharedData(other), + pattern(other.pattern), patternOptions(other.patternOptions), + mutex(), + compiledPattern(0), studyData(0), + errorString(0), + errorOffset(-1), capturingCount(0), + usedCount(0), + usingCrLfNewlines(false), isDirty(true) +{ +} + +/*! + \internal +*/ +void QRegularExpressionPrivate::cleanCompiledPattern() +{ + pcre16_free(compiledPattern); + pcre16_free_study(studyData); + usedCount = 0; + compiledPattern = 0; + studyData = 0; + usingCrLfNewlines = false; + errorOffset = -1; + capturingCount = 0; +} + +/*! + \internal +*/ +void QRegularExpressionPrivate::compilePattern() +{ + QMutexLocker lock(&mutex); + + if (!isDirty) + return; + + isDirty = false; + cleanCompiledPattern(); + + int options = convertToPcreOptions(patternOptions); + options |= PCRE_UTF16; + + int errorCode; + compiledPattern = pcre16_compile2(pattern.utf16(), options, + &errorCode, &errorString, &errorOffset, 0); + + if (!compiledPattern) + return; + + Q_ASSERT(errorCode == 0); + Q_ASSERT(studyData == 0); // studying (=>optimizing) is always done later + errorOffset = -1; + + getPatternInfo(); +} + +/*! + \internal +*/ +void QRegularExpressionPrivate::getPatternInfo() +{ + Q_ASSERT(compiledPattern); + + pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_CAPTURECOUNT, &capturingCount); + + // detect the settings for the newline + int patternNewlineSetting; + pcre16_fullinfo(compiledPattern, studyData, PCRE_INFO_OPTIONS, &patternNewlineSetting); + patternNewlineSetting &= PCRE_NEWLINE_CR | PCRE_NEWLINE_LF | PCRE_NEWLINE_CRLF + | PCRE_NEWLINE_ANY | PCRE_NEWLINE_ANYCRLF; + if (patternNewlineSetting == 0) { + // no option was specified in the regexp, grab PCRE build defaults + int pcreNewlineSetting; + pcre16_config(PCRE_CONFIG_NEWLINE, &pcreNewlineSetting); + switch (pcreNewlineSetting) { + case 13: + patternNewlineSetting = PCRE_NEWLINE_CR; break; + case 10: + patternNewlineSetting = PCRE_NEWLINE_LF; break; + case 3338: // (13<<8 | 10) + patternNewlineSetting = PCRE_NEWLINE_CRLF; break; + case -2: + patternNewlineSetting = PCRE_NEWLINE_ANYCRLF; break; + case -1: + patternNewlineSetting = PCRE_NEWLINE_ANY; break; + default: + qWarning("QRegularExpressionPrivate::compilePattern(): " + "PCRE_CONFIG_NEWLINE returned an unknown newline"); + break; + } + } + + usingCrLfNewlines = (patternNewlineSetting == PCRE_NEWLINE_CRLF) || + (patternNewlineSetting == PCRE_NEWLINE_ANY) || + (patternNewlineSetting == PCRE_NEWLINE_ANYCRLF); +} + +/*! + \internal +*/ +void QRegularExpressionPrivate::optimizePattern() +{ + Q_ASSERT(compiledPattern); + + QMutexLocker lock(&mutex); + + if (studyData || (++usedCount != OPTIMIZE_AFTER_USE_COUNT)) + return; + + int studyOptions = PCRE_STUDY_JIT_COMPILE; + const char *err; + studyData = pcre16_study(compiledPattern, studyOptions, &err); + + if (!studyData && err) + qWarning("QRegularExpressionPrivate::optimizePattern(): pcre_study failed: %s", err); +} + +/*! + \internal + + Returns the capturing group number for the given name. Duplicated names for + capturing groups are not supported. +*/ +int QRegularExpressionPrivate::captureIndexForName(const QString &name) const +{ + Q_ASSERT(!name.isEmpty()); + + int index = pcre16_get_stringnumber(compiledPattern, name.utf16()); + if (index >= 0) + return index; + + return -1; +} + +/*! + \internal + + Performs a match of type \a matchType on the given \a subject string with + options \a matchOptions and returns the QRegularExpressionMatchPrivate of + the result. It also advances a match if a previous result is given as \a + previous. + + Advancing a match is a tricky algorithm. If the previous match matched a + non-empty string, we just do an ordinary match at the offset position. + + If the previous match matched an empty string, then an anchored, non-empty + match is attempted at the offset position. If that succeeds, then we got + the next match and we can return it. Otherwise, we advance by 1 position + (which can be one or two code units in UTF-16!) and reattempt a "normal" + match. We also have the problem of detecting the current newline format: if + the new advanced offset is pointing to the beginning of a CRLF sequence, we + must advance over it. +*/ +QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString &subject, + int offset, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + const QRegularExpressionMatchPrivate *previous) const +{ + if (offset < 0) + offset += subject.length(); + + QRegularExpression re(*const_cast(this)); + + if (offset < 0 || offset > subject.length()) + return new QRegularExpressionMatchPrivate(re, subject, matchType, matchOptions, 0); + + if (!compiledPattern) { + qWarning("QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); + return new QRegularExpressionMatchPrivate(re, subject, matchType, matchOptions, 0); + } + + QRegularExpressionMatchPrivate *priv = new QRegularExpressionMatchPrivate(re, subject, + matchType, matchOptions, + capturingCount); + + // this is mutex protected + const_cast(this)->optimizePattern(); + + int pcreOptions = convertToPcreOptions(matchOptions); + + if (matchType == QRegularExpression::PartialPreferCompleteMatch) + pcreOptions |= PCRE_PARTIAL_SOFT; + else if (matchType == QRegularExpression::PartialPreferFirstMatch) + pcreOptions |= PCRE_PARTIAL_HARD; + + bool previousMatchWasEmpty = false; + if (previous && previous->hasMatch && + (previous->capturedOffsets.at(0) == previous->capturedOffsets.at(1))) { + previousMatchWasEmpty = true; + } + + int * const captureOffsets = priv->capturedOffsets.data(); + const int captureOffsetsCount = priv->capturedOffsets.size(); + + const unsigned short * const subjectUtf16 = subject.utf16(); + const int subjectLength = subject.length(); + + int result; + + if (!previousMatchWasEmpty) { + result = pcre16_exec(compiledPattern, studyData, + subjectUtf16, subjectLength, + offset, pcreOptions, + captureOffsets, captureOffsetsCount); + } else { + result = pcre16_exec(compiledPattern, studyData, + subjectUtf16, subjectLength, + offset, pcreOptions | PCRE_NOTEMPTY_ATSTART | PCRE_ANCHORED, + captureOffsets, captureOffsetsCount); + + if (result == PCRE_ERROR_NOMATCH) { + ++offset; + + if (usingCrLfNewlines + && offset < subjectLength + && subjectUtf16[offset - 1] == QLatin1Char('\r') + && subjectUtf16[offset] == QLatin1Char('\n')) { + ++offset; + } else if (offset < subjectLength + && QChar::isLowSurrogate(subjectUtf16[offset])) { + ++offset; + } + + result = pcre16_exec(compiledPattern, studyData, + subjectUtf16, subjectLength, + offset, pcreOptions, + captureOffsets, captureOffsetsCount); + } + } + +#ifdef QREGULAREXPRESSION_DEBUG + qDebug() << "Matching" << pattern << "against" << subject + << offset << matchType << matchOptions << previousMatchWasEmpty + << "result" << result; +#endif + + // result == 0 means not enough space in captureOffsets; should never happen + Q_ASSERT(result != 0); + + if (result > 0) { + // full match + priv->isValid = true; + priv->hasMatch = true; + priv->capturedCount = result; + priv->capturedOffsets.resize(result * 2); + } else { + // no match, partial match or error + priv->hasPartialMatch = (result == PCRE_ERROR_PARTIAL); + priv->isValid = (result == PCRE_ERROR_NOMATCH || result == PCRE_ERROR_PARTIAL); + + if (result == PCRE_ERROR_PARTIAL) { + // partial match: + // leave the start and end capture offsets (i.e. cap(0)) + priv->capturedCount = 1; + priv->capturedOffsets.resize(2); + } else { + // no match or error + priv->capturedCount = 0; + priv->capturedOffsets.clear(); + } + } + + return priv; +} + +/*! + \internal +*/ +QRegularExpressionMatchPrivate::QRegularExpressionMatchPrivate(const QRegularExpression &re, + const QString &subject, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + int capturingCount) + : regularExpression(re), subject(subject), + matchType(matchType), matchOptions(matchOptions), + capturedCount(0), + hasMatch(false), hasPartialMatch(false), isValid(false) +{ + Q_ASSERT(capturingCount >= 0); + const int captureOffsetsCount = (capturingCount + 1) * 3; + capturedOffsets.resize(captureOffsetsCount); +} + + +/*! + \internal +*/ +QRegularExpressionMatch QRegularExpressionMatchPrivate::nextMatch() const +{ + Q_ASSERT(isValid); + Q_ASSERT(hasMatch || hasPartialMatch); + + QRegularExpressionMatchPrivate *nextPrivate = regularExpression.d->doMatch(subject, + capturedOffsets.at(1), + matchType, + matchOptions, + this); + return QRegularExpressionMatch(*nextPrivate); +} + +/*! + \internal +*/ +QRegularExpressionMatchIteratorPrivate::QRegularExpressionMatchIteratorPrivate(const QRegularExpression re, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + const QRegularExpressionMatch &next) + : next(next), + regularExpression(re), + matchType(matchType), matchOptions(matchOptions) +{ +} + +/*! + \internal +*/ +bool QRegularExpressionMatchIteratorPrivate::hasNext() const +{ + return next.isValid() && (next.hasMatch() || next.hasPartialMatch()); +} + +// PUBLIC API + +/*! + Constructs a QRegularExpression object with an empty pattern and no pattern + options. + + \sa setPattern(), setPatternOptions() +*/ +QRegularExpression::QRegularExpression() + : d(new QRegularExpressionPrivate) +{ +} + +/*! + Constructs a QRegularExpression object using the given \a pattern as + pattern and the \a options as the pattern options. + + \sa setPattern(), setPatternOptions() +*/ +QRegularExpression::QRegularExpression(const QString &pattern, PatternOptions options) + : d(new QRegularExpressionPrivate) +{ + d->pattern = pattern; + d->patternOptions = options; +} + +/*! + Constructs a QRegularExpression object as a copy of \a re. + + \sa operator=() +*/ +QRegularExpression::QRegularExpression(const QRegularExpression &re) + : d(re.d) +{ +} + +/*! + Destroys the QRegularExpression object. +*/ +QRegularExpression::~QRegularExpression() +{ +} + +/*! + Assigns the regular expression \a re to this object, and returns a reference + to the copy. Both the pattern and the pattern options are copied. +*/ +QRegularExpression &QRegularExpression::operator=(const QRegularExpression &re) +{ + d = re.d; + return *this; +} + +/*! + \fn void QRegularExpression::swap(QRegularExpression &other) + + Swaps the regular expression \a other with this regular expression. This + operation is very fast and never fails. +*/ + +/*! + Returns the pattern string of the regular expression. + + \sa setPattern(), patternOptions() +*/ +QString QRegularExpression::pattern() const +{ + return d->pattern; +} + +/*! + Sets the pattern string of the regular expression to \a pattern. The + pattern options are left unchanged. + + \sa pattern(), setPatternOptions() +*/ +void QRegularExpression::setPattern(const QString &pattern) +{ + d.detach(); + d->isDirty = true; + d->pattern = pattern; +} + +/*! + Returns the pattern options for the regular expression. + + \sa setPatternOptions(), pattern() +*/ +QRegularExpression::PatternOptions QRegularExpression::patternOptions() const +{ + return d->patternOptions; +} + +/*! + Sets the given \a options as the pattern options of the regular expression. + The pattern string is left unchanged. + + \sa patternOptions(), setPattern() +*/ +void QRegularExpression::setPatternOptions(PatternOptions options) +{ + d.detach(); + d->isDirty = true; + d->patternOptions = options; +} + +/*! + Returns true if the regular expression is a valid regular expression (that + is, it contains no syntax errors, etc.), or false otherwise. Use + errorString() to obtain a textual description of the error. + + \sa errorString(), patternErrorOffset() +*/ +bool QRegularExpression::isValid() const +{ + d.data()->compilePattern(); + return d->compiledPattern; +} + +/*! + Returns a textual description of the error found when checking the validity + of the regular expression, or "no error" if no error was found. + + \sa isValid(), patternErrorOffset() +*/ +QString QRegularExpression::errorString() const +{ + d.data()->compilePattern(); + if (d->errorString) + return QCoreApplication::translate("QRegularExpression", d->errorString, 0, QCoreApplication::UnicodeUTF8); + return QCoreApplication::translate("QRegularExpression", "no error", 0, QCoreApplication::UnicodeUTF8); +} + +/*! + Returns the offset, inside the pattern string, at which an error was found + when checking the validity of the regular expression. If no error was + found, then -1 is returned. + + \sa pattern(), isValid(), errorString() +*/ +int QRegularExpression::patternErrorOffset() const +{ + d.data()->compilePattern(); + return d->errorOffset; +} + +/*! + Attempts to match the regular expression against the given \a subject + string, starting at the position \a offset inside the subject, using a + match of type \a matchType and honoring the given \a matchOptions. + + The returned QRegularExpressionMatch object contains the results of the + match. + + \sa QRegularExpressionMatch, {normal matching} +*/ +QRegularExpressionMatch QRegularExpression::match(const QString &subject, + int offset, + MatchType matchType, + MatchOptions matchOptions) const +{ + d.data()->compilePattern(); + + QRegularExpressionMatchPrivate *priv = d->doMatch(subject, offset, matchType, matchOptions); + return QRegularExpressionMatch(*priv); +} + +/*! + Attempts to perform a global match of the regular expression against the + given \a subject string, starting at the position \a offset inside the + subject, using a match of type \a matchType and honoring the given \a + matchOptions. + + The returned QRegularExpressionMatchIterator is positioned before the + first match result (if any). + + \sa QRegularExpressionMatchIterator, {global matching} +*/ +QRegularExpressionMatchIterator QRegularExpression::globalMatch(const QString &subject, + int offset, + MatchType matchType, + MatchOptions matchOptions) const +{ + QRegularExpressionMatchIteratorPrivate *priv = + new QRegularExpressionMatchIteratorPrivate(*this, + matchType, + matchOptions, + match(subject, offset, matchType, matchOptions)); + + return QRegularExpressionMatchIterator(*priv); +} + +/*! + Returns true if the regular expression is equal to \a re, or false + otherwise. Two QRegularExpression objects are equal if they have + the same pattern string and the same pattern options. + + \sa operator!=() +*/ +bool QRegularExpression::operator==(const QRegularExpression &re) const +{ + return (pattern() == re.pattern() && patternOptions() == re.patternOptions()); +} + +/*! + \fn bool QRegularExpression::operator!=(const QRegularExpression &re) const + + Returns true if the regular expression is different from \a re, or + false otherwise. + + \sa operator==() +*/ + +/*! + Escapes all characters of \a str so that they no longer have any special + meaning when used as a regular expression pattern string, and returns + the escaped string. For instance: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 26 + + This is very convenient in order to build patterns from arbitrary strings: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 27 + + \note This function implements Perl's quotemeta algorithm and escapes with + a backslash all characters in \a str, except for the characters in the + \c{[A-Z]}, \c{[a-z]} and \c{[0-9]} ranges, as well as the underscore + (\c{_}) character. The only difference with Perl is that a literal NUL + inside \a str is escaped with the sequence \c{"\\\\0"} (backslash + + \c{'0'}), instead of \c{"\\\\\\0"} (backslash + \c{NUL}). +*/ +QString QRegularExpression::escape(const QString &str) +{ + QString result; + const int count = str.size(); + result.reserve(count * 2); + + // everything but [a-zA-Z0-9_] gets escaped, + // cf. perldoc -f quotemeta + for (int i = 0; i < count; ++i) { + const QChar current = str.at(i); + + if (current == QChar::Null) { + // unlike Perl, a literal NUL must be escaped with + // "\\0" (backslash + 0) and not "\\\0" (backslash + NUL), + // because pcre16_compile uses a NUL-terminated string + result.append(QLatin1Char('\\')); + result.append(QLatin1Char('0')); + } else if ( (current < QLatin1Char('a') || current > QLatin1Char('z')) && + (current < QLatin1Char('A') || current > QLatin1Char('Z')) && + (current < QLatin1Char('0') || current > QLatin1Char('9')) && + current != QLatin1Char('_') ) + { + result.append(QLatin1Char('\\')); + result.append(current); + if (current.isHighSurrogate() && i < (count - 1)) + result.append(str.at(++i)); + } else { + result.append(current); + } + } + + result.squeeze(); + return result; +} + +/*! + Destroys the match result. +*/ +QRegularExpressionMatch::~QRegularExpressionMatch() +{ +} + +/*! + Constructs a match result by copying the result of the given \a match. + + \sa operator=() +*/ +QRegularExpressionMatch::QRegularExpressionMatch(const QRegularExpressionMatch &match) + : d(match.d) +{ +} + +/*! + Assigns the match result \a match to this object, and returns a reference + to the copy. +*/ +QRegularExpressionMatch &QRegularExpressionMatch::operator=(const QRegularExpressionMatch &match) +{ + d = match.d; + return *this; +} + +/*! + \fn void QRegularExpressionMatch::swap(QRegularExpressionMatch &other) + + Swaps the match result \a other with this match result. This + operation is very fast and never fails. +*/ + +/*! + \internal +*/ +QRegularExpressionMatch::QRegularExpressionMatch(QRegularExpressionMatchPrivate &dd) + : d(&dd) +{ +} + +/*! + Returns the QRegularExpression object whose match() function returned this + object. + + \sa QRegularExpression::match(), matchType(), matchOptions() +*/ +QRegularExpression QRegularExpressionMatch::regularExpression() const +{ + return d->regularExpression; +} + + +/*! + Returns the match type that was used to get this QRegularExpressionMatch + object, that is, the match type that was passed to + QRegularExpression::match() or QRegularExpression::globalMatch(). + + \sa QRegularExpression::match(), regularExpression(), matchOptions() +*/ +QRegularExpression::MatchType QRegularExpressionMatch::matchType() const +{ + return d->matchType; +} + +/*! + Returns the match options that were used to get this + QRegularExpressionMatch object, that is, the match options that were passed + to QRegularExpression::match() or QRegularExpression::globalMatch(). + + \sa QRegularExpression::match(), regularExpression(), matchType() +*/ +QRegularExpression::MatchOptions QRegularExpressionMatch::matchOptions() const +{ + return d->matchOptions; +} + +/*! + Returns the index of the last capturing group that captured something, + including the implicit capturing group 0. This can be used to extract all + the substrings that were captured: + + \snippet doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp 28 + + Note that some of the capturing groups with an index less than + lastCapturedIndex() could have not matched, and therefore captured nothing. + + If the regular expression did not match, this function returns -1. + + \sa captured(), capturedStart(), capturedEnd(), capturedLength() +*/ +int QRegularExpressionMatch::lastCapturedIndex() const +{ + return d->capturedCount - 1; +} + +/*! + Returns the substring captured by the \a nth capturing group. If the \a nth + capturing group did not capture a string or doesn't exist, returns a null + QString. + + \sa capturedRef(), lastCapturedIndex(), capturedStart(), capturedEnd(), + capturedLength(), QString::isNull() +*/ +QString QRegularExpressionMatch::captured(int nth) const +{ + if (nth < 0 || nth > lastCapturedIndex()) + return QString(); + + int start = capturedStart(nth); + + if (start == -1) // didn't capture + return QString(); + + return d->subject.mid(start, capturedLength(nth)); +} + +/*! + Returns a reference to the substring captured by the \a nth capturing group. + If the \a nth capturing group did not capture a string or doesn't exist, + returns a null QStringRef. + + \sa captured(), lastCapturedIndex(), capturedStart(), capturedEnd(), + capturedLength(), QStringRef::isNull() +*/ +QStringRef QRegularExpressionMatch::capturedRef(int nth) const +{ + if (nth < 0 || nth > lastCapturedIndex()) + return QStringRef(); + + int start = capturedStart(nth); + + if (start == -1) // didn't capture + return QStringRef(); + + return d->subject.midRef(start, capturedLength(nth)); +} + +/*! + Returns the substring captured by the capturing group named \a name. If the + capturing group named \a name did not capture a string or doesn't exist, + returns a null QString. + + \sa capturedRef(), capturedStart(), capturedEnd(), capturedLength(), + QString::isNull() +*/ +QString QRegularExpressionMatch::captured(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("QRegularExpressionMatch::captured: empty capturing group name passed"); + return QString(); + } + int nth = d->regularExpression.d->captureIndexForName(name); + if (nth == -1) + return QString(); + return captured(nth); +} + +/*! + Returns a reference to the string captured by the capturing group named \a + name. If the capturing group named \a name did not capture a string or + doesn't exist, returns a null QStringRef. + + \sa captured(), capturedStart(), capturedEnd(), capturedLength(), + QStringRef::isNull() +*/ +QStringRef QRegularExpressionMatch::capturedRef(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("QRegularExpressionMatch::capturedRef: empty capturing group name passed"); + return QStringRef(); + } + int nth = d->regularExpression.d->captureIndexForName(name); + if (nth == -1) + return QStringRef(); + return capturedRef(nth); +} + +/*! + Returns a list of all strings captured by capturing groups, in the order + the groups themselves appear in the pattern string. +*/ +QStringList QRegularExpressionMatch::capturedTexts() const +{ + QStringList texts; + for (int i = 0; i <= lastCapturedIndex(); ++i) + texts << captured(i); + return texts; +} + +/*! + Returns the offset inside the subject string corresponding to the + starting position of the substring captured by the \a nth capturing group. + If the \a nth capturing group did not capture a string or doesn't exist, + returns -1. + + \sa capturedEnd(), capturedLength(), captured() +*/ +int QRegularExpressionMatch::capturedStart(int nth) const +{ + if (nth < 0 || nth > lastCapturedIndex()) + return -1; + + return d->capturedOffsets.at(nth * 2); +} + +/*! + Returns the length of the substring captured by the \a nth capturing group. + + \note This function returns 0 if the \a nth capturing group did not capture + a string or doesn't exist. + + \sa capturedStart(), capturedEnd(), captured() +*/ +int QRegularExpressionMatch::capturedLength(int nth) const +{ + // bound checking performed by these two functions + return capturedEnd(nth) - capturedStart(nth); +} + +/*! + Returns the offset inside the subject string immediately after the ending + position of the substring captured by the \a nth capturing group. If the \a + nth capturing group did not capture a string or doesn't exist, returns -1. + + \sa capturedStart(), capturedLength(), captured() +*/ +int QRegularExpressionMatch::capturedEnd(int nth) const +{ + if (nth < 0 || nth > lastCapturedIndex()) + return -1; + + return d->capturedOffsets.at(nth * 2 + 1); +} + +/*! + Returns the offset inside the subject string corresponding to the starting + position of the substring captured by the capturing group named \a name. + If the capturing group named \a name did not capture a string or doesn't + exist, returns -1. + + \sa capturedEnd(), capturedLength(), captured() +*/ +int QRegularExpressionMatch::capturedStart(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("QRegularExpressionMatch::capturedStart: empty capturing group name passed"); + return -1; + } + int nth = d->regularExpression.d->captureIndexForName(name); + if (nth == -1) + return -1; + return capturedStart(nth); +} + +/*! + Returns the offset inside the subject string corresponding to the starting + position of the substring captured by the capturing group named \a name. + + \note This function returns 0 if the capturing group named \a name did not + capture a string or doesn't exist. + + \sa capturedStart(), capturedEnd(), captured() +*/ +int QRegularExpressionMatch::capturedLength(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("QRegularExpressionMatch::capturedLength: empty capturing group name passed"); + return 0; + } + int nth = d->regularExpression.d->captureIndexForName(name); + if (nth == -1) + return 0; + return capturedLength(nth); +} + +/*! + Returns the offset inside the subject string immediately after the ending + position of the substring captured by the capturing group named \a name. If + the capturing group named \a name did not capture a string or doesn't + exist, returns -1. + + \sa capturedStart(), capturedLength(), captured() +*/ +int QRegularExpressionMatch::capturedEnd(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("QRegularExpressionMatch::capturedEnd: empty capturing group name passed"); + return -1; + } + int nth = d->regularExpression.d->captureIndexForName(name); + if (nth == -1) + return -1; + return capturedEnd(nth); +} + +/*! + Returns true if the regular expression matched against the subject string, + or false otherwise. + + \sa QRegularExpression::match(), hasPartialMatch() +*/ +bool QRegularExpressionMatch::hasMatch() const +{ + return d->hasMatch; +} + +/*! + Returns true if the regular expression partially matched against the + subject string, or false otherwise. + + \note Only a match that explicitely used the one of the partial match types + can yield a partial match. Still, if such a match succeeds totally, this + function will return false, while hasMatch() will return true. + + \sa QRegularExpression::match(), QRegularExpression::MatchType, hasMatch() +*/ +bool QRegularExpressionMatch::hasPartialMatch() const +{ + return d->hasPartialMatch; +} + +/*! + Returns true if the match object was obtained as a result from the + QRegularExpression::match() function invoked on a valid QRegularExpression + object; returns false if the QRegularExpression was invalid. + + \sa QRegularExpression::match(), QRegularExpression::isValid() +*/ +bool QRegularExpressionMatch::isValid() const +{ + return d->isValid; +} + +/*! + \internal +*/ +QRegularExpressionMatchIterator::QRegularExpressionMatchIterator(QRegularExpressionMatchIteratorPrivate &dd) + : d(&dd) +{ +} + +/*! + Destroys the QRegularExpressionMatchIterator object. +*/ +QRegularExpressionMatchIterator::~QRegularExpressionMatchIterator() +{ +} + +/*! + Constructs a QRegularExpressionMatchIterator object as a copy of \a + iterator. + + \sa operator=() +*/ +QRegularExpressionMatchIterator::QRegularExpressionMatchIterator(const QRegularExpressionMatchIterator &iterator) + : d(iterator.d) +{ +} + +/*! + Assigns the iterator \a iterator to this object, and returns a reference to + the copy. +*/ +QRegularExpressionMatchIterator &QRegularExpressionMatchIterator::operator=(const QRegularExpressionMatchIterator &iterator) +{ + d = iterator.d; + return *this; +} + +/*! + \fn void QRegularExpressionMatchIterator::swap(QRegularExpressionMatchIterator &other) + + Swaps the iterator \a other with this iterator object. This operation is + very fast and never fails. +*/ + +/*! + Returns true if the iterator object was obtained as a result from the + QRegularExpression::globalMatch() function invoked on a valid + QRegularExpression object; returns false if the QRegularExpression was + invalid. + + \sa QRegularExpression::globalMatch(), QRegularExpression::isValid() +*/ +bool QRegularExpressionMatchIterator::isValid() const +{ + return d->next.isValid(); +} + +/*! + Returns true if there is at least one match result ahead of the iterator; + otherwise it returns false. + + \sa next() +*/ +bool QRegularExpressionMatchIterator::hasNext() const +{ + return d->hasNext(); +} + +/*! + Returns the next match result without moving the iterator. + + \note Calling this function when the iterator is at the end of the result + set leads to undefined results. +*/ +QRegularExpressionMatch QRegularExpressionMatchIterator::peekNext() const +{ + if (!hasNext()) + qWarning("QRegularExpressionMatchIterator::peekNext() called on an iterator already at end"); + + return d->next; +} + +/*! + Returns the next match result and advances the iterator by one position. + + \note Calling this function when the iterator is at the end of the result + set leads to undefined results. +*/ +QRegularExpressionMatch QRegularExpressionMatchIterator::next() +{ + if (!hasNext()) { + qWarning("QRegularExpressionMatchIterator::next() called on an iterator already at end"); + return d->next; + } + + QRegularExpressionMatch current = d->next; + d->next = d->next.d.constData()->nextMatch(); + return current; +} + +/*! + Returns the QRegularExpression object whose globalMatch() function returned + this object. + + \sa QRegularExpression::globalMatch(), matchType(), matchOptions() +*/ +QRegularExpression QRegularExpressionMatchIterator::regularExpression() const +{ + return d->regularExpression; +} + +/*! + Returns the match type that was used to get this + QRegularExpressionMatchIterator object, that is, the match type that was + passed to QRegularExpression::globalMatch(). + + \sa QRegularExpression::globalMatch(), regularExpression(), matchOptions() +*/ +QRegularExpression::MatchType QRegularExpressionMatchIterator::matchType() const +{ + return d->matchType; +} + +/*! + Returns the match options that were used to get this + QRegularExpressionMatchIterator object, that is, the match options that + were passed to QRegularExpression::globalMatch(). + + \sa QRegularExpression::globalMatch(), regularExpression(), matchType() +*/ +QRegularExpression::MatchOptions QRegularExpressionMatchIterator::matchOptions() const +{ + return d->matchOptions; +} + +#ifndef QT_NO_DATASTREAM +/*! + \relates QRegularExpression + + Writes the regular expression \a re to stream \a out. + + \sa {Serializing Qt Data Types} +*/ +QDataStream &operator<<(QDataStream &out, const QRegularExpression &re) +{ + out << re.pattern() << quint32(re.patternOptions()); + return out; +} + +/*! + \relates QRegularExpression + + Reads a regular expression from stream \a in into \a re. + + \sa {Serializing Qt Data Types} +*/ +QDataStream &operator>>(QDataStream &in, QRegularExpression &re) +{ + QString pattern; + quint32 patternOptions; + in >> pattern >> patternOptions; + re.setPattern(pattern); + re.setPatternOptions(QRegularExpression::PatternOptions(patternOptions)); + return in; +} +#endif + +#ifndef QT_NO_DEBUG_STREAM +/*! + \relates QRegularExpression + + Writes the regular expression \a re into the debug object \a debug for + debugging purposes. + + \sa {Debugging Techniques} +*/ +QDebug operator<<(QDebug debug, const QRegularExpression &re) +{ + debug.nospace() << "QRegularExpression(" << re.pattern() << ", " << re.patternOptions() << ")"; + return debug.space(); +} + +/*! + \relates QRegularExpressionMatch + + Writes the match object \a match into the debug object \a debug for + debugging purposes. + + \sa {Debugging Techniques} +*/ +QDebug operator<<(QDebug debug, const QRegularExpressionMatch &match) +{ + debug.nospace() << "QRegularExpressionMatch("; + + if (!match.isValid()) { + debug << "Invalid)"; + return debug.space(); + } + + debug << "Valid"; + + if (match.hasMatch()) { + debug << ", has match: "; + for (int i = 0; i <= match.lastCapturedIndex(); ++i) { + debug << i + << ":(" << match.capturedStart(i) << ", " << match.capturedEnd(i) + << ", " << match.captured(i) << ")"; + if (i < match.lastCapturedIndex()) + debug << ", "; + } + } else if (match.hasPartialMatch()) { + debug << ", has partial match: (" + << match.capturedStart(0) << ", " + << match.capturedEnd(0) << ", " + << match.captured(0) << ")"; + } else { + debug << ", no match"; + } + + debug << ")"; + + return debug.space(); +} +#endif + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qregularexpression.h b/src/corelib/tools/qregularexpression.h new file mode 100644 index 0000000000..c9bcb1e7ba --- /dev/null +++ b/src/corelib/tools/qregularexpression.h @@ -0,0 +1,245 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Giuseppe D'Angelo . +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREGULAREXPRESSION_H +#define QREGULAREXPRESSION_H + +#ifndef QT_NO_REGEXP + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QRegularExpressionMatch; +class QRegularExpressionMatchIterator; +struct QRegularExpressionPrivate; + +class Q_CORE_EXPORT QRegularExpression +{ +public: + enum PatternOption { + NoPatternOption = 0x0000, + CaseInsensitiveOption = 0x0001, + DotMatchesEverythingOption = 0x0002, + MultilineOption = 0x0004, + ExtendedPatternSyntaxOption = 0x0008, + InvertedGreedinessOption = 0x0010, + DontCaptureOption = 0x0020, + UseUnicodePropertiesOption = 0x0040 + }; + Q_DECLARE_FLAGS(PatternOptions, PatternOption) + + PatternOptions patternOptions() const; + void setPatternOptions(PatternOptions options); + + QRegularExpression(); + explicit QRegularExpression(const QString &pattern, PatternOptions options = NoPatternOption); + QRegularExpression(const QRegularExpression &re); + ~QRegularExpression(); + QRegularExpression &operator=(const QRegularExpression &re); + +#ifdef Q_COMPILER_RVALUE_REFS + inline QRegularExpression &operator=(QRegularExpression &&re) + { d.swap(re.d); return *this; } +#endif + + inline void swap(QRegularExpression &re) { d.swap(re.d); } + + QString pattern() const; + void setPattern(const QString &pattern); + + bool isValid() const; + int patternErrorOffset() const; + QString errorString() const; + + enum MatchType { + NormalMatch = 0, + PartialPreferCompleteMatch, + PartialPreferFirstMatch + }; + + enum MatchOption { + NoMatchOption = 0x0000, + AnchoredMatchOption = 0x0001 + }; + Q_DECLARE_FLAGS(MatchOptions, MatchOption) + + QRegularExpressionMatch match(const QString &subject, + int offset = 0, + MatchType matchType = NormalMatch, + MatchOptions matchOptions = NoMatchOption) const; + + QRegularExpressionMatchIterator globalMatch(const QString &subject, + int offset = 0, + MatchType matchType = NormalMatch, + MatchOptions matchOptions = NoMatchOption) const; + + static QString escape(const QString &str); + + bool operator==(const QRegularExpression &re) const; + inline bool operator!=(const QRegularExpression &re) const { return !operator==(re); } + +private: + friend struct QRegularExpressionPrivate; + friend class QRegularExpressionMatch; + friend struct QRegularExpressionMatchPrivate; + friend class QRegularExpressionMatchIterator; + + QRegularExpression(QRegularExpressionPrivate &dd); + QExplicitlySharedDataPointer d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QRegularExpression::PatternOptions) +Q_DECLARE_OPERATORS_FOR_FLAGS(QRegularExpression::MatchOptions) +Q_DECLARE_TYPEINFO(QRegularExpression, Q_MOVABLE_TYPE); + +#ifndef QT_NO_DATASTREAM +Q_CORE_EXPORT QDataStream &operator<<(QDataStream &out, const QRegularExpression &re); +Q_CORE_EXPORT QDataStream &operator>>(QDataStream &in, QRegularExpression &re); +#endif + +#ifndef QT_NO_DEBUG_STREAM +Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QRegularExpression &re); +#endif + +struct QRegularExpressionMatchPrivate; + +class Q_CORE_EXPORT QRegularExpressionMatch +{ +public: + ~QRegularExpressionMatch(); + QRegularExpressionMatch(const QRegularExpressionMatch &match); + QRegularExpressionMatch &operator=(const QRegularExpressionMatch &match); + +#ifdef Q_COMPILER_RVALUE_REFS + inline QRegularExpressionMatch &operator=(QRegularExpressionMatch &&match) + { d.swap(match.d); return *this; } +#endif + inline void swap(QRegularExpressionMatch &match) { d.swap(match.d); } + + QRegularExpression regularExpression() const; + QRegularExpression::MatchType matchType() const; + QRegularExpression::MatchOptions matchOptions() const; + + bool hasMatch() const; + bool hasPartialMatch() const; + + bool isValid() const; + + int lastCapturedIndex() const; + + QString captured(int nth = 0) const; + QStringRef capturedRef(int nth = 0) const; + + QString captured(const QString &name) const; + QStringRef capturedRef(const QString &name) const; + + QStringList capturedTexts() const; + + int capturedStart(int nth = 0) const; + int capturedLength(int nth = 0) const; + int capturedEnd(int nth = 0) const; + + int capturedStart(const QString &name) const; + int capturedLength(const QString &name) const; + int capturedEnd(const QString &name) const; + +private: + friend class QRegularExpression; + friend struct QRegularExpressionMatchPrivate; + friend class QRegularExpressionMatchIterator; + + QRegularExpressionMatch(QRegularExpressionMatchPrivate &dd); + QSharedDataPointer d; +}; + +Q_DECLARE_TYPEINFO(QRegularExpressionMatch, Q_MOVABLE_TYPE); + +#ifndef QT_NO_DEBUG_STREAM +Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QRegularExpressionMatch &match); +#endif + +struct QRegularExpressionMatchIteratorPrivate; + +class Q_CORE_EXPORT QRegularExpressionMatchIterator +{ +public: + ~QRegularExpressionMatchIterator(); + QRegularExpressionMatchIterator(const QRegularExpressionMatchIterator &iterator); + QRegularExpressionMatchIterator &operator=(const QRegularExpressionMatchIterator &iterator); +#ifdef Q_COMPILER_RVALUE_REFS + inline QRegularExpressionMatchIterator &operator=(QRegularExpressionMatchIterator &&iterator) + { d.swap(iterator.d); return *this; } +#endif + void swap(QRegularExpressionMatchIterator &iterator) { d.swap(iterator.d); } + + bool isValid() const; + + bool hasNext() const; + QRegularExpressionMatch next(); + QRegularExpressionMatch peekNext() const; + + QRegularExpression regularExpression() const; + QRegularExpression::MatchType matchType() const; + QRegularExpression::MatchOptions matchOptions() const; + +private: + friend class QRegularExpression; + + QRegularExpressionMatchIterator(QRegularExpressionMatchIteratorPrivate &dd); + QSharedDataPointer d; +}; + +Q_DECLARE_TYPEINFO(QRegularExpressionMatchIterator, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QRegularExpression) + +QT_END_HEADER + +#endif // QT_NO_REGEXP + +#endif // QREGULAREXPRESSION_H diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 3740975b12..250789a969 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -30,6 +30,7 @@ HEADERS += \ tools/qqueue.h \ tools/qrect.h \ tools/qregexp.h \ + tools/qregularexpression.h \ tools/qringbuffer_p.h \ tools/qrefcount.h \ tools/qscopedpointer.h \ @@ -75,6 +76,7 @@ SOURCES += \ tools/qcontiguouscache.cpp \ tools/qrect.cpp \ tools/qregexp.cpp \ + tools/qregularexpression.cpp \ tools/qrefcount.cpp \ tools/qshareddata.cpp \ tools/qsharedpointer.cpp \ @@ -105,6 +107,12 @@ contains(QT_CONFIG,icu) { DEFINES += QT_USE_ICU } +pcre { + include($$PWD/../../3rdparty/pcre.pri) +} else { + LIBS_PRIVATE += -lpcre16 +} + DEFINES += HB_EXPORT=Q_CORE_EXPORT INCLUDEPATH += ../3rdparty/harfbuzz/src HEADERS += ../3rdparty/harfbuzz/src/harfbuzz.h -- cgit v1.2.3 From aea65cbaa4fd889129d7945600dad3277f9c6d9b Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Tue, 7 Feb 2012 23:56:40 +0000 Subject: QRegularExpression: QDebug support for pattern options Added the proper QDebug operator to debug the QRegularExpression::PatternOptions flags. Change-Id: Icd00e93a0c6cc4345db528d494fc176624f7b7a2 Reviewed-by: hjk Reviewed-by: Lars Knoll --- src/corelib/tools/qregularexpression.cpp | 37 ++++++++++++++++++++++++++++++++ src/corelib/tools/qregularexpression.h | 1 + 2 files changed, 38 insertions(+) (limited to 'src') diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 488a454aaa..7fbbfaa9ef 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -1976,6 +1976,43 @@ QDebug operator<<(QDebug debug, const QRegularExpression &re) return debug.space(); } +/*! + \relates QRegularExpression + + Writes the pattern options \a patternOptions into the debug object \a debug + for debugging purposes. + + \sa {Debugging Techniques} +*/ +QDebug operator<<(QDebug debug, QRegularExpression::PatternOptions patternOptions) +{ + QStringList flags; + + if (patternOptions == QRegularExpression::NoPatternOption) { + flags << QLatin1String("NoPatternOption"); + } else { + if (patternOptions & QRegularExpression::CaseInsensitiveOption) + flags << QLatin1String("CaseInsensitiveOption"); + if (patternOptions & QRegularExpression::DotMatchesEverythingOption) + flags << QLatin1String("DotMatchesEverythingOption"); + if (patternOptions & QRegularExpression::MultilineOption) + flags << QLatin1String("MultilineOption"); + if (patternOptions & QRegularExpression::ExtendedPatternSyntaxOption) + flags << QLatin1String("ExtendedPatternSyntaxOption"); + if (patternOptions & QRegularExpression::InvertedGreedinessOption) + flags << QLatin1String("InvertedGreedinessOption"); + if (patternOptions & QRegularExpression::DontCaptureOption) + flags << QLatin1String("DontCaptureOption"); + if (patternOptions & QRegularExpression::UseUnicodePropertiesOption) + flags << QLatin1String("UseUnicodePropertiesOption"); + } + + debug.nospace() << "QRegularExpression::PatternOptions(" + << qPrintable(flags.join(QLatin1String("|"))) + << ")"; + + return debug.space(); +} /*! \relates QRegularExpressionMatch diff --git a/src/corelib/tools/qregularexpression.h b/src/corelib/tools/qregularexpression.h index c9bcb1e7ba..13c7de7cab 100644 --- a/src/corelib/tools/qregularexpression.h +++ b/src/corelib/tools/qregularexpression.h @@ -142,6 +142,7 @@ Q_CORE_EXPORT QDataStream &operator>>(QDataStream &in, QRegularExpression &re); #ifndef QT_NO_DEBUG_STREAM Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QRegularExpression &re); +Q_CORE_EXPORT QDebug operator<<(QDebug debug, QRegularExpression::PatternOptions patternOptions); #endif struct QRegularExpressionMatchPrivate; -- cgit v1.2.3 From bd30234b59c1a0cef81b8ce43f2fefac1f28b318 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Wed, 8 Feb 2012 18:53:22 +0000 Subject: QRegularExpression: improve operator==, add dedicated autotest Trivial change: compare dpointers first, then the data. Added test function for operator==. Change-Id: I33ac64a59db4ccad56c30be17622187e42415f38 Reviewed-by: Lars Knoll --- src/corelib/tools/qregularexpression.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 7fbbfaa9ef..b7a5c3de8e 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -1395,7 +1395,8 @@ QRegularExpressionMatchIterator QRegularExpression::globalMatch(const QString &s */ bool QRegularExpression::operator==(const QRegularExpression &re) const { - return (pattern() == re.pattern() && patternOptions() == re.patternOptions()); + return (d == re.d) || + (d->pattern == re.d->pattern && d->patternOptions == re.d->patternOptions); } /*! -- cgit v1.2.3 From 1899861858eb262df7826eb32d1274233a35536e Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Wed, 8 Feb 2012 19:28:14 +0000 Subject: QRegularExpression: do not use JIT in debug builds PCRE's JIT uses self-modifying code extensively, requiring full SMC checks enabled by tools like valgrind, which slow down the execution considerably; not enabling SMC checks lead to crashes. Therefore, JIT is now disabled by default in debug builds of Qt. Its usage (both in debug and release builds) can be controlled by setting the QT_ENABLE_REGEXP_JIT environment variable. Change-Id: Ib38952400e4219582942ce65ab9edcd89c432f3e Reviewed-by: Lars Knoll --- src/corelib/tools/qregularexpression.cpp | 42 +++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index b7a5c3de8e..17988bdb31 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -509,6 +509,22 @@ QT_BEGIN_NAMESPACE QRegExp::CaretAtOffset behaviour. There is no equivalent for the other QRegExp::CaretMode modes. + \section1 Debugging code that uses QRegularExpression + + QRegularExpression internally uses a just in time compiler (JIT) to + optimize the execution of the matching algorithm. The JIT makes extensive + usage of self-modifying code, which can lead debugging tools such as + Valgrind to crash. You must enable all checks for self-modifying code if + you want to debug programs using QRegularExpression (f.i., see Valgrind's + \c{--smc-check} command line option). The downside of enabling such checks + is that your program will run considerably slower. + + To avoid that, the JIT is disabled by default if you compile Qt in debug + mode. It is possible to override the default and enable or disable the JIT + usage (both in debug or release mode) by setting the + \c{QT_ENABLE_REGEXP_JIT} environment variable to a non-zero or zero value + respectively. + \sa QRegularExpressionMatch, QRegularExpressionMatchIterator */ @@ -969,6 +985,25 @@ void QRegularExpressionPrivate::getPatternInfo() (patternNewlineSetting == PCRE_NEWLINE_ANYCRLF); } +/*! + \internal +*/ +static bool isJitEnabled() +{ + QByteArray jitEnvironment = qgetenv("QT_ENABLE_REGEXP_JIT"); + if (!jitEnvironment.isEmpty()) { + bool ok; + int enableJit = jitEnvironment.toInt(&ok); + return ok ? (enableJit != 0) : true; + } + +#ifdef QT_DEBUG + return false; +#else + return true; +#endif +} + /*! \internal */ @@ -981,7 +1016,12 @@ void QRegularExpressionPrivate::optimizePattern() if (studyData || (++usedCount != OPTIMIZE_AFTER_USE_COUNT)) return; - int studyOptions = PCRE_STUDY_JIT_COMPILE; + static const bool enableJit = isJitEnabled(); + + int studyOptions = 0; + if (enableJit) + studyOptions |= PCRE_STUDY_JIT_COMPILE; + const char *err; studyData = pcre16_study(compiledPattern, studyOptions, &err); -- cgit v1.2.3 From efcd4d9470781e3a0331afeab49cd00cee24399f Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Sun, 19 Feb 2012 23:56:50 +0100 Subject: QRegularExpression: add captureCount() QRegularExpression::captureCount() returns the number of capturing groups inside the regular expression pattern. Change-Id: Ib90ce67c67d06ab2966f0c98bd91da21defc156d Reviewed-by: Thiago Macieira --- src/corelib/tools/qregularexpression.cpp | 13 +++++++++++++ src/corelib/tools/qregularexpression.h | 2 ++ 2 files changed, 15 insertions(+) (limited to 'src') diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 17988bdb31..7bbac0144b 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -1340,6 +1340,19 @@ void QRegularExpression::setPatternOptions(PatternOptions options) d->patternOptions = options; } +/*! + Returns the number of capturing groups inside the pattern string, + or -1 if the regular expression is not valid. + + \sa isValid() +*/ +int QRegularExpression::captureCount() const +{ + if (!isValid()) // will compile the pattern + return -1; + return d->capturingCount; +} + /*! Returns true if the regular expression is a valid regular expression (that is, it contains no syntax errors, etc.), or false otherwise. Use diff --git a/src/corelib/tools/qregularexpression.h b/src/corelib/tools/qregularexpression.h index 13c7de7cab..3ca83c9e27 100644 --- a/src/corelib/tools/qregularexpression.h +++ b/src/corelib/tools/qregularexpression.h @@ -94,6 +94,8 @@ public: int patternErrorOffset() const; QString errorString() const; + int captureCount() const; + enum MatchType { NormalMatch = 0, PartialPreferCompleteMatch, -- cgit v1.2.3 From d7b720dd3ee7a095abe7e283d1bed881268b0f5e Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Sun, 19 Feb 2012 23:58:04 +0100 Subject: QRegularExpression: const correctness fixes Adding some const qualifiers to members which are never written. Change-Id: Ibb8953764c7b7790a419a5d48f2956751d5fc1f9 Reviewed-by: Thiago Macieira --- src/corelib/tools/qregularexpression.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 7bbac0144b..40b6b5a08e 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -817,14 +817,14 @@ struct QRegularExpressionMatchPrivate : QSharedData QRegularExpressionMatch nextMatch() const; - QRegularExpression regularExpression; - QString subject; + const QRegularExpression regularExpression; + const QString subject; // the capturedOffsets vector contains pairs of (start, end) positions // for each captured substring QVector capturedOffsets; - QRegularExpression::MatchType matchType; - QRegularExpression::MatchOptions matchOptions; + const QRegularExpression::MatchType matchType; + const QRegularExpression::MatchOptions matchOptions; int capturedCount; @@ -835,16 +835,16 @@ struct QRegularExpressionMatchPrivate : QSharedData struct QRegularExpressionMatchIteratorPrivate : QSharedData { - QRegularExpressionMatchIteratorPrivate(const QRegularExpression re, + QRegularExpressionMatchIteratorPrivate(const QRegularExpression &re, QRegularExpression::MatchType matchType, QRegularExpression::MatchOptions matchOptions, const QRegularExpressionMatch &next); bool hasNext() const; QRegularExpressionMatch next; - QRegularExpression regularExpression; - QRegularExpression::MatchType matchType; - QRegularExpression::MatchOptions matchOptions; + const QRegularExpression regularExpression; + const QRegularExpression::MatchType matchType; + const QRegularExpression::MatchOptions matchOptions; }; /*! @@ -1216,7 +1216,7 @@ QRegularExpressionMatch QRegularExpressionMatchPrivate::nextMatch() const /*! \internal */ -QRegularExpressionMatchIteratorPrivate::QRegularExpressionMatchIteratorPrivate(const QRegularExpression re, +QRegularExpressionMatchIteratorPrivate::QRegularExpressionMatchIteratorPrivate(const QRegularExpression &re, QRegularExpression::MatchType matchType, QRegularExpression::MatchOptions matchOptions, const QRegularExpressionMatch &next) -- cgit v1.2.3 From b979956ec46093e5668c2b264f9b68da3cbb0326 Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Wed, 15 Feb 2012 09:56:23 +0100 Subject: QSqlTableModel: handle changes between submit and select Once an insert has been submitted, the cached record behaves like an update. For row bookkeeping, we still have to remember that it was originally inserted and is not in the query rows. Between submitting a delete and selecting, we remove the values from the deleted record. This causes a blank row to be displayed. Read-only flag is set for cells in deleted row. Reverting between submit and select means going back to the last submitted values. When removing rows, it's better to process from highest row numbers to lowest. This avoids complications with higher rows shifting down when lower rows are removed. Change-Id: I8752fa11f7a1b88f2a71b9e03a020ac37e62487f Reviewed-by: Honglei Zhang --- src/sql/models/qsqltablemodel.cpp | 52 +++++++++++++++++++++++---------------- src/sql/models/qsqltablemodel_p.h | 47 +++++++++++++++++++++++++++++------ 2 files changed, 71 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 24668dd946..9ade5f14d5 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -87,7 +87,7 @@ int QSqlTableModelPrivate::insertCount(int maxRow) const for (; i != e && (maxRow < 0 || i.key() <= maxRow); ++i) { - if (i.value().op() == Insert) + if (i.value().insert()) ++cnt; } @@ -122,19 +122,17 @@ void QSqlTableModelPrivate::revertCachedRow(int row) Q_Q(QSqlTableModel); ModifiedRow r = cache.value(row); - // cannot revert a committed change - if (r.submitted()) - return; - switch (r.op()) { case QSqlTableModelPrivate::None: Q_ASSERT_X(false, "QSqlTableModelPrivate::revertCachedRow()", "Invalid entry in cache map"); return; case QSqlTableModelPrivate::Update: case QSqlTableModelPrivate::Delete: - cache.remove(row); - emit q->dataChanged(q->createIndex(row, 0), - q->createIndex(row, q->columnCount() - 1)); + if (!r.submitted()) { + cache[row].revert(); + emit q->dataChanged(q->createIndex(row, 0), + q->createIndex(row, q->columnCount() - 1)); + } break; case QSqlTableModelPrivate::Insert: { QMap::Iterator it = cache.find(row); @@ -373,7 +371,7 @@ bool QSqlTableModel::select() while (it != d->cache.constBegin()) { --it; // rows must be accounted for - if (it.value().op() == QSqlTableModelPrivate::Insert) { + if (it.value().insert()) { beginRemoveRows(QModelIndex(), it.key(), it.key()); it = d->cache.erase(it); endRemoveRows(); @@ -470,11 +468,14 @@ bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, in if (!index.isValid() || index.column() >= d->rec.count() || index.row() >= rowCount()) return false; + if (d->cache.value(index.row()).op() == QSqlTableModelPrivate::Delete) + return false; + if (d->strategy == OnFieldChange && d->cache.value(index.row()).op() != QSqlTableModelPrivate::Insert) { - d->cache.clear(); + revertAll(); } else if (d->strategy == OnRowChange && !d->cache.isEmpty() && !d->cache.contains(index.row())) { submit(); - d->cache.clear(); + revertAll(); } QSqlTableModelPrivate::ModifiedRow &row = d->cache[index.row()]; @@ -759,8 +760,10 @@ void QSqlTableModel::revertAll() { Q_D(QSqlTableModel); - while (!d->cache.isEmpty()) - revertRow(d->cache.constBegin().key()); + const QList rows(d->cache.keys()); + for (int i = rows.size() - 1; i >= 0; --i) { + revertRow(rows.value(i)); + } } /*! @@ -967,15 +970,17 @@ bool QSqlTableModel::removeRows(int row, int count, const QModelIndex &parent) else if (!count) return true; - for (int i = 0; i < count; ++i) { - int idx = row + i; - if (d->cache.value(idx).op() == QSqlTableModelPrivate::Insert) { + // Iterate backwards so we don't have to worry about removed rows causing + // higher cache entries to shift downwards. + for (int idx = row + count - 1; idx >= row; --idx) { + QSqlTableModelPrivate::ModifiedRow& mrow = d->cache[idx]; + if (mrow.op() == QSqlTableModelPrivate::Insert) { revertRow(idx); - // Reverting a row means all the other cache entries have been adjusted downwards - // so fake this by adjusting row - --row; } else { - d->cache[idx] = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Delete, record(idx)); + if (mrow.op() == QSqlTableModelPrivate::None) + mrow = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Delete, record(idx)); + else + mrow.setOp(QSqlTableModelPrivate::Delete); if (d->strategy == OnManualSubmit) emit headerDataChanged(Qt::Vertical, idx, idx); } @@ -1158,6 +1163,8 @@ Qt::ItemFlags QSqlTableModel::flags(const QModelIndex &index) const return 0; if (d->rec.field(index.column()).isReadOnly()) return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + if (d->cache.value(index.row()).op() == QSqlTableModelPrivate::Delete) + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; } @@ -1186,8 +1193,11 @@ bool QSqlTableModel::setRecord(int row, const QSqlRecord &values) if (row >= rowCount()) return false; + if (d->cache.value(row).op() == QSqlTableModelPrivate::Delete) + return false; + if (d->strategy == OnFieldChange && d->cache.value(row).op() != QSqlTableModelPrivate::Insert) - d->cache.clear(); + revertAll(); else if (d->strategy == OnRowChange && !d->cache.isEmpty() && !d->cache.contains(row)) submit(); diff --git a/src/sql/models/qsqltablemodel_p.h b/src/sql/models/qsqltablemodel_p.h index 57051a0e9c..ba2fdf5df3 100644 --- a/src/sql/models/qsqltablemodel_p.h +++ b/src/sql/models/qsqltablemodel_p.h @@ -101,18 +101,51 @@ public: { public: inline ModifiedRow(Op o = None, const QSqlRecord &r = QSqlRecord()) - : m_op(o), m_rec(r), m_submitted(false) - { init_rec(); } + : m_op(None), m_db_values(r), m_insert(o == Insert) + { setOp(o); } inline Op op() const { return m_op; } + inline void setOp(Op o) + { + if (o == m_op) + return; + m_submitted = (o != Insert && o != Delete); + m_op = o; + m_rec = m_db_values; + setGenerated(m_rec, m_op == Delete); + } inline QSqlRecord rec() const { return m_rec; } inline QSqlRecord& recRef() { return m_rec; } inline void setValue(int c, const QVariant &v) { + m_submitted = false; m_rec.setValue(c, v); m_rec.setGenerated(c, true); } inline bool submitted() const { return m_submitted; } - inline void setSubmitted() { m_submitted = true; } + inline void setSubmitted() + { + m_submitted = true; + setGenerated(m_rec, false); + if (m_op == Delete) { + m_rec.clearValues(); + } + else { + m_op = Update; + m_db_values = m_rec; + setGenerated(m_db_values, true); + } + } + inline bool insert() const { return m_insert; } + inline void revert() + { + if (m_submitted) + return; + if (m_op == Delete) + m_op = Update; + m_rec = m_db_values; + setGenerated(m_rec, false); + m_submitted = true; + } inline QSqlRecord primaryValues(const QSqlRecord& pi) const { if (m_op == None || m_op == Insert) @@ -126,16 +159,16 @@ public: return values; } private: - void init_rec() + inline static void setGenerated(QSqlRecord& r, bool g) { - for (int i = m_rec.count() - 1; i >= 0; --i) - m_rec.setGenerated(i, false); - m_db_values = m_rec; + for (int i = r.count() - 1; i >= 0; --i) + r.setGenerated(i, g); } Op m_op; QSqlRecord m_rec; QSqlRecord m_db_values; bool m_submitted; + bool m_insert; }; typedef QMap CacheMap; -- cgit v1.2.3 From 291e2c7d5416af4d16dc0a6e60df7980ba745a3d Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Thu, 16 Feb 2012 02:38:02 +0100 Subject: QSqlTableModel: long live selectRow()! Change-Id: If26dbcc8a1e8ef1376ef7a688c946ce5270e5706 Reviewed-by: Yunqiao Yin --- src/sql/models/qsqltablemodel.cpp | 43 +++++++++++++++++++++++++++++++++++++++ src/sql/models/qsqltablemodel.h | 1 + src/sql/models/qsqltablemodel_p.h | 14 +++++++++++++ 3 files changed, 58 insertions(+) (limited to 'src') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 9ade5f14d5..40230c3043 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -391,6 +391,49 @@ bool QSqlTableModel::select() return true; } +/*! + Refreshes \a row in the model with values from the database table row matching + on primary key values. Without a primary key, all column values must match. If + no matching row is found, the model will show an empty row. + + Returns true if successful; otherwise returns false. + + \sa select() +*/ +bool QSqlTableModel::selectRow(int row) +{ + Q_D(QSqlTableModel); + + if (row < 0 || row >= rowCount()) + return false; + + const int table_sort_col = d->sortColumn; + d->sortColumn = -1; + const QString table_filter = d->filter; + d->filter = d->db.driver()->sqlStatement(QSqlDriver::WhereStatement, + d->tableName, + d->primaryValues(row), + false); + if (d->filter.startsWith(QLatin1String("WHERE "), Qt::CaseInsensitive)) + d->filter.remove(0, 6); + const QString stmt = selectStatement(); + d->sortColumn = table_sort_col; + d->filter = table_filter; + + QSqlQuery q(d->db); + q.setForwardOnly(true); + if (!q.exec(stmt)) + return false; + + bool exists = q.next(); + d->cache[row].refresh(exists, q.record()); + + emit headerDataChanged(Qt::Vertical, row, row); + emit dataChanged(createIndex(row, 0), createIndex(row, columnCount() - 1)); + + return true; +} + /*! \reimp */ diff --git a/src/sql/models/qsqltablemodel.h b/src/sql/models/qsqltablemodel.h index 38e92200c7..13316bc4ed 100644 --- a/src/sql/models/qsqltablemodel.h +++ b/src/sql/models/qsqltablemodel.h @@ -67,6 +67,7 @@ public: virtual ~QSqlTableModel(); virtual bool select(); + virtual bool selectRow(int row); virtual void setTable(const QString &tableName); QString tableName() const; diff --git a/src/sql/models/qsqltablemodel_p.h b/src/sql/models/qsqltablemodel_p.h index ba2fdf5df3..323964afe8 100644 --- a/src/sql/models/qsqltablemodel_p.h +++ b/src/sql/models/qsqltablemodel_p.h @@ -135,6 +135,20 @@ public: setGenerated(m_db_values, true); } } + inline void refresh(bool exists, const QSqlRecord& newvals) + { + m_submitted = true; + if (exists) { + m_op = Update; + m_db_values = newvals; + m_rec = newvals; + setGenerated(m_rec, false); + } else { + m_op = Delete; + m_rec.clear(); + m_db_values.clear(); + } + } inline bool insert() const { return m_insert; } inline void revert() { -- cgit v1.2.3 From 888fed8065f708baeb6efa5f280c2f1aec3dee78 Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Tue, 21 Feb 2012 09:22:26 +0100 Subject: QSqlTableModel: use selectRow() for field and row edit strategies Calling select refreshes the query data but disrupts view navigation. For OnFieldChange and OnRecordChange it makes sense to only select the row in question. This does not disturb view navigation. Assume disruption of view navigation is not a problem for OnManualSubmit because the user or application decides when submitAll is called. Task-number: QTBUG-2875 Change-Id: I1e5f68668fb9102f6296d67d543e80daa403f1c4 Reviewed-by: Yunqiao Yin --- src/sql/models/qsqltablemodel.cpp | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 40230c3043..aa7dc3dcf8 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -663,8 +663,8 @@ bool QSqlTableModel::deleteRowFromTable(int row) Returns false on error, detailed error information can be obtained with lastError(). - On success the model will be repopulated. Any views - presenting it will lose their selections. + In OnManualSubmit, on success the model will be repopulated. + Any views presenting it will lose their selections. Note: In OnManualSubmit mode, already submitted changes won't be cleared from the cache when submitAll() fails. This allows @@ -677,6 +677,8 @@ bool QSqlTableModel::submitAll() { Q_D(QSqlTableModel); + bool success = true; + for (QSqlTableModelPrivate::CacheMap::Iterator it = d->cache.begin(); it != d->cache.constEnd(); ++it) { if (it.value().submitted()) @@ -684,25 +686,35 @@ bool QSqlTableModel::submitAll() switch (it.value().op()) { case QSqlTableModelPrivate::Insert: - if (!insertRowIntoTable(it.value().rec())) - return false; + success = insertRowIntoTable(it.value().rec()); break; case QSqlTableModelPrivate::Update: - if (!updateRowInTable(it.key(), it.value().rec())) - return false; + success = updateRowInTable(it.key(), it.value().rec()); break; case QSqlTableModelPrivate::Delete: - if (!deleteRowFromTable(it.key())) - return false; + success = deleteRowFromTable(it.key()); break; case QSqlTableModelPrivate::None: Q_ASSERT_X(false, "QSqlTableModel::submitAll()", "Invalid cache operation"); break; } - it.value().setSubmitted(); + + if (success) { + it.value().setSubmitted(); + if (d->strategy != OnManualSubmit) + success = selectRow(it.key()); + } + + if (!success) + break; + } + + if (success) { + if (d->strategy == OnManualSubmit) + success = select(); } - return select(); + return success; } /*! @@ -719,8 +731,8 @@ bool QSqlTableModel::submitAll() Returns true on success; otherwise returns false. Use lastError() to query detailed error information. - On success the model will be repopulated. Any views - presenting it will lose their selections. + Does not automatically repopulate the model. Submitted rows are + refreshed from the database on success. \sa revert(), revertRow(), submitAll(), revertAll(), lastError() */ -- cgit v1.2.3 From ebb94587f69207d02269d7c20dd963e59629cfc4 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 2 Mar 2012 08:07:23 +0100 Subject: Use #define before including SHA-2 3rdparty code Using typedef causes errors due to re-definition, so #define the types needed by the SHA-2 code to the q[u]int* equivalents instead. Change-Id: I6fc29788dd05aeee28723820f511527d482d31f2 Reviewed-by: Oliver Wolff --- src/corelib/tools/qcryptographichash.cpp | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp index 3730a6c580..be124c94f7 100644 --- a/src/corelib/tools/qcryptographichash.cpp +++ b/src/corelib/tools/qcryptographichash.cpp @@ -48,23 +48,16 @@ #include "../../3rdparty/sha1/sha1.cpp" /* - These typedefs are needed by the RFC6234 code. Normally they would come - from from stdint.h, but since this header is not available on all platforms - (MSVC 2008, for example), we need to define them ourselves. + These #defines replace the typedefs needed by the RFC6234 code. Normally + the typedefs would come from from stdint.h, but since this header is not + available on all platforms (MSVC 2008, for example), we #define them to the + Qt equivalents. */ -#ifndef _UINT64_T_DECLARED -typedef QT_PREPEND_NAMESPACE(quint64) uint64_t; -#endif +#define uint64_t QT_PREPEND_NAMESPACE(quint64) +#define uint32_t QT_PREPEND_NAMESPACE(quint32) +#define uint8_t QT_PREPEND_NAMESPACE(quint8) +#define int_least16_t QT_PREPEND_NAMESPACE(qint16) -#ifndef _UINT32_T_DECLARED -typedef QT_PREPEND_NAMESPACE(quint32) uint32_t; -#endif - -#ifndef _UINT8_T_DECLARED -typedef QT_PREPEND_NAMESPACE(quint8) uint8_t; -#endif - -typedef QT_PREPEND_NAMESPACE(qint16) int_least16_t; // Header from rfc6234 with 1 modification: // sha1.h - commented out '#include ' on line 74 #include "../../3rdparty/rfc6234/sha.h" @@ -90,16 +83,21 @@ static int SHA384_512AddLength(SHA512Context *context, unsigned int length); // sha384-512.c - appended 'M' to the SHA224_256AddLength macro on line 304 #include "../../3rdparty/rfc6234/sha384-512.c" +#undef uint64_t +#undef uint32_t +#undef uint68_t +#undef int_least16_t + #include static inline int SHA224_256AddLength(SHA256Context *context, unsigned int length) { - uint32_t addTemp; + QT_PREPEND_NAMESPACE(quint32) addTemp; return SHA224_256AddLengthM(context, length); } static inline int SHA384_512AddLength(SHA512Context *context, unsigned int length) { - uint64_t addTemp; + QT_PREPEND_NAMESPACE(quint64) addTemp; return SHA384_512AddLengthM(context, length); } -- cgit v1.2.3 From c74bc26605f2337b13f366c2600fff4822d88ffe Mon Sep 17 00:00:00 2001 From: Aaron McCarthy Date: Tue, 6 Mar 2012 11:17:07 +1000 Subject: Support legacy QDataStream serialization of QDate. Commit 8327fa7c11f6c84ccc66be4365ee282a76288788 changed the type of the Julian day member of QDate from quint32 to qint64. This changed the QDataStream format. Keep the old behavior, with the limited date range, if the stream version is less than Qt_5_0. Change-Id: I800448979a1891581069f39de7f9ab9c634e4f0e Reviewed-by: John Layt Reviewed-by: Thiago Macieira --- src/corelib/tools/qdatetime.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index 64ad3121d0..fa5eed4f86 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -3511,7 +3511,10 @@ void QDateTime::detach() QDataStream &operator<<(QDataStream &out, const QDate &date) { - return out << (qint64)(date.jd); + if (out.version() < QDataStream::Qt_5_0) + return out << quint32(date.jd); + else + return out << qint64(date.jd); } /*! @@ -3524,9 +3527,16 @@ QDataStream &operator<<(QDataStream &out, const QDate &date) QDataStream &operator>>(QDataStream &in, QDate &date) { - qint64 jd; - in >> jd; - date.jd = jd; + if (in.version() < QDataStream::Qt_5_0) { + quint32 jd; + in >> jd; + date.jd = jd; + } else { + qint64 jd; + in >> jd; + date.jd = jd; + } + return in; } -- cgit v1.2.3 From 7ae6a6e744f92db2626b7c9b38175dc4140a4eaf Mon Sep 17 00:00:00 2001 From: Rohan McGovern Date: Tue, 6 Mar 2012 17:55:14 +1000 Subject: Fixed warning from gcc with -Wundef for some values of WCHAR_MAX Certain versions of system headers will declare WCHAR_MAX like: #define __WCHAR_MAX ( (wchar_t) - 1 ) #define WCHAR_MAX __WCHAR_MAX In particular on ARM (see e.g. http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=598937 ) In this case, defined(WCHAR_MAX) is true, but attempting to use the value of WCHAR_MAX in a preprocessor expression will not give the desired results - "wchar_t" is unknown to the preprocessor, so WCHAR_MAX silently (without -Wundef) evaluates to ( (0) - 1 ) == -1. A simple workaround is to avoid looking at WCHAR_MAX when the superior __SIZEOF_WCHAR_T__ is defined. Change-Id: I439b166cffb93416737ee19025fb6e8d51c27876 Reviewed-by: Bradley T. Hughes Reviewed-by: Thiago Macieira --- src/corelib/tools/qstring.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 6fc86fc04b..4d02fbe66d 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -98,7 +98,9 @@ template struct QConstStringData #define QT_UNICODE_LITERAL_II(str) u"" str -#elif defined(Q_OS_WIN) || (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2) || defined(WCHAR_MAX) && (WCHAR_MAX - 0 < 65536) +#elif defined(Q_OS_WIN) \ + || (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2) \ + || (!defined(__SIZEOF_WCHAR_T__) && defined(WCHAR_MAX) && (WCHAR_MAX - 0 < 65536)) // wchar_t is 2 bytes template struct QConstStringData { -- cgit v1.2.3 From 53bbea232830af3f056cd8253c4ff4dd33f525c7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 6 Mar 2012 13:05:09 +0100 Subject: Fix parsing of unicode escape sequences Change-Id: I63a7cd3a571fb47c97157bcb2ca29c4239c600ba Reviewed-by: Thiago Macieira --- src/corelib/json/qjsonparser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/corelib/json/qjsonparser.cpp b/src/corelib/json/qjsonparser.cpp index 16eedadf1a..a83685da22 100644 --- a/src/corelib/json/qjsonparser.cpp +++ b/src/corelib/json/qjsonparser.cpp @@ -584,9 +584,9 @@ static inline bool addHexDigit(char digit, uint *result) if (digit >= '0' && digit <= '9') *result |= (digit - '0'); else if (digit >= 'a' && digit <= 'f') - *result |= (digit - 'a'); + *result |= (digit - 'a') + 10; else if (digit >= 'A' && digit <= 'F') - *result |= (digit - 'A'); + *result |= (digit - 'A') + 10; else return false; return true; -- cgit v1.2.3 From 1c438f104815e670afd94bfb396922dae09e7dcd Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 5 Mar 2012 16:56:55 +0100 Subject: Fix off by one in updateAccessibility. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that indexOfChild is 0-based, the update notifications should follow. Change-Id: I5e0303516d503d5e23061df5894b2428c00da2ce Reviewed-by: Jan-Arve Sæther Reviewed-by: Frederik Gladhorn --- src/plugins/platforms/windows/qwindowsaccessibility.cpp | 14 ++++++-------- src/widgets/widgets/qmenu.cpp | 2 +- src/widgets/widgets/qmenubar.cpp | 1 - src/widgets/widgets/qtabbar.cpp | 4 ++-- 4 files changed, 9 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/windows/qwindowsaccessibility.cpp b/src/plugins/platforms/windows/qwindowsaccessibility.cpp index 1a8f593609..134b1c81ce 100644 --- a/src/plugins/platforms/windows/qwindowsaccessibility.cpp +++ b/src/plugins/platforms/windows/qwindowsaccessibility.cpp @@ -844,14 +844,12 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, I QPair ref = qAccessibleRecentSentEvents()->value(entry); if (ref.first) { acc = QAccessible::queryAccessibleInterface(ref.first); - if (acc && ref.second) { - if (ref.second) { - QAccessibleInterface *res = acc->child(ref.second - 1); - delete acc; - if (!res) - return E_INVALIDARG; - acc = res; - } + if (acc && ref.second >= 0) { + QAccessibleInterface *res = acc->child(ref.second); + delete acc; + if (!res) + return E_INVALIDARG; + acc = res; } } } else { diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 3fb2a6122c..fd030a5383 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -1075,7 +1075,7 @@ void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e if (action_e == QAction::Hover) { #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive()) { - int actionIndex = indexOf(action) + 1; + int actionIndex = indexOf(action); QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::Focus, q, actionIndex)); QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::Selection, q, actionIndex)); } diff --git a/src/widgets/widgets/qmenubar.cpp b/src/widgets/widgets/qmenubar.cpp index 11f6592cc9..0efa6caebc 100644 --- a/src/widgets/widgets/qmenubar.cpp +++ b/src/widgets/widgets/qmenubar.cpp @@ -531,7 +531,6 @@ void QMenuBarPrivate::_q_actionHovered() #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive()) { int actionIndex = actions.indexOf(action); - ++actionIndex; QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::Focus, q, actionIndex)); QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::Selection, q, actionIndex)); } diff --git a/src/widgets/widgets/qtabbar.cpp b/src/widgets/widgets/qtabbar.cpp index ca94854d11..ce25a22847 100644 --- a/src/widgets/widgets/qtabbar.cpp +++ b/src/widgets/widgets/qtabbar.cpp @@ -1183,8 +1183,8 @@ void QTabBar::setCurrentIndex(int index) d->layoutTab(index); #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive()) { - QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::Focus, this, index + 1)); - QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::Selection, this, index + 1)); + QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::Focus, this, index)); + QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::Selection, this, index)); } #endif emit currentChanged(index); -- cgit v1.2.3 From 76d0df1b0ad9de56b7ca2e86c254f61d6e5ea702 Mon Sep 17 00:00:00 2001 From: Rohan McGovern Date: Tue, 6 Mar 2012 17:50:05 +1000 Subject: Fixed warnings from arm builds with -Wundef Do not use the value of a macro before verifying that the macro is defined. Change-Id: I36bebe37da5f4e5e7af1e423b7f2b18091e35707 Reviewed-by: Oswald Buddenhagen Reviewed-by: Bradley T. Hughes --- src/corelib/global/qprocessordetection.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/corelib/global/qprocessordetection.h b/src/corelib/global/qprocessordetection.h index 4213d5830e..1f16f090a9 100644 --- a/src/corelib/global/qprocessordetection.h +++ b/src/corelib/global/qprocessordetection.h @@ -93,7 +93,7 @@ || defined(__ARM_ARCH_7A__) \ || defined(__ARM_ARCH_7R__) \ || defined(__ARM_ARCH_7M__) \ - || (__TARGET_ARCH_ARM-0 >= 7) + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 7) # define Q_PROCESSOR_ARM_V7 # define Q_PROCESSOR_ARM_V6 # define Q_PROCESSOR_ARM_V5 @@ -104,11 +104,11 @@ || defined(__ARM_ARCH_6K__) \ || defined(__ARM_ARCH_6ZK__) \ || defined(__ARM_ARCH_6M__) \ - || (__TARGET_ARCH_ARM-0 >= 6) + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 6) # define Q_PROCESSOR_ARM_V6 # define Q_PROCESSOR_ARM_V5 # elif defined(__ARM_ARCH_5TEJ__) \ - || (__TARGET_ARCH_ARM-0 >= 5) + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM-0 >= 5) # define Q_PROCESSOR_ARM_V5 # endif # if defined(__ARMEL__) -- cgit v1.2.3 From 02d947524d887e3ff6cb24065ccdbf3311ea81a8 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Sat, 3 Mar 2012 12:41:13 +0000 Subject: QRegularExpression: fix documentation due to qdoc changes Removes the usage of various qdoc macros which are now deprecated. Change-Id: I74fa70f8d2a2a1bff57cdb2bcc14a31a7198dea0 Reviewed-by: Casper van Donderen --- src/corelib/tools/qregularexpression.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 40b6b5a08e..7faa907e35 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -73,21 +73,21 @@ QT_BEGIN_NAMESPACE strings and texts. This is useful in many contexts, e.g., \table - \row \i Validation - \i A regexp can test whether a substring meets some criteria, + \row \li Validation + \li A regexp can test whether a substring meets some criteria, e.g. is an integer or contains no whitespace. - \row \i Searching - \i A regexp provides more powerful pattern matching than + \row \li Searching + \li A regexp provides more powerful pattern matching than simple substring matching, e.g., match one of the words \e{mail}, \e{letter} or \e{correspondence}, but none of the words \e{email}, \e{mailman}, \e{mailer}, \e{letterbox}, etc. - \row \i Search and Replace - \i A regexp can replace all occurrences of a substring with a + \row \li Search and Replace + \li A regexp can replace all occurrences of a substring with a different substring, e.g., replace all occurrences of \e{&} with \e{\&} except where the \e{&} is already followed by an \e{amp;}. - \row \i String Splitting - \i A regexp can be used to identify where a string should be + \row \li String Splitting + \li A regexp can be used to identify where a string should be split apart, e.g. splitting tab-delimited strings. \endtable @@ -99,12 +99,12 @@ QT_BEGIN_NAMESPACE Good references about regular expressions include: \list - \o \e {Mastering Regular Expressions} (Third Edition) by Jeffrey E. F. + \li \e {Mastering Regular Expressions} (Third Edition) by Jeffrey E. F. Friedl, ISBN 0-596-52812-4; - \o the \l{http://pcre.org/pcre.txt} {pcrepattern(3)} man page, describing + \li the \l{http://pcre.org/pcre.txt} {pcrepattern(3)} man page, describing the pattern syntax supported by PCRE (the reference implementation of Perl-compatible regular expressions); - \o the \l{http://perldoc.perl.org/perlre.html} {Perl's regular expression + \li the \l{http://perldoc.perl.org/perlre.html} {Perl's regular expression documentation} and the \l{http://perldoc.perl.org/perlretut.html} {Perl's regular expression tutorial}. \endlist @@ -117,7 +117,7 @@ QT_BEGIN_NAMESPACE supports Unicode. For an overview of the regular expression syntax supported by QRegularExpression, please refer to the aforementioned pcrepattern(3) man page. A regular expression is made up of two things: a - \bold{pattern string} and a set of \bold{pattern options} that change the + \b{pattern string} and a set of \b{pattern options} that change the meaning of the pattern string. You can set the pattern string by passing a string to the QRegularExpression @@ -307,9 +307,9 @@ QT_BEGIN_NAMESPACE to do so we must distinguish three cases: \list - \o the input cannot possibly match the regular expression; - \o the input does match the regular expression; - \o the input does not match the regular expression right now, + \li the input cannot possibly match the regular expression; + \li the input does match the regular expression; + \li the input does not match the regular expression right now, but it will if more charaters will be added to it. \endlist @@ -653,7 +653,7 @@ QT_BEGIN_NAMESPACE \value ExtendedPatternSyntaxOption Any whitespace in the pattern string which is not escaped and outside a - character class is ignored. Moreover, an unescaped sharp (\bold{#}) + character class is ignored. Moreover, an unescaped sharp (\b{#}) outside a character class causes all the following characters, until the first newline (included), to be ignored. This can be used to increase the readability of a pattern string as well as put comments -- cgit v1.2.3 From eddabd2395c6bf6516893d04c22e7fde2be9e1a1 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 6 Mar 2012 16:29:50 +0100 Subject: Fix the bit test for ABS_MT_SLOT to detect protocol B I tested, it didn't work. This is from a previous patch I had already gotten working before 40a5ba4d3fccb449dcfd8d9a0deaf4c7f0fe12bc was submitted. Change-Id: I868f069fe834b3122ed9b5b3dc9af0781d6e1d0d Reviewed-by: Laszlo Agocs --- src/plugins/generic/evdevtouch/qevdevtouch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/generic/evdevtouch/qevdevtouch.cpp b/src/plugins/generic/evdevtouch/qevdevtouch.cpp index 73f253ae96..9e6347457d 100644 --- a/src/plugins/generic/evdevtouch/qevdevtouch.cpp +++ b/src/plugins/generic/evdevtouch/qevdevtouch.cpp @@ -134,7 +134,7 @@ void QTouchScreenData::registerDevice() static inline bool testBit(long bit, const long *array) { - return array[bit / LONG_BITS] & (1 << (bit & (LONG_BITS - 1))); + return (array[bit / LONG_BITS] >> bit % LONG_BITS) & 1; } QTouchScreenHandler::QTouchScreenHandler(const QString &spec) -- cgit v1.2.3 From b371f3f943703840d0dfbe30505018bcca06e260 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Tue, 6 Mar 2012 16:09:09 +0200 Subject: Fix double click handling. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until now double clicking in Qt 5 resulted in the following sequence of mouse events: pressed, released, double clicked, released. This is wrong, the press belonging to the second button down is missing. In Qt 4 that pressed event is present. The problem is not apparent in desktop environments because the double click is functioning properly even when the second pressed is missing. However when using a platform plug-in like wayland, where the clients receive only press, move and release events, double click was broken because the second click was effectively ignored (due to receiving nothing but a button release). Change-Id: Ief6af12c666b23e544da4a68cb835cd577265469 Reviewed-by: Samuel Rødal --- src/gui/kernel/qguiapplication.cpp | 42 +++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index be82005a54..095336a948 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -933,6 +933,7 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo QPointF globalPoint = e->globalPos; Qt::MouseButton button = Qt::NoButton; + bool doubleClick = false; if (QGuiApplicationPrivate::lastCursorPosition != globalPoint) { type = QEvent::MouseMove; @@ -940,8 +941,7 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo if (qAbs(globalPoint.x() - mousePressX) > mouse_double_click_distance|| qAbs(globalPoint.y() - mousePressY) > mouse_double_click_distance) mousePressButton = Qt::NoButton; - } - else { // Check to see if a new button has been pressed/released. + } else { // Check to see if a new button has been pressed/released. for (int check = Qt::LeftButton; check <= int(Qt::MaxMouseButton); check = check << 1) { @@ -956,25 +956,19 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo } buttons = e->buttons; if (button & e->buttons) { - if ((e->timestamp - mousePressTime) < static_cast(qApp->styleHints()->mouseDoubleClickInterval()) && - button == mousePressButton) { - type = QEvent::MouseButtonDblClick; - mousePressButton = Qt::NoButton; - } - else { - type = QEvent::MouseButtonPress; - mousePressTime = e->timestamp; - mousePressButton = button; - const QPoint point = QGuiApplicationPrivate::lastCursorPosition.toPoint(); - mousePressX = point.x(); - mousePressY = point.y(); - } - } - else + ulong doubleClickInterval = static_cast(qApp->styleHints()->mouseDoubleClickInterval()); + doubleClick = e->timestamp - mousePressTime < doubleClickInterval && button == mousePressButton; + type = QEvent::MouseButtonPress; + mousePressTime = e->timestamp; + mousePressButton = button; + const QPoint point = QGuiApplicationPrivate::lastCursorPosition.toPoint(); + mousePressX = point.x(); + mousePressY = point.y(); + } else { type = QEvent::MouseButtonRelease; + } } - if (window) { QMouseEvent ev(type, localPoint, localPoint, globalPoint, button, buttons, e->modifiers); ev.setTimestamp(e->timestamp); @@ -1017,12 +1011,16 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo fake.synthetic = true; processTouchEvent(&fake); } + if (doubleClick) { + mousePressButton = Qt::NoButton; + QMouseEvent dblClickEvent(QEvent::MouseButtonDblClick, localPoint, localPoint, globalPoint, + button, buttons, e->modifiers); + dblClickEvent.setTimestamp(e->timestamp); + QGuiApplication::sendSpontaneousEvent(window, &dblClickEvent); + } } } - -//### there's a lot of duplicated logic here -- refactoring required! - void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::WheelEvent *e) { if (!e->window) @@ -1041,8 +1039,6 @@ void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::Wh } } - - // Remember, Qt convention is: keyboard state is state *before* void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyEvent *e) -- cgit v1.2.3 From 484e2923b45ceccb0ea547a4a860ecc5315ada67 Mon Sep 17 00:00:00 2001 From: Donald Carr Date: Fri, 2 Mar 2012 04:58:33 +0000 Subject: Query udev build parameters from pkg-config Change-Id: Ia3b7329d7359684ee7bf572a7e5fb681105108f4 Reviewed-by: Donald Carr Reviewed-by: Holger Freyther Reviewed-by: Laszlo Agocs Reviewed-by: Oswald Buddenhagen --- src/platformsupport/udev/udev.pri | 2 ++ src/plugins/generic/evdevkeyboard/evdevkeyboard.pro | 4 ++-- src/plugins/generic/evdevmouse/evdevmouse.pro | 2 ++ src/plugins/generic/evdevtouch/evdevtouch.pro | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/platformsupport/udev/udev.pri b/src/platformsupport/udev/udev.pri index 21c71d5f5b..c99d4b6810 100644 --- a/src/platformsupport/udev/udev.pri +++ b/src/platformsupport/udev/udev.pri @@ -1,4 +1,6 @@ contains(QT_CONFIG, libudev) { HEADERS += $$PWD/qudevhelper_p.h $$PWD/qudevicehelper_p.h SOURCES += $$PWD/qudevhelper.cpp $$PWD/qudevicehelper.cpp + + INCLUDEPATH += $$QMAKE_INCDIR_LIBUDEV } diff --git a/src/plugins/generic/evdevkeyboard/evdevkeyboard.pro b/src/plugins/generic/evdevkeyboard/evdevkeyboard.pro index 82edf170a8..21e4bf63ee 100644 --- a/src/plugins/generic/evdevkeyboard/evdevkeyboard.pro +++ b/src/plugins/generic/evdevkeyboard/evdevkeyboard.pro @@ -12,11 +12,11 @@ HEADERS = \ QT += core-private platformsupport-private -LIBS += -ludev - SOURCES = main.cpp \ qevdevkeyboardhandler.cpp \ qevdevkeyboardmanager.cpp OTHER_FILES += \ evdevkeyboard.json + +LIBS += $$QMAKE_LIBS_LIBUDEV diff --git a/src/plugins/generic/evdevmouse/evdevmouse.pro b/src/plugins/generic/evdevmouse/evdevmouse.pro index c5d162fb2f..781d901f28 100644 --- a/src/plugins/generic/evdevmouse/evdevmouse.pro +++ b/src/plugins/generic/evdevmouse/evdevmouse.pro @@ -14,3 +14,5 @@ SOURCES = main.cpp \ OTHER_FILES += \ evdevmouse.json + +LIBS += $$QMAKE_LIBS_LIBUDEV diff --git a/src/plugins/generic/evdevtouch/evdevtouch.pro b/src/plugins/generic/evdevtouch/evdevtouch.pro index f9fb4a61d8..192a87c2a3 100644 --- a/src/plugins/generic/evdevtouch/evdevtouch.pro +++ b/src/plugins/generic/evdevtouch/evdevtouch.pro @@ -16,6 +16,8 @@ QT += core-private platformsupport-private OTHER_FILES += \ evdevtouch.json +LIBS += $$QMAKE_LIBS_LIBUDEV + # DEFINES += USE_MTDEV contains(DEFINES, USE_MTDEV): LIBS += -lmtdev -- cgit v1.2.3 From a47d974e19512ae5a445f64fbd2b1703201f781d Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Thu, 16 Feb 2012 23:49:27 +0100 Subject: QRegularExpression: fix optimizePattern, document the issue The studyData pointer is atomically set by the pointer assignment, but another processor running a different thread might see the new studyData value but not the memory it points to. Therefore, the current studyData is returned from optimizePattern and used by that thread. Docs were added to optimizePattern to explain what's going on. Change-Id: I4502c336077bb98a1751011aa93ffd4f585ed101 Reviewed-by: Thiago Macieira --- src/corelib/tools/qregularexpression.cpp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 7faa907e35..0252a30c89 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -773,7 +773,7 @@ struct QRegularExpressionPrivate : QSharedData void cleanCompiledPattern(); void compilePattern(); void getPatternInfo(); - void optimizePattern(); + pcre16_extra *optimizePattern(); QRegularExpressionMatchPrivate *doMatch(const QString &subject, int offset, @@ -1006,15 +1006,30 @@ static bool isJitEnabled() /*! \internal + + The purpose of the function is to call pcre16_study (which allows some + optimizations to be performed, including JIT-compiling the pattern), and + setting the studyData member variable to the result of the study. It gets + called by doMatch() every time a match is performed. As of now, the + optimizations on the pattern are performed after a certain number of usages + (i.e. the OPTIMIZE_AFTER_USE_COUNT constant). + + Notice that although the method is protected by a mutex, one thread may + invoke this function and return immediately (i.e. not study the pattern, + leaving studyData to NULL); but before calling pcre16_exec to perform the + match, another thread performs the studying and sets studyData to something + else. Although the assignment to studyData is itself atomic, the release of + the memory pointed by studyData isn't. Therefore, the current studyData + value is returned and used by doMatch. */ -void QRegularExpressionPrivate::optimizePattern() +pcre16_extra *QRegularExpressionPrivate::optimizePattern() { Q_ASSERT(compiledPattern); QMutexLocker lock(&mutex); if (studyData || (++usedCount != OPTIMIZE_AFTER_USE_COUNT)) - return; + return studyData; static const bool enableJit = isJitEnabled(); @@ -1027,6 +1042,8 @@ void QRegularExpressionPrivate::optimizePattern() if (!studyData && err) qWarning("QRegularExpressionPrivate::optimizePattern(): pcre_study failed: %s", err); + + return studyData; } /*! @@ -1089,7 +1106,7 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString capturingCount); // this is mutex protected - const_cast(this)->optimizePattern(); + const pcre16_extra *currentStudyData = const_cast(this)->optimizePattern(); int pcreOptions = convertToPcreOptions(matchOptions); @@ -1113,12 +1130,12 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString int result; if (!previousMatchWasEmpty) { - result = pcre16_exec(compiledPattern, studyData, + result = pcre16_exec(compiledPattern, currentStudyData, subjectUtf16, subjectLength, offset, pcreOptions, captureOffsets, captureOffsetsCount); } else { - result = pcre16_exec(compiledPattern, studyData, + result = pcre16_exec(compiledPattern, currentStudyData, subjectUtf16, subjectLength, offset, pcreOptions | PCRE_NOTEMPTY_ATSTART | PCRE_ANCHORED, captureOffsets, captureOffsetsCount); @@ -1136,7 +1153,7 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString ++offset; } - result = pcre16_exec(compiledPattern, studyData, + result = pcre16_exec(compiledPattern, currentStudyData, subjectUtf16, subjectLength, offset, pcreOptions, captureOffsets, captureOffsetsCount); -- cgit v1.2.3 From 3ae2dce9118c0d37cdfed60b07c83cdd20ef3e36 Mon Sep 17 00:00:00 2001 From: Sarah Smith Date: Tue, 6 Mar 2012 11:51:22 +1000 Subject: Compile with -qtnamespace Mac has to work with -qtnamespace as well, and other files have gotten this right, so follow their example. Change-Id: I551e1843e8a0e82a82d1d5ea8c8cd5f20e6880fa Reviewed-by: Rohan McGovern --- src/plugins/platforms/cocoa/qcocoasystemsettings.mm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm index eea2fb6f5d..d5b2fce2e7 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm @@ -45,6 +45,8 @@ #include +QT_BEGIN_NAMESPACE + QColor qt_mac_colorFromCGColor(CGColorRef cgcolor) { QColor pc; @@ -144,7 +146,6 @@ QPalette * qt_mac_createSystemPalette() return palette; } - struct QMacPaletteMap { inline QMacPaletteMap(QPlatformTheme::Palette p, ThemeBrush a, ThemeBrush i) : paletteRole(p), active(a), inactive(i) { } @@ -227,3 +228,5 @@ QHash qt_mac_createRolePalettes() } return palettes; } + +QT_END_NAMESPACE -- cgit v1.2.3 From 678ab52ccba0b6a6903e9aee404dbe84fb74c91d Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 2 Mar 2012 10:46:57 +0100 Subject: Improve type detection for query results When an aggregate function is used for a column in a SQL resultset then it should ensure that the right data type is reported for that column. This also concerns expressions when the returned column does not map directly to a table column. Test included for this. Task-number: QTBUG-22038 Change-Id: I07487694c0ed393d46af06e232914fe923356a99 Reviewed-by: Mark Brand Reviewed-by: Honglei Zhang --- src/sql/drivers/sqlite/qsql_sqlite.cpp | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/sql/drivers/sqlite/qsql_sqlite.cpp b/src/sql/drivers/sqlite/qsql_sqlite.cpp index d2dc5af070..b2a55252ac 100644 --- a/src/sql/drivers/sqlite/qsql_sqlite.cpp +++ b/src/sql/drivers/sqlite/qsql_sqlite.cpp @@ -174,12 +174,37 @@ void QSQLiteResultPrivate::initColumns(bool emptyResultset) // must use typeName for resolving the type to match QSqliteDriver::record QString typeName = QString(reinterpret_cast( sqlite3_column_decltype16(stmt, i))); - - int dotIdx = colName.lastIndexOf(QLatin1Char('.')); - QSqlField fld(colName.mid(dotIdx == -1 ? 0 : dotIdx + 1), qGetColumnType(typeName)); - // sqlite3_column_type is documented to have undefined behavior if the result set is empty int stp = emptyResultset ? -1 : sqlite3_column_type(stmt, i); + + QVariant::Type fieldType; + + if (!typeName.isEmpty()) { + fieldType = qGetColumnType(typeName); + } else { + // Get the proper type for the field based on stp value + switch (stp) { + case SQLITE_INTEGER: + fieldType = QVariant::Int; + break; + case SQLITE_FLOAT: + fieldType = QVariant::Double; + break; + case SQLITE_BLOB: + fieldType = QVariant::ByteArray; + break; + case SQLITE_TEXT: + fieldType = QVariant::String; + break; + case SQLITE_NULL: + default: + fieldType = QVariant::Invalid; + break; + } + } + + int dotIdx = colName.lastIndexOf(QLatin1Char('.')); + QSqlField fld(colName.mid(dotIdx == -1 ? 0 : dotIdx + 1), fieldType); fld.setSqlType(stp); rInf.append(fld); } -- cgit v1.2.3 From 79c0d9adbec05b0d6648e74952a862fa72b13c63 Mon Sep 17 00:00:00 2001 From: Mark Brand Date: Wed, 7 Mar 2012 00:24:08 +0100 Subject: QSqlTableModel::selectRow(): complete documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mention in changes and document Qt version (merci à dfaure). Follow-up to 291e2c7d5416af4d16dc0a6e60df7980ba745a3d. Change-Id: Ie5626e9cd268812c1173ca494ccd8d6bd9be2687 Reviewed-by: Honglei Zhang --- src/sql/models/qsqltablemodel.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index aa7dc3dcf8..d39df1d710 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -392,6 +392,8 @@ bool QSqlTableModel::select() } /*! + \since 5.0 + Refreshes \a row in the model with values from the database table row matching on primary key values. Without a primary key, all column values must match. If no matching row is found, the model will show an empty row. -- cgit v1.2.3 From 3d6901066af9f0f9c42d5c7eedf74519946917d6 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 7 Mar 2012 09:49:20 +0100 Subject: Fix support for scripts that require OpenType We need to load OpenType tables when initializing fonts for scripts that require them. This fixes support for many Brahmic scripts. Change-Id: Ib5e50f2c7e5edb4b3e3ecf9fd004f2cf62634add Reviewed-by: Lars Knoll --- src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp | 2 +- src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp index 5cfbc6cb4f..ebe73bff16 100644 --- a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp @@ -229,7 +229,7 @@ QFontEngine *QBasicFontDatabase::fontEngine(const QFontDef &fontDef, QUnicodeTab delete engine; engine = 0; } else if (scriptRequiresOpenType(script)) { - HB_Face hbFace = engine->harfbuzzFace(); + HB_Face hbFace = engine->initializedHarfbuzzFace(); if (!hbFace || !hbFace->supported_scripts[script]) { delete engine; engine = 0; diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index 8a9670118f..69ec3ba08a 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -575,7 +575,7 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, QUnicodeTables:: delete engine; engine = 0; } else if (scriptRequiresOpenType(script)) { - HB_Face hbFace = engine->harfbuzzFace(); + HB_Face hbFace = engine->initializedHarfbuzzFace(); if (!hbFace || !hbFace->supported_scripts[script]) { delete engine; engine = 0; -- cgit v1.2.3 From 7439fb47cd7c5731dd1aadeaf10c46b58aa59798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Tue, 6 Mar 2012 15:12:06 +0100 Subject: Add nicer error message in qMetaTypeId function. The function can be used only with a registered type and it would fail to compile for other types. By adding the static assert we can print an almost user friendly compilation error message. Change-Id: I59ab148cabf32afe0baef186b82cb03303b57780 Reviewed-by: Stephen Kelly Reviewed-by: Olivier Goffart --- src/corelib/kernel/qmetatype.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 7f6eaf2230..eacb8403dc 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -484,6 +484,7 @@ inline int qMetaTypeId( #endif ) { + Q_STATIC_ASSERT_X(QMetaTypeId2::Defined, "Type is not registered, please use Q_DECLARE_METATYPE macro to make it know to Qt's meta-object system"); return QMetaTypeId2::qt_metatype_id(); } -- cgit v1.2.3 From 83cabda862dced9477d155c84df9440047c856cf Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 7 Mar 2012 08:56:41 +0100 Subject: Add fonts to QPlatformTheme. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove QPlatformFontDatabase::defaultFonts() returning a hash containing widget name ->font and the Windows implementation. - Add enumeration and font accessor to QPlatformTheme. The value returned for the enumeration value overwrites the default font of the font database. - Implement for Windows, Mac and KDE. - Add more Windows palettes. Task-number: QTBUG-23686 Change-Id: I8a2abdfd216df23daa7c9630c54264cdf61295db Reviewed-by: Morten Johan Sørvig --- src/gui/kernel/qguiapplication.cpp | 5 + src/gui/kernel/qplatformtheme_qpa.cpp | 6 + src/gui/kernel/qplatformtheme_qpa.h | 28 ++++ src/gui/text/qplatformfontdatabase_qpa.cpp | 12 -- src/gui/text/qplatformfontdatabase_qpa.h | 2 +- .../themes/genericunix/qgenericunixthemes.cpp | 51 ++++++- .../themes/genericunix/qgenericunixthemes_p.h | 23 +++- src/plugins/platforms/cocoa/qcocoasystemsettings.h | 1 + .../platforms/cocoa/qcocoasystemsettings.mm | 40 ++++++ src/plugins/platforms/cocoa/qcocoatheme.h | 2 + src/plugins/platforms/cocoa/qcocoatheme.mm | 8 ++ .../platforms/windows/qtwindows_additional.h | 3 +- .../platforms/windows/qwindowsfontdatabase.cpp | 49 +------ .../platforms/windows/qwindowsfontdatabase.h | 1 - .../platforms/windows/qwindowsfontdatabase_ft.cpp | 40 +----- .../platforms/windows/qwindowsfontdatabase_ft.h | 5 +- src/plugins/platforms/windows/qwindowstheme.cpp | 148 +++++++++++++++++---- src/plugins/platforms/windows/qwindowstheme.h | 8 +- src/widgets/kernel/qapplication.cpp | 5 - src/widgets/kernel/qapplication_p.h | 6 +- src/widgets/kernel/qapplication_qpa.cpp | 54 ++++++++ src/widgets/widgets/qmdisubwindow.cpp | 2 +- 22 files changed, 348 insertions(+), 151 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 095336a948..f5aea654fc 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -172,6 +172,11 @@ static inline void clearPalette() static void initFontUnlocked() { + if (!QGuiApplicationPrivate::app_font) { + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + if (const QFont *font = theme->font(QPlatformTheme::SystemFont)) + QGuiApplicationPrivate::app_font = new QFont(*font); + } if (!QGuiApplicationPrivate::app_font) QGuiApplicationPrivate::app_font = new QFont(QGuiApplicationPrivate::platformIntegration()->fontDatabase()->defaultFont()); diff --git a/src/gui/kernel/qplatformtheme_qpa.cpp b/src/gui/kernel/qplatformtheme_qpa.cpp index 3fdece70ea..c6314825d7 100644 --- a/src/gui/kernel/qplatformtheme_qpa.cpp +++ b/src/gui/kernel/qplatformtheme_qpa.cpp @@ -137,6 +137,12 @@ const QPalette *QPlatformTheme::palette(Palette type) const return 0; } +const QFont *QPlatformTheme::font(Font type) const +{ + Q_UNUSED(type) + return 0; +} + QVariant QPlatformTheme::themeHint(ThemeHint hint) const { switch (hint) { diff --git a/src/gui/kernel/qplatformtheme_qpa.h b/src/gui/kernel/qplatformtheme_qpa.h index be18e4fe38..6ac6a0f573 100644 --- a/src/gui/kernel/qplatformtheme_qpa.h +++ b/src/gui/kernel/qplatformtheme_qpa.h @@ -55,6 +55,7 @@ class QPlatformMenuBar; class QPlatformDialogHelper; class QVariant; class QPalette; +class QFont; class Q_GUI_EXPORT QPlatformTheme { @@ -102,6 +103,31 @@ public: NPalettes }; + enum Font { + SystemFont, + MenuFont, + MenuBarFont, + MenuItemFont, + MessageBoxFont, + LabelFont, + TipLabelFont, + StatusBarFont, + TitleBarFont, + MdiSubWindowTitleFont, + DockWidgetTitleFont, + PushButtonFont, + ToolButtonFont, + ItemViewFont, + ListViewFont, + HeaderViewFont, + ListBoxFont, + ComboMenuItemFont, + ComboLineEditFont, + SmallFont, + MiniFont, + NFonts + }; + enum KeyboardSchemes { WindowsKeyboardScheme, @@ -122,6 +148,8 @@ public: virtual const QPalette *palette(Palette type = SystemPalette) const; + virtual const QFont *font(Font type = SystemFont) const; + virtual QVariant themeHint(ThemeHint hint) const; }; diff --git a/src/gui/text/qplatformfontdatabase_qpa.cpp b/src/gui/text/qplatformfontdatabase_qpa.cpp index 8fcf421330..47a9fe5ab1 100644 --- a/src/gui/text/qplatformfontdatabase_qpa.cpp +++ b/src/gui/text/qplatformfontdatabase_qpa.cpp @@ -374,18 +374,6 @@ QFont QPlatformFontDatabase::defaultFont() const return QFont(QLatin1String("Helvetica")); } -/*! - Returns fonts for class names. - - \sa QGuiApplication::font() - \since 5.0 -*/ - -QHash QPlatformFontDatabase::defaultFonts() const -{ - return QHash(); -} - /*! Resolve alias to actual font family names. diff --git a/src/gui/text/qplatformfontdatabase_qpa.h b/src/gui/text/qplatformfontdatabase_qpa.h index 6a58a3106c..5a5a8f321f 100644 --- a/src/gui/text/qplatformfontdatabase_qpa.h +++ b/src/gui/text/qplatformfontdatabase_qpa.h @@ -100,7 +100,7 @@ public: virtual QString fontDir() const; virtual QFont defaultFont() const; - virtual QHash defaultFonts() const; + virtual QString resolveFontFamilyAlias(const QString &family) const; //callback diff --git a/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp b/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp index 257fe9156f..602dd6264a 100644 --- a/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp +++ b/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp @@ -43,6 +43,7 @@ #include "../../services/genericunix/qgenericunixservices_p.h" #include +#include #include #include #include @@ -54,6 +55,20 @@ QT_BEGIN_NAMESPACE +ResourceHelper::ResourceHelper() +{ + qFill(palettes, palettes + QPlatformTheme::NPalettes, static_cast(0)); + qFill(fonts, fonts + QPlatformTheme::NFonts, static_cast(0)); +} + +void ResourceHelper::clear() +{ + qDeleteAll(palettes, palettes + QPlatformTheme::NPalettes); + qDeleteAll(fonts, fonts + QPlatformTheme::NFonts); + qFill(palettes, palettes + QPlatformTheme::NPalettes, static_cast(0)); + qFill(fonts, fonts + QPlatformTheme::NFonts, static_cast(0)); +} + /*! \class QGenericX11ThemeQKdeTheme \brief QGenericX11Theme is a generic theme implementation for X11. @@ -148,19 +163,40 @@ QKdeTheme::QKdeTheme(const QString &kdeHome, int kdeVersion) : m_kdeHome(kdeHome), m_kdeVersion(kdeVersion), m_toolButtonStyle(Qt::ToolButtonTextBesideIcon), m_toolBarIconSize(0) { - qFill(m_palettes, m_palettes + NPalettes, static_cast(0)); refresh(); } -void QKdeTheme::clearPalettes() +static inline QFont *readKdeFontSetting(const QSettings &settings, const QString &key) { - qDeleteAll(m_palettes, m_palettes + NPalettes); - qFill(m_palettes, m_palettes + NPalettes, static_cast(0)); + const QVariant fontValue = settings.value(key); + if (fontValue.isValid()) { + // Read font value: Might be a QStringList as KDE stores fonts without quotes. + // Also retrieve the family for the constructor since we cannot use the + // default constructor of QFont, which accesses QGuiApplication::systemFont() + // causing recursion. + QString fontDescription; + QString fontFamily; + if (fontValue.type() == QVariant::StringList) { + const QStringList list = fontValue.toStringList(); + if (!list.isEmpty()) { + fontFamily = list.first(); + fontDescription = list.join(QStringLiteral(",")); + } + } else { + fontDescription = fontFamily = fontValue.toString(); + } + if (!fontDescription.isEmpty()) { + QFont font(fontFamily); + if (font.fromString(fontDescription)) + return new QFont(font); + } + } + return 0; } void QKdeTheme::refresh() { - clearPalettes(); + m_resources.clear(); m_toolButtonStyle = Qt::ToolButtonTextBesideIcon; m_toolBarIconSize = 0; @@ -177,7 +213,7 @@ void QKdeTheme::refresh() QPalette systemPalette; if (readKdeSystemPalette(kdeSettings, &systemPalette)) - m_palettes[SystemPalette] = new QPalette(systemPalette); + m_resources.palettes[SystemPalette] = new QPalette(systemPalette); //## TODO tooltip color const QVariant styleValue = kdeSettings.value(QStringLiteral("widgetStyle")); @@ -205,6 +241,9 @@ void QKdeTheme::refresh() else if (toolBarStyle == QStringLiteral("TextUnderIcon")) m_toolButtonStyle = Qt::ToolButtonTextUnderIcon; } + + // Read system font, ignore 'fixed' 'smallestReadableFont' + m_resources.fonts[SystemFont] = readKdeFontSetting(kdeSettings, QStringLiteral("font")); } QString QKdeTheme::globalSettingsFile() const diff --git a/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h b/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h index 12937a205f..a9db29e8cd 100644 --- a/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h +++ b/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h @@ -50,6 +50,18 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE +class ResourceHelper +{ +public: + ResourceHelper(); + ~ResourceHelper() { clear(); } + + void clear(); + + QPalette *palettes[QPlatformTheme::NPalettes]; + QFont *fonts[QPlatformTheme::NFonts]; +}; + class QGenericUnixTheme : public QPlatformTheme { public: @@ -66,21 +78,24 @@ class QKdeTheme : public QPlatformTheme { QKdeTheme(const QString &kdeHome, int kdeVersion); public: - ~QKdeTheme() { clearPalettes(); } static QPlatformTheme *createKdeTheme(); virtual QVariant themeHint(ThemeHint hint) const; + virtual const QPalette *palette(Palette type = SystemPalette) const - { return m_palettes[type]; } + { return m_resources.palettes[type]; } + + virtual const QFont *font(Font type) const + { return m_resources.fonts[type]; } private: QString globalSettingsFile() const; - void clearPalettes(); void refresh(); const QString m_kdeHome; const int m_kdeVersion; - QPalette *m_palettes[NPalettes]; + + ResourceHelper m_resources; QString m_iconThemeName; QString m_iconFallbackThemeName; QStringList m_styleNames; diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.h b/src/plugins/platforms/cocoa/qcocoasystemsettings.h index 2ed6f766aa..10cac27dcd 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.h +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.h @@ -50,6 +50,7 @@ QT_BEGIN_NAMESPACE QPalette * qt_mac_createSystemPalette(); QHash qt_mac_createRolePalettes(); +QHash qt_mac_createRoleFonts(); QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm index d5b2fce2e7..c2f5df4d38 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm @@ -42,6 +42,7 @@ #include "qcocoasystemsettings.h" #include +#include #include @@ -229,4 +230,43 @@ QHash qt_mac_createRolePalettes() return palettes; } +QFont *qt_mac_qfontForThemeFont(ThemeFontID themeID) +{ + CTFontUIFontType ctID = HIThemeGetUIFontType(themeID); + QCFType ctfont = CTFontCreateUIFontForLanguage(ctID, 0, 0); + QString familyName = QCFString(CTFontCopyFamilyName(ctfont)); + QCFType dict = CTFontCopyTraits(ctfont); + CFNumberRef num = static_cast(CFDictionaryGetValue(dict, kCTFontWeightTrait)); + float fW; + CFNumberGetValue(num, kCFNumberFloat32Type, &fW); + QFont::Weight wght = fW > 0. ? QFont::Bold : QFont::Normal; + num = static_cast(CFDictionaryGetValue(dict, kCTFontSlantTrait)); + CFNumberGetValue(num, kCFNumberFloatType, &fW); + bool italic = (fW != 0.0); + return new QFont(familyName, CTFontGetSize(ctfont), wght, italic); +} + +QHash qt_mac_createRoleFonts() +{ + QHash fonts; + + fonts.insert(QPlatformTheme::SystemFont, qt_mac_qfontForThemeFont(kThemeApplicationFont)); + fonts.insert(QPlatformTheme::PushButtonFont, qt_mac_qfontForThemeFont(kThemePushButtonFont)); + fonts.insert(QPlatformTheme::ListViewFont, qt_mac_qfontForThemeFont(kThemeViewsFont)); + fonts.insert(QPlatformTheme::ListBoxFont, qt_mac_qfontForThemeFont(kThemeViewsFont)); + fonts.insert(QPlatformTheme::TitleBarFont, qt_mac_qfontForThemeFont(kThemeWindowTitleFont)); + fonts.insert(QPlatformTheme::MenuFont, qt_mac_qfontForThemeFont(kThemeMenuItemFont)); + fonts.insert(QPlatformTheme::ComboMenuItemFont, qt_mac_qfontForThemeFont(kThemeSystemFont)); + fonts.insert(QPlatformTheme::HeaderViewFont, qt_mac_qfontForThemeFont(kThemeSmallSystemFont)); + fonts.insert(QPlatformTheme::TipLabelFont, qt_mac_qfontForThemeFont(kThemeSmallSystemFont)); + fonts.insert(QPlatformTheme::LabelFont, qt_mac_qfontForThemeFont(kThemeSystemFont)); + fonts.insert(QPlatformTheme::ToolButtonFont, qt_mac_qfontForThemeFont(kThemeSmallSystemFont)); + fonts.insert(QPlatformTheme::MenuItemFont, qt_mac_qfontForThemeFont(kThemeMenuItemFont)); + fonts.insert(QPlatformTheme::ComboLineEditFont, qt_mac_qfontForThemeFont(kThemeViewsFont)); + fonts.insert(QPlatformTheme::SmallFont, qt_mac_qfontForThemeFont(kThemeSmallSystemFont)); + fonts.insert(QPlatformTheme::MiniFont, qt_mac_qfontForThemeFont(kThemeMiniSystemFont)); + + return fonts; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h index dccda2ce3b..030db1822c 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.h +++ b/src/plugins/platforms/cocoa/qcocoatheme.h @@ -61,11 +61,13 @@ public: QPlatformDialogHelper *createPlatformDialogHelper(DialogType dialogType) const; const QPalette *palette(Palette type = SystemPalette) const; + const QFont *font(Font type = SystemFont) const; QVariant themeHint(ThemeHint hint) const; private: mutable QPalette *m_systemPalette; mutable QHash m_palettes; + mutable QHash m_fonts; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index 71d7c9e294..8ec6e3801e 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -118,6 +118,14 @@ const QPalette *QCocoaTheme::palette(Palette type) const return 0; } +const QFont *QCocoaTheme::font(Font type) const +{ + if (m_fonts.isEmpty()) { + m_fonts = qt_mac_createRoleFonts(); + } + return m_fonts.value(type, 0); +} + QVariant QCocoaTheme::themeHint(ThemeHint hint) const { switch (hint) { diff --git a/src/plugins/platforms/windows/qtwindows_additional.h b/src/plugins/platforms/windows/qtwindows_additional.h index d82240255e..ac768e2dab 100644 --- a/src/plugins/platforms/windows/qtwindows_additional.h +++ b/src/plugins/platforms/windows/qtwindows_additional.h @@ -61,7 +61,8 @@ # define FE_FONTSMOOTHINGCLEARTYPE 0x0002 # define CLEARTYPE_QUALITY 5 # define SPI_GETDROPSHADOW 0x1024 - +# define COLOR_MENUHILIGHT 29 +# define COLOR_MENUBAR 30 # define CF_DIBV5 17 #define CO_E_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x80004021L) diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp index fba7794a12..5094ad9dc3 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qwindowsfontdatabase.h" +#include "qwindowsfontdatabase_ft.h" // for default font #include "qwindowscontext.h" #include "qwindowsfontengine.h" #include "qwindowsfontenginedirectwrite.h" @@ -1066,53 +1067,7 @@ static inline int verticalDPI() QFont QWindowsFontDatabase::defaultFont() const { - LOGFONT lf; - GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(lf), &lf); - QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(lf); - // "MS Shell Dlg 2" is the correct system font >= Win2k - if (systemFont.family() == QStringLiteral("MS Shell Dlg")) - systemFont.setFamily(QStringLiteral("MS Shell Dlg 2")); - if (QWindowsContext::verboseFonts) - qDebug() << __FUNCTION__ << systemFont; - return systemFont; -} - -QHash QWindowsFontDatabase::defaultFonts() const -{ - QHash result; - NONCLIENTMETRICS ncm; - ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0); - - const int verticalRes = verticalDPI(); - - const QFont menuFont = LOGFONT_to_QFont(ncm.lfMenuFont, verticalRes); - const QFont messageFont = LOGFONT_to_QFont(ncm.lfMessageFont, verticalRes); - const QFont statusFont = LOGFONT_to_QFont(ncm.lfStatusFont, verticalRes); - const QFont titleFont = LOGFONT_to_QFont(ncm.lfCaptionFont, verticalRes); - - LOGFONT lfIconTitleFont; - SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0); - const QFont iconTitleFont = LOGFONT_to_QFont(lfIconTitleFont, verticalRes); - - result.insert(QByteArray("QMenu"), menuFont); - result.insert(QByteArray("QMenuBar"), menuFont); - result.insert(QByteArray("QMessageBox"), messageFont); - result.insert(QByteArray("QTipLabel"), statusFont); - result.insert(QByteArray("QStatusBar"), statusFont); - result.insert(QByteArray("Q3TitleBar"), titleFont); - result.insert(QByteArray("QWorkspaceTitleBar"), titleFont); - result.insert(QByteArray("QAbstractItemView"), iconTitleFont); - result.insert(QByteArray("QDockWidgetTitle"), iconTitleFont); - if (QWindowsContext::verboseFonts) { - typedef QHash::const_iterator CIT; - QDebug nsp = qDebug().nospace(); - nsp << __FUNCTION__ << " DPI=" << verticalRes << "\n"; - const CIT cend = result.constEnd(); - for (CIT it = result.constBegin(); it != cend; ++it) - nsp << it.key() << ' ' << it.value() << '\n'; - } - return result; + return QWindowsFontDatabaseFT::systemDefaultFont(); } QFont QWindowsFontDatabase::LOGFONT_to_QFont(const LOGFONT& logFont, int verticalDPI_In) diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.h b/src/plugins/platforms/windows/qwindowsfontdatabase.h index b08b682991..04d6ccdd91 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.h +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.h @@ -86,7 +86,6 @@ public: virtual QString fontDir() const; virtual QFont defaultFont() const; - virtual QHash defaultFonts() const; static QFontEngine *createEngine(int script, const QFontDef &request, HDC fontHdc, int dpi, bool rawMode, diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp index e972ae23ee..fcce87d0bd 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp @@ -460,7 +460,7 @@ static inline int verticalDPI() return GetDeviceCaps(QWindowsContext::instance()->displayContext(), LOGPIXELSY); } -QFont QWindowsFontDatabaseFT::defaultFont() const +QFont QWindowsFontDatabaseFT::systemDefaultFont() { LOGFONT lf; GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(lf), &lf); @@ -473,44 +473,6 @@ QFont QWindowsFontDatabaseFT::defaultFont() const return systemFont; } -QHash QWindowsFontDatabaseFT::defaultFonts() const -{ - QHash result; - NONCLIENTMETRICS ncm; - ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0); - - const int verticalRes = verticalDPI(); - - const QFont menuFont = LOGFONT_to_QFont(ncm.lfMenuFont, verticalRes); - const QFont messageFont = LOGFONT_to_QFont(ncm.lfMessageFont, verticalRes); - const QFont statusFont = LOGFONT_to_QFont(ncm.lfStatusFont, verticalRes); - const QFont titleFont = LOGFONT_to_QFont(ncm.lfCaptionFont, verticalRes); - - LOGFONT lfIconTitleFont; - SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0); - const QFont iconTitleFont = LOGFONT_to_QFont(lfIconTitleFont, verticalRes); - - result.insert(QByteArray("QMenu"), menuFont); - result.insert(QByteArray("QMenuBar"), menuFont); - result.insert(QByteArray("QMessageBox"), messageFont); - result.insert(QByteArray("QTipLabel"), statusFont); - result.insert(QByteArray("QStatusBar"), statusFont); - result.insert(QByteArray("Q3TitleBar"), titleFont); - result.insert(QByteArray("QWorkspaceTitleBar"), titleFont); - result.insert(QByteArray("QAbstractItemView"), iconTitleFont); - result.insert(QByteArray("QDockWidgetTitle"), iconTitleFont); - if (QWindowsContext::verboseFonts) { - typedef QHash::const_iterator CIT; - QDebug nsp = qDebug().nospace(); - nsp << __FUNCTION__ << " DPI=" << verticalRes << "\n"; - const CIT cend = result.constEnd(); - for (CIT it = result.constBegin(); it != cend; ++it) - nsp << it.key() << ' ' << it.value() << '\n'; - } - return result; -} - QFont QWindowsFontDatabaseFT::LOGFONT_to_QFont(const LOGFONT& logFont, int verticalDPI_In) { if (verticalDPI_In <= 0) diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h index 5a0c4c6377..4136b75dd8 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h @@ -59,8 +59,9 @@ public: QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName); virtual QString fontDir() const; - virtual QFont defaultFont() const; - virtual QHash defaultFonts() const; + virtual QFont defaultFont() const { return systemDefaultFont(); } + static QFont systemDefaultFont(); + static HFONT systemFont(); static QFont LOGFONT_to_QFont(const LOGFONT& lf, int verticalDPI = 0); diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 5350b3ca3f..10b4682ad8 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -44,6 +44,7 @@ #include "qwindowscontext.h" #include "qwindowsintegration.h" #include "qt_windows.h" +#include "qwindowsfontdatabase_ft.h" #include #include @@ -76,6 +77,22 @@ static inline QString paletteToString(const QPalette &palette) return result; } +static inline bool booleanSystemParametersInfo(UINT what, bool defaultValue) +{ + BOOL result; + if (SystemParametersInfo(what, 0, &result, 0)) + return result ? true : false; + return defaultValue; +} + +static inline bool dWordSystemParametersInfo(UINT what, DWORD defaultValue) +{ + DWORD result; + if (SystemParametersInfo(what, 0, &result, 0)) + return result; + return defaultValue; +} + static inline QColor mixColors(const QColor &c1, const QColor &c2) { return QColor ((c1.red() + c2.red()) / 2, @@ -138,7 +155,7 @@ static inline QPalette systemPalette() return result; } -QPalette toolTipPalette(const QPalette &systemPalette) +static inline QPalette toolTipPalette(const QPalette &systemPalette) { QPalette result(systemPalette); const QColor tipBgColor(getSysColor(COLOR_INFOBK)); @@ -163,24 +180,58 @@ QPalette toolTipPalette(const QPalette &systemPalette) return result; } -static inline bool booleanSystemParametersInfo(UINT what, bool defaultValue) +static inline QPalette menuPalette(const QPalette &systemPalette) { - BOOL result; - if (SystemParametersInfo(what, 0, &result, 0)) - return result ? true : false; - return defaultValue; + QPalette result(systemPalette); + const QColor menuColor(getSysColor(COLOR_INFOBK)); + const QColor menuTextColor(getSysColor(COLOR_MENUTEXT)); + const QColor disabled(getSysColor(COLOR_GRAYTEXT)); + const bool isFlat = booleanSystemParametersInfo(SPI_GETFLATMENU, false); + // we might need a special color group for the result. + result.setColor(QPalette::Active, QPalette::Button, menuColor); + result.setColor(QPalette::Active, QPalette::Text, menuTextColor); + result.setColor(QPalette::Active, QPalette::WindowText, menuTextColor); + result.setColor(QPalette::Active, QPalette::ButtonText, menuTextColor); + result.setColor(QPalette::Disabled, QPalette::WindowText, disabled); + result.setColor(QPalette::Disabled, QPalette::Text, disabled); + result.setColor(QPalette::Disabled, QPalette::Highlight, + getSysColor(isFlat ? COLOR_MENUHILIGHT : COLOR_HIGHLIGHT)); + result.setColor(QPalette::Disabled, QPalette::HighlightedText, disabled); + result.setColor(QPalette::Disabled, QPalette::Button, + result.color(QPalette::Active, QPalette::Button)); + result.setColor(QPalette::Inactive, QPalette::Button, + result.color(QPalette::Active, QPalette::Button)); + result.setColor(QPalette::Inactive, QPalette::Text, + result.color(QPalette::Active, QPalette::Text)); + result.setColor(QPalette::Inactive, QPalette::WindowText, + result.color(QPalette::Active, QPalette::WindowText)); + result.setColor(QPalette::Inactive, QPalette::ButtonText, + result.color(QPalette::Active, QPalette::ButtonText)); + result.setColor(QPalette::Inactive, QPalette::Highlight, + result.color(QPalette::Active, QPalette::Highlight)); + result.setColor(QPalette::Inactive, QPalette::HighlightedText, + result.color(QPalette::Active, QPalette::HighlightedText)); + result.setColor(QPalette::Inactive, QPalette::ButtonText, + systemPalette.color(QPalette::Inactive, QPalette::Dark)); + return result; } -static inline bool dWordSystemParametersInfo(UINT what, DWORD defaultValue) +static inline QPalette *menuBarPalette(const QPalette &menuPalette) { - DWORD result; - if (SystemParametersInfo(what, 0, &result, 0)) - return result; - return defaultValue; + QPalette *result = 0; + if (booleanSystemParametersInfo(SPI_GETFLATMENU, false)) { + result = new QPalette(menuPalette); + const QColor menubar(getSysColor(COLOR_MENUBAR)); + result->setColor(QPalette::Active, QPalette::Button, menubar); + result->setColor(QPalette::Disabled, QPalette::Button, menubar); + result->setColor(QPalette::Inactive, QPalette::Button, menubar); + } + return result; } QWindowsTheme::QWindowsTheme() { + qFill(m_fonts, m_fonts + NFonts, static_cast(0)); qFill(m_palettes, m_palettes + NPalettes, static_cast(0)); refresh(); } @@ -188,12 +239,7 @@ QWindowsTheme::QWindowsTheme() QWindowsTheme::~QWindowsTheme() { clearPalettes(); -} - -void QWindowsTheme::clearPalettes() -{ - qDeleteAll(m_palettes, m_palettes + NPalettes); - qFill(m_palettes, m_palettes + NPalettes, static_cast(0)); + clearFonts(); } QWindowsTheme *QWindowsTheme::instance() @@ -243,17 +289,65 @@ QVariant QWindowsTheme::themeHint(ThemeHint hint) const return QPlatformTheme::themeHint(hint); } -void QWindowsTheme::refresh() +void QWindowsTheme::clearPalettes() { - clearPalettes(); - if (QGuiApplication::desktopSettingsAware()) { - m_palettes[SystemPalette] = new QPalette(systemPalette()); - m_palettes[ToolTipPalette] = new QPalette(toolTipPalette(*m_palettes[SystemPalette])); - if (QWindowsContext::verboseTheming) - qDebug() << __FUNCTION__ << '\n' - << " system=" << paletteToString(*m_palettes[SystemPalette]) - << " tooltip=" << paletteToString(*m_palettes[ToolTipPalette]); - } + qDeleteAll(m_palettes, m_palettes + NPalettes); + qFill(m_palettes, m_palettes + NPalettes, static_cast(0)); +} + +void QWindowsTheme::refreshPalettes() +{ + + if (!QGuiApplication::desktopSettingsAware()) + return; + m_palettes[SystemPalette] = new QPalette(systemPalette()); + m_palettes[ToolTipPalette] = new QPalette(toolTipPalette(*m_palettes[SystemPalette])); + m_palettes[MenuPalette] = new QPalette(menuPalette(*m_palettes[SystemPalette])); + m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette]); + if (QWindowsContext::verboseTheming) + qDebug() << __FUNCTION__ << '\n' + << " system=" << paletteToString(*m_palettes[SystemPalette]) + << " tooltip=" << paletteToString(*m_palettes[ToolTipPalette]); +} + +void QWindowsTheme::clearFonts() +{ + qDeleteAll(m_fonts, m_fonts + NFonts); + qFill(m_fonts, m_fonts + NFonts, static_cast(0)); +} + +void QWindowsTheme::refreshFonts() +{ + clearFonts(); + if (!QGuiApplication::desktopSettingsAware()) + return; + NONCLIENTMETRICS ncm; + ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0); + + const QFont menuFont = QWindowsFontDatabaseFT::LOGFONT_to_QFont(ncm.lfMenuFont); + const QFont messageBoxFont = QWindowsFontDatabaseFT::LOGFONT_to_QFont(ncm.lfMessageFont); + const QFont statusFont = QWindowsFontDatabaseFT::LOGFONT_to_QFont(ncm.lfStatusFont); + const QFont titleFont = QWindowsFontDatabaseFT::LOGFONT_to_QFont(ncm.lfCaptionFont); + + LOGFONT lfIconTitleFont; + SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0); + const QFont iconTitleFont = QWindowsFontDatabaseFT::LOGFONT_to_QFont(lfIconTitleFont); + + m_fonts[SystemFont] = new QFont(QWindowsFontDatabaseFT::systemDefaultFont()); + m_fonts[MenuFont] = new QFont(menuFont); + m_fonts[MenuBarFont] = new QFont(menuFont); + m_fonts[MessageBoxFont] = new QFont(messageBoxFont); + m_fonts[TipLabelFont] = new QFont(statusFont); + m_fonts[StatusBarFont] = new QFont(statusFont); + m_fonts[MdiSubWindowTitleFont] = new QFont(titleFont); + m_fonts[DockWidgetTitleFont] = new QFont(titleFont); + m_fonts[ItemViewFont] = new QFont(iconTitleFont); + + if (QWindowsContext::verboseTheming) + qDebug() << __FUNCTION__ << '\n' + << " menuFont=" << menuFont + << " messageBox=" << MessageBoxFont; } bool QWindowsTheme::usePlatformNativeDialog(DialogType type) const diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h index 950c380737..37346eed3a 100644 --- a/src/plugins/platforms/windows/qwindowstheme.h +++ b/src/plugins/platforms/windows/qwindowstheme.h @@ -64,14 +64,20 @@ public: virtual QVariant themeHint(ThemeHint) const; virtual const QPalette *palette(Palette type = SystemPalette) const { return m_palettes[type]; } + virtual const QFont *font(Font type = SystemFont) const + { return m_fonts[type]; } void windowsThemeChanged(QWindow *window); private: - void refresh(); + void refresh() { refreshPalettes(); refreshFonts(); } void clearPalettes(); + void refreshPalettes(); + void clearFonts(); + void refreshFonts(); QPalette *m_palettes[NPalettes]; + QFont *m_fonts[NFonts]; }; static inline COLORREF qColorToCOLORREF(const QColor &color) diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index bf2729a6d8..90b64db579 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -431,11 +431,6 @@ PaletteHash *qt_app_palettes_hash() return app_palettes(); } -FontHash::FontHash() -{ - QHash::operator=(QGuiApplicationPrivate::platformIntegration()->fontDatabase()->defaultFonts()); -} - Q_GLOBAL_STATIC(FontHash, app_fonts) FontHash *qt_app_fonts_hash() { diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h index ae4f0c2044..2d639172e2 100644 --- a/src/widgets/kernel/qapplication_p.h +++ b/src/widgets/kernel/qapplication_p.h @@ -157,10 +157,7 @@ QMacTabletHash *qt_mac_tablet_hash(); # endif #endif -struct FontHash : public QHash -{ - FontHash(); -}; +typedef QHash FontHash; FontHash *qt_app_fonts_hash(); typedef QHash PaletteHash; @@ -292,6 +289,7 @@ public: static void setSystemPalette(const QPalette &pal); static void setPalette_helper(const QPalette &palette, const char* className, bool clearWidgetPaletteHash); static void initializeWidgetPaletteHash(); + static void initializeWidgetFontHash(); static void setSystemFont(const QFont &font); #if defined(Q_WS_X11) diff --git a/src/widgets/kernel/qapplication_qpa.cpp b/src/widgets/kernel/qapplication_qpa.cpp index 6c91b89674..97fc794252 100644 --- a/src/widgets/kernel/qapplication_qpa.cpp +++ b/src/widgets/kernel/qapplication_qpa.cpp @@ -318,6 +318,58 @@ void QApplicationPrivate::initializeWidgetPaletteHash() setPossiblePalette(platformTheme->palette(QPlatformTheme::TextLineEditPalette), "QLineEdit"); } +void QApplicationPrivate::initializeWidgetFontHash() +{ + const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme(); + if (!theme) + return; + FontHash *fontHash = qt_app_fonts_hash(); + if (const QFont *font = theme->font(QPlatformTheme::MenuFont)) + fontHash->insert(QByteArrayLiteral("QMenu"), *font); + if (const QFont *font = theme->font(QPlatformTheme::MenuBarFont)) + fontHash->insert(QByteArrayLiteral("QMenuBar"), *font); + if (const QFont *font = theme->font(QPlatformTheme::MenuItemFont)) + fontHash->insert(QByteArrayLiteral("QMenuItem"), *font); + if (const QFont *font = theme->font(QPlatformTheme::MessageBoxFont)) + fontHash->insert(QByteArrayLiteral("QMessageBox"), *font); + if (const QFont *font = theme->font(QPlatformTheme::LabelFont)) + fontHash->insert(QByteArrayLiteral("QLabel"), *font); + if (const QFont *font = theme->font(QPlatformTheme::TipLabelFont)) + fontHash->insert(QByteArrayLiteral("QTipLabel"), *font); + if (const QFont *font = theme->font(QPlatformTheme::TitleBarFont)) + fontHash->insert(QByteArrayLiteral("QTitleBar"), *font); + if (const QFont *font = theme->font(QPlatformTheme::StatusBarFont)) + fontHash->insert(QByteArrayLiteral("QStatusBar"), *font); + if (const QFont *font = theme->font(QPlatformTheme::MdiSubWindowTitleFont)) { + fontHash->insert(QByteArrayLiteral("QWorkspaceTitleBar"), *font); + fontHash->insert(QByteArrayLiteral("QMdiSubWindowTitleBar"), *font); + } + if (const QFont *font = theme->font(QPlatformTheme::DockWidgetTitleFont)) + fontHash->insert(QByteArrayLiteral("QDockWidgetTitle"), *font); + if (const QFont *font = theme->font(QPlatformTheme::PushButtonFont)) + fontHash->insert(QByteArrayLiteral("QPushButton"), *font); + if (const QFont *font = theme->font(QPlatformTheme::ToolButtonFont)) + fontHash->insert(QByteArrayLiteral("QToolButton"), *font); + if (const QFont *font = theme->font(QPlatformTheme::ItemViewFont)) + fontHash->insert(QByteArrayLiteral("QAbstractItemView"), *font); + if (const QFont *font = theme->font(QPlatformTheme::ListViewFont)) + fontHash->insert(QByteArrayLiteral("QListViewFont"), *font); + if (const QFont *font = theme->font(QPlatformTheme::HeaderViewFont)) { + fontHash->insert(QByteArrayLiteral("QHeaderViewFont"), *font); + fontHash->insert(QByteArrayLiteral("Q3Header"), *font); + } + if (const QFont *font = theme->font(QPlatformTheme::ListBoxFont)) + fontHash->insert(QByteArrayLiteral("QListBox"), *font); + if (const QFont *font = theme->font(QPlatformTheme::ComboMenuItemFont)) + fontHash->insert(QByteArrayLiteral("QComboMenuItemFont"), *font); + if (const QFont *font = theme->font(QPlatformTheme::ComboLineEditFont)) + fontHash->insert(QByteArrayLiteral("QComboLineEditFont"), *font); + if (const QFont *font = theme->font(QPlatformTheme::SmallFont)) + fontHash->insert(QByteArrayLiteral("QSmallFont"), *font); + if (const QFont *font = theme->font(QPlatformTheme::MiniFont)) + fontHash->insert(QByteArrayLiteral("QMiniFont"), *font); +} + #ifndef QT_NO_WHEELEVENT void QApplication::setWheelScrollLines(int lines) { @@ -416,6 +468,7 @@ QPlatformNativeInterface *QApplication::platformNativeInterface() void qt_init(QApplicationPrivate *priv, int type) { + Q_UNUSED(priv); Q_UNUSED(type); qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); @@ -423,6 +476,7 @@ void qt_init(QApplicationPrivate *priv, int type) if (const QPalette *toolTipPalette = QGuiApplicationPrivate::platformTheme()->palette(QPlatformTheme::ToolTipPalette)) QToolTip::setPalette(*toolTipPalette); + QApplicationPrivate::initializeWidgetFontHash(); qApp->setObjectName(appName); } diff --git a/src/widgets/widgets/qmdisubwindow.cpp b/src/widgets/widgets/qmdisubwindow.cpp index b42fedf0be..af75d6b8aa 100644 --- a/src/widgets/widgets/qmdisubwindow.cpp +++ b/src/widgets/widgets/qmdisubwindow.cpp @@ -2260,7 +2260,7 @@ QMdiSubWindow::QMdiSubWindow(QWidget *parent, Qt::WindowFlags flags) d->updateGeometryConstraints(); setAttribute(Qt::WA_Resized, false); d->titleBarPalette = d->desktopPalette(); - d->font = QApplication::font("QWorkspaceTitleBar"); + d->font = QApplication::font("QMdiSubWindowTitleBar"); // We don't want the menu icon by default on mac. #ifndef Q_WS_MAC if (windowIcon().isNull()) -- cgit v1.2.3 From 5713dde8a1e6a35483134ffe9d986db66b208cf5 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 7 Mar 2012 12:24:30 +0100 Subject: Fix deadlock in QPropertyAnimation Commit 1e6514a714c1f55b9cb57d2b8b65bc2305c2e2c6 changed the mutex from recursive to non-recursive, which could introduce dead lock if the animation starts other animation (This is the case in QMainWindow layouts) Change-Id: I1b149b78a802748eb24b5700fffeca0b8555f005 Reviewed-by: Friedemann Kleint --- src/corelib/animation/qpropertyanimation.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/corelib/animation/qpropertyanimation.cpp b/src/corelib/animation/qpropertyanimation.cpp index 83ae7bafaa..f7ba49c3cd 100644 --- a/src/corelib/animation/qpropertyanimation.cpp +++ b/src/corelib/animation/qpropertyanimation.cpp @@ -281,6 +281,7 @@ void QPropertyAnimation::updateState(QAbstractAnimation::State newState, d->updateMetaProperty(); animToStop = hash.value(key, 0); hash.insert(key, this); + locker.unlock(); // update the default start value if (oldState == Stopped) { d->setDefaultStartEndValue(d->targetValue->property(d->propertyName.constData())); -- cgit v1.2.3 From 24be238fccee67ad060247ad22e15b6bddd506e0 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 7 Mar 2012 14:46:04 +0100 Subject: Cocoa: Implement Drag-and-Drop. Implement drag and drop support for drags originating from outside Qt. Port mime and pasteboard code from Qt 4. Use QSimpleDrag from from platform support to implement internal Qt drags. Change-Id: I5b664a95ebb00f48de2bd21c24dfb579af16123e Reviewed-by: Friedemann Kleint --- src/plugins/platforms/cocoa/cocoa.pro | 6 + src/plugins/platforms/cocoa/qcocoadrag.h | 76 ++ src/plugins/platforms/cocoa/qcocoadrag.mm | 99 +++ src/plugins/platforms/cocoa/qcocoahelpers.h | 5 + src/plugins/platforms/cocoa/qcocoahelpers.mm | 58 ++ src/plugins/platforms/cocoa/qcocoaintegration.h | 3 + src/plugins/platforms/cocoa/qcocoaintegration.mm | 9 +- src/plugins/platforms/cocoa/qmacclipboard.h | 93 +++ src/plugins/platforms/cocoa/qmacclipboard.mm | 653 ++++++++++++++++ src/plugins/platforms/cocoa/qmacmime.h | 77 ++ src/plugins/platforms/cocoa/qmacmime.mm | 938 +++++++++++++++++++++++ src/plugins/platforms/cocoa/qnsview.h | 4 + src/plugins/platforms/cocoa/qnsview.mm | 74 ++ src/plugins/platforms/cocoa/qt_mac_p.h | 38 - 14 files changed, 2094 insertions(+), 39 deletions(-) create mode 100644 src/plugins/platforms/cocoa/qcocoadrag.h create mode 100644 src/plugins/platforms/cocoa/qcocoadrag.mm create mode 100644 src/plugins/platforms/cocoa/qmacclipboard.h create mode 100644 src/plugins/platforms/cocoa/qmacclipboard.mm create mode 100644 src/plugins/platforms/cocoa/qmacmime.h create mode 100644 src/plugins/platforms/cocoa/qmacmime.mm (limited to 'src') diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index ce87de2574..b953210720 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -27,6 +27,9 @@ OBJECTIVE_SOURCES += main.mm \ qcocoafiledialoghelper.mm \ qcocoafontdialoghelper.mm \ qcocoacursor.mm \ + qcocoadrag.mm \ + qmacclipboard.mm \ + qmacmime.mm \ qcocoasystemsettings.mm \ HEADERS += qcocoaintegration.h \ @@ -52,6 +55,9 @@ HEADERS += qcocoaintegration.h \ qcocoafiledialoghelper.h \ qcocoafontdialoghelper.h \ qcocoacursor.h \ + qcocoadrag.h \ + qmacclipboard.h \ + qmacmime.h \ qcocoasystemsettings.h \ FORMS += $$PWD/../../../widgets/dialogs/qfiledialog.ui diff --git a/src/plugins/platforms/cocoa/qcocoadrag.h b/src/plugins/platforms/cocoa/qcocoadrag.h new file mode 100644 index 0000000000..17df54f748 --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoadrag.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOCOADRAG_H +#define QCOCOADRAG_H + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QCocoaDrag : public QSimpleDrag +{ +public: +private: +}; + +class QCocoaDropData : public QInternalMimeData +{ +public: + QCocoaDropData(NSPasteboard *pasteboard); + ~QCocoaDropData(); +protected: + bool hasFormat_sys(const QString &mimeType) const; + QStringList formats_sys() const; + QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const; +public: + CFStringRef dropPasteboard; +}; + + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/cocoa/qcocoadrag.mm b/src/plugins/platforms/cocoa/qcocoadrag.mm new file mode 100644 index 0000000000..c596e3fdbb --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoadrag.mm @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcocoadrag.h" +#include "qmacmime.h" +#include "qmacclipboard.h" + +QT_BEGIN_NAMESPACE + +QCocoaDropData::QCocoaDropData(NSPasteboard *pasteboard) +{ + dropPasteboard = reinterpret_cast(const_cast([pasteboard name])); + CFRetain(dropPasteboard); +} + +QCocoaDropData::~QCocoaDropData() +{ + CFRelease(dropPasteboard); +} + +QStringList QCocoaDropData::formats_sys() const +{ + QStringList formats; + PasteboardRef board; + if (PasteboardCreate(dropPasteboard, &board) != noErr) { + qDebug("DnD: Cannot get PasteBoard!"); + return formats; + } + formats = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).formats(); + return formats; +} + +QVariant QCocoaDropData::retrieveData_sys(const QString &mimeType, QVariant::Type type) const +{ + QVariant data; + PasteboardRef board; + if (PasteboardCreate(dropPasteboard, &board) != noErr) { + qDebug("DnD: Cannot get PasteBoard!"); + return data; + } + data = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).retrieveData(mimeType, type); + CFRelease(board); + return data; +} + +bool QCocoaDropData::hasFormat_sys(const QString &mimeType) const +{ + bool has = false; + PasteboardRef board; + if (PasteboardCreate(dropPasteboard, &board) != noErr) { + qDebug("DnD: Cannot get PasteBoard!"); + return has; + } + has = QMacPasteboard(board, QMacPasteboardMime::MIME_DND).hasFormat(mimeType); + CFRelease(board); + return has; +} + + +QT_END_NAMESPACE + diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 7b1247b739..3e3e8fa507 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -77,6 +77,11 @@ NSSize qt_mac_toNSSize(const QSize &qtSize); QChar qt_mac_qtKey2CocoaKey(Qt::Key key); Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode); +NSDragOperation qt_mac_mapDropAction(Qt::DropAction action); +NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions); +Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions); +Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions); + // Misc void qt_mac_transformProccessToForegroundApplication(); QString qt_mac_removeMnemonics(const QString &original); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 29c505e1ab..ec4399b66c 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -270,6 +270,64 @@ Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode) return i->qtKey; } +struct dndenum_mapper +{ + NSDragOperation mac_code; + Qt::DropAction qt_code; + bool Qt2Mac; +}; + +static dndenum_mapper dnd_enums[] = { + { NSDragOperationLink, Qt::LinkAction, true }, + { NSDragOperationMove, Qt::MoveAction, true }, + { NSDragOperationCopy, Qt::CopyAction, true }, + { NSDragOperationGeneric, Qt::CopyAction, false }, + { NSDragOperationEvery, Qt::ActionMask, false }, + { NSDragOperationNone, Qt::IgnoreAction, false } +}; + +NSDragOperation qt_mac_mapDropAction(Qt::DropAction action) +{ + for (int i=0; dnd_enums[i].qt_code; i++) { + if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) { + return dnd_enums[i].mac_code; + } + } + return NSDragOperationNone; +} + +NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions) +{ + NSDragOperation nsActions = NSDragOperationNone; + for (int i=0; dnd_enums[i].qt_code; i++) { + if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code)) + nsActions |= dnd_enums[i].mac_code; + } + return nsActions; +} + +Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions) +{ + Qt::DropAction action = Qt::IgnoreAction; + for (int i=0; dnd_enums[i].mac_code; i++) { + if (nsActions & dnd_enums[i].mac_code) + return dnd_enums[i].qt_code; + } + return action; +} + +Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions) +{ + Qt::DropActions actions = Qt::IgnoreAction; + for (int i=0; dnd_enums[i].mac_code; i++) { + if (nsActions & dnd_enums[i].mac_code) + actions |= dnd_enums[i].qt_code; + } + return actions; +} + + + // // Misc // diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index 5493b21c34..aa0c933fab 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -46,6 +46,7 @@ #include "qcocoaautoreleasepool.h" #include "qcocoacursor.h" +#include "qcocoadrag.h" #include #include @@ -88,6 +89,7 @@ public: QPlatformNativeInterface *nativeInterface() const; QPlatformAccessibility *accessibility() const; + QPlatformDrag *drag() const; QPlatformTheme *platformTheme() const; private: @@ -98,6 +100,7 @@ private: QScopedPointer mAccessibility; QScopedPointer mPlatformTheme; QList mScreens; + QScopedPointer mCocoaDrag; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 626a7fe0f9..7921cc6ae7 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -52,6 +52,7 @@ #include "qmenu_mac.h" #include "qcocoafiledialoghelper.h" #include "qcocoatheme.h" +#include "qmacmime.h" #include #include @@ -91,7 +92,7 @@ QCocoaIntegration::QCocoaIntegration() , mEventDispatcher(new QCocoaEventDispatcher()) , mAccessibility(new QPlatformAccessibility) , mPlatformTheme(new QCocoaTheme) - + , mCocoaDrag(new QCocoaDrag) { QCocoaAutoReleasePool pool; @@ -138,6 +139,7 @@ QCocoaIntegration::QCocoaIntegration() screenAdded(screen); } + QMacPasteboardMime::initialize(); } QCocoaIntegration::~QCocoaIntegration() @@ -198,6 +200,11 @@ QPlatformAccessibility *QCocoaIntegration::accessibility() const return mAccessibility.data(); } +QPlatformDrag *QCocoaIntegration::drag() const +{ + return mCocoaDrag.data(); +} + QPlatformTheme *QCocoaIntegration::platformTheme() const { return mPlatformTheme.data(); diff --git a/src/plugins/platforms/cocoa/qmacclipboard.h b/src/plugins/platforms/cocoa/qmacclipboard.h new file mode 100644 index 0000000000..9371aca459 --- /dev/null +++ b/src/plugins/platforms/cocoa/qmacclipboard.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMACCLIPBOARD_H +#define QMACCLIPBOARD_H + +#include +#include "qmacmime.h" + +#undef slots + +#import + +class QMacPasteboard +{ + struct Promise { + Promise() : itemId(0), convertor(0) { } + Promise(int itemId, QMacPasteboardMime *c, QString m, QVariant d, int o=0) : itemId(itemId), offset(o), convertor(c), mime(m), data(d) { } + int itemId, offset; + QMacPasteboardMime *convertor; + QString mime; + QVariant data; + }; + QList promises; + + PasteboardRef paste; + uchar mime_type; + mutable QPointer mime; + mutable bool mac_mime_source; + static OSStatus promiseKeeper(PasteboardRef, PasteboardItemID, CFStringRef, void *); + void clear_helper(); +public: + QMacPasteboard(PasteboardRef p, uchar mime_type=0); + QMacPasteboard(uchar mime_type); + QMacPasteboard(CFStringRef name=0, uchar mime_type=0); + ~QMacPasteboard(); + + bool hasFlavor(QString flavor) const; + bool hasOSType(int c_flavor) const; + + PasteboardRef pasteBoard() const; + QMimeData *mimeData() const; + void setMimeData(QMimeData *mime); + + QStringList formats() const; + bool hasFormat(const QString &format) const; + QVariant retrieveData(const QString &format, QVariant::Type) const; + + void clear(); + bool sync() const; +}; + +QString qt_mac_get_pasteboardString(PasteboardRef paste); + +#endif diff --git a/src/plugins/platforms/cocoa/qmacclipboard.mm b/src/plugins/platforms/cocoa/qmacclipboard.mm new file mode 100644 index 0000000000..d5af6de69c --- /dev/null +++ b/src/plugins/platforms/cocoa/qmacclipboard.mm @@ -0,0 +1,653 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmacclipboard.h" +#include "qclipboard.h" +#include "qapplication.h" +#include "qbitmap.h" +#include "qdatetime.h" +#include "qdebug.h" +#include "qguiapplication.h" +#include "qevent.h" +#include "qurl.h" +#include +#include +#include "qcocoahelpers.h" +#include "qmacmime.h" +#include "qcocoaautoreleasepool.h" + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +/***************************************************************************** + QClipboard debug facilities + *****************************************************************************/ +//#define DEBUG_PASTEBOARD + +#ifndef QT_NO_CLIPBOARD + +/***************************************************************************** + QClipboard member functions for mac. + *****************************************************************************/ + +static QMacPasteboard *qt_mac_pasteboards[2] = {0, 0}; + +static inline QMacPasteboard *qt_mac_pasteboard(QClipboard::Mode mode) +{ + Q_ASSERT(mode == QClipboard::Clipboard || mode == QClipboard::FindBuffer); + if (mode == QClipboard::Clipboard) + return qt_mac_pasteboards[0]; + else + return qt_mac_pasteboards[1]; +} + +static void qt_mac_cleanupPasteboard() { + delete qt_mac_pasteboards[0]; + delete qt_mac_pasteboards[1]; + qt_mac_pasteboards[0] = 0; + qt_mac_pasteboards[1] = 0; +} + +static bool qt_mac_updateScrap(QClipboard::Mode mode) +{ + if (!qt_mac_pasteboards[0]) { + qt_mac_pasteboards[0] = new QMacPasteboard(kPasteboardClipboard, QMacPasteboardMime::MIME_CLIP); + qt_mac_pasteboards[1] = new QMacPasteboard(kPasteboardFind, QMacPasteboardMime::MIME_CLIP); + qAddPostRoutine(qt_mac_cleanupPasteboard); + return true; + } + return qt_mac_pasteboard(mode)->sync(); +} + +void QClipboard::clear(Mode mode) +{ + if (!supportsMode(mode)) + return; + qt_mac_updateScrap(mode); + qt_mac_pasteboard(mode)->clear(); + setMimeData(0, mode); +} + +void QClipboard::ownerDestroyed() +{ +} + + +void QClipboard::connectNotify(const char *signal) +{ + Q_UNUSED(signal); +} + +bool QClipboard::event(QEvent *e) +{ + if (e->type() != QEvent::Clipboard) + return QObject::event(e); + + if (qt_mac_updateScrap(QClipboard::Clipboard)) { + emitChanged(QClipboard::Clipboard); + } + + if (qt_mac_updateScrap(QClipboard::FindBuffer)) { + emitChanged(QClipboard::FindBuffer); + } + + return QObject::event(e); +} + +const QMimeData *QClipboard::mimeData(Mode mode) const +{ + if (!supportsMode(mode)) + return 0; + qt_mac_updateScrap(mode); + return qt_mac_pasteboard(mode)->mimeData(); +} + +void QClipboard::setMimeData(QMimeData *src, Mode mode) +{ + if (!supportsMode(mode)) + return; + qt_mac_updateScrap(mode); + qt_mac_pasteboard(mode)->setMimeData(src); + emitChanged(mode); +} + +bool QClipboard::supportsMode(Mode mode) const +{ + return (mode == Clipboard || mode == FindBuffer); +} + +bool QClipboard::ownsMode(Mode mode) const +{ + Q_UNUSED(mode); + return false; +} + +#endif // QT_NO_CLIPBOARD + +/***************************************************************************** + QMacPasteboard code +*****************************************************************************/ + +QMacPasteboard::QMacPasteboard(PasteboardRef p, uchar mt) +{ + mac_mime_source = false; + mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL); + paste = p; + CFRetain(paste); +} + +QMacPasteboard::QMacPasteboard(uchar mt) +{ + mac_mime_source = false; + mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL); + paste = 0; + OSStatus err = PasteboardCreate(0, &paste); + if (err == noErr) { + PasteboardSetPromiseKeeper(paste, promiseKeeper, this); + } else { + qDebug("PasteBoard: Error creating pasteboard: [%d]", (int)err); + } +} + +QMacPasteboard::QMacPasteboard(CFStringRef name, uchar mt) +{ + mac_mime_source = false; + mime_type = mt ? mt : uchar(QMacPasteboardMime::MIME_ALL); + paste = 0; + OSStatus err = PasteboardCreate(name, &paste); + if (err == noErr) { + PasteboardSetPromiseKeeper(paste, promiseKeeper, this); + } else { + qDebug("PasteBoard: Error creating pasteboard: %s [%d]", QCFString::toQString(name).toLatin1().constData(), (int)err); + } +} + +QMacPasteboard::~QMacPasteboard() +{ + // commit all promises for paste after exit close + for (int i = 0; i < promises.count(); ++i) { + const Promise &promise = promises.at(i); + QCFString flavor = QCFString(promise.convertor->flavorFor(promise.mime)); + promiseKeeper(paste, (PasteboardItemID)promise.itemId, flavor, this); + } + + if (paste) + CFRelease(paste); +} + +PasteboardRef +QMacPasteboard::pasteBoard() const +{ + return paste; +} + +OSStatus QMacPasteboard::promiseKeeper(PasteboardRef paste, PasteboardItemID id, CFStringRef flavor, void *_qpaste) +{ + QMacPasteboard *qpaste = (QMacPasteboard*)_qpaste; + const long promise_id = (long)id; + + // Find the kept promise + const QString flavorAsQString = QCFString::toQString(flavor); + QMacPasteboard::Promise promise; + for (int i = 0; i < qpaste->promises.size(); i++){ + QMacPasteboard::Promise tmp = qpaste->promises[i]; + if (tmp.itemId == promise_id && tmp.convertor->canConvert(tmp.mime, flavorAsQString)){ + promise = tmp; + break; + } + } + + if (!promise.itemId && flavorAsQString == QLatin1String("com.trolltech.qt.MimeTypeName")) { + // we have promised this data, but wont be able to convert, so return null data. + // This helps in making the application/x-qt-mime-type-name hidden from normal use. + QByteArray ba; + QCFType data = CFDataCreate(0, (UInt8*)ba.constData(), ba.size()); + PasteboardPutItemFlavor(paste, id, flavor, data, kPasteboardFlavorNoFlags); + return noErr; + } + + if (!promise.itemId) { + // There was no promise that could deliver data for the + // given id and flavor. This should not happend. + qDebug("Pasteboard: %d: Request for %ld, %s, but no promise found!", __LINE__, promise_id, qPrintable(flavorAsQString)); + return cantGetFlavorErr; + } + +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: Calling in promise for %s[%ld] [%s] (%s) [%d]", qPrintable(promise.mime), promise_id, + qPrintable(flavorAsQString), qPrintable(promise.convertor->convertorName()), promise.offset); +#endif + + QList md = promise.convertor->convertFromMime(promise.mime, promise.data, flavorAsQString); + if (md.size() <= promise.offset) + return cantGetFlavorErr; + const QByteArray &ba = md[promise.offset]; + QCFType data = CFDataCreate(0, (UInt8*)ba.constData(), ba.size()); + PasteboardPutItemFlavor(paste, id, flavor, data, kPasteboardFlavorNoFlags); + return noErr; +} + +bool +QMacPasteboard::hasOSType(int c_flavor) const +{ + if (!paste) + return false; + + sync(); + + ItemCount cnt = 0; + if (PasteboardGetItemCount(paste, &cnt) || !cnt) + return false; + +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: hasOSType [%c%c%c%c]", (c_flavor>>24)&0xFF, (c_flavor>>16)&0xFF, + (c_flavor>>8)&0xFF, (c_flavor>>0)&0xFF); +#endif + for (uint index = 1; index <= cnt; ++index) { + + PasteboardItemID id; + if (PasteboardGetItemIdentifier(paste, index, &id) != noErr) + return false; + + QCFType types; + if (PasteboardCopyItemFlavors(paste, id, &types ) != noErr) + return false; + + const int type_count = CFArrayGetCount(types); + for (int i = 0; i < type_count; ++i) { + CFStringRef flavor = (CFStringRef)CFArrayGetValueAtIndex(types, i); + const int os_flavor = UTGetOSTypeFromString(UTTypeCopyPreferredTagWithClass(flavor, kUTTagClassOSType)); + if (os_flavor == c_flavor) { +#ifdef DEBUG_PASTEBOARD + qDebug(" - Found!"); +#endif + return true; + } + } + } +#ifdef DEBUG_PASTEBOARD + qDebug(" - NotFound!"); +#endif + return false; +} + +bool +QMacPasteboard::hasFlavor(QString c_flavor) const +{ + if (!paste) + return false; + + sync(); + + ItemCount cnt = 0; + if (PasteboardGetItemCount(paste, &cnt) || !cnt) + return false; + +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: hasFlavor [%s]", qPrintable(c_flavor)); +#endif + for (uint index = 1; index <= cnt; ++index) { + + PasteboardItemID id; + if (PasteboardGetItemIdentifier(paste, index, &id) != noErr) + return false; + + PasteboardFlavorFlags flags; + if (PasteboardGetItemFlavorFlags(paste, id, QCFString(c_flavor), &flags) == noErr) { +#ifdef DEBUG_PASTEBOARD + qDebug(" - Found!"); +#endif + return true; + } + } +#ifdef DEBUG_PASTEBOARD + qDebug(" - NotFound!"); +#endif + return false; +} + +class QMacPasteboardMimeSource : public QMimeData { + const QMacPasteboard *paste; +public: + QMacPasteboardMimeSource(const QMacPasteboard *p) : QMimeData(), paste(p) { } + ~QMacPasteboardMimeSource() { } + virtual QStringList formats() const { return paste->formats(); } + virtual QVariant retrieveData(const QString &format, QVariant::Type type) const { return paste->retrieveData(format, type); } +}; + +QMimeData +*QMacPasteboard::mimeData() const +{ + if (!mime) { + mac_mime_source = true; + mime = new QMacPasteboardMimeSource(this); + + } + return mime; +} + +class QMacMimeData : public QMimeData +{ +public: + QVariant variantData(const QString &mime) { return retrieveData(mime, QVariant::Invalid); } +private: + QMacMimeData(); +}; + +void +QMacPasteboard::setMimeData(QMimeData *mime_src) +{ + if (!paste) + return; + + if (mime == mime_src || (!mime_src && mime && mac_mime_source)) + return; + mac_mime_source = false; + delete mime; + mime = mime_src; + + QList availableConverters = QMacPasteboardMime::all(mime_type); + if (mime != 0) { + clear_helper(); + QStringList formats = mime_src->formats(); + + // QMimeData sub classes reimplementing the formats() might not expose the + // temporary "application/x-qt-mime-type-name" mimetype. So check the existence + // of this mime type while doing drag and drop. + QString dummyMimeType(QLatin1String("application/x-qt-mime-type-name")); + if (!formats.contains(dummyMimeType)) { + QByteArray dummyType = mime_src->data(dummyMimeType); + if (!dummyType.isEmpty()) { + formats.append(dummyMimeType); + } + } + for (int f = 0; f < formats.size(); ++f) { + QString mimeType = formats.at(f); + for (QList::Iterator it = availableConverters.begin(); it != availableConverters.end(); ++it) { + QMacPasteboardMime *c = (*it); + QString flavor(c->flavorFor(mimeType)); + if (!flavor.isEmpty()) { + QVariant mimeData = static_cast(mime_src)->variantData(mimeType); +#if 0 + //### Grrr, why didn't I put in a virtual int QMacPasteboardMime::count()? --Sam + const int numItems = c->convertFromMime(mimeType, mimeData, flavor).size(); +#else + int numItems = 1; //this is a hack but it is much faster than allowing conversion above + if (c->convertorName() == QLatin1String("FileURL")) + numItems = mime_src->urls().count(); +#endif + for (int item = 0; item < numItems; ++item) { + const int itemID = item+1; //id starts at 1 + promises.append(QMacPasteboard::Promise(itemID, c, mimeType, mimeData, item)); + PasteboardPutItemFlavor(paste, (PasteboardItemID)itemID, QCFString(flavor), 0, kPasteboardFlavorNoFlags); +#ifdef DEBUG_PASTEBOARD + qDebug(" - adding %d %s [%s] <%s> [%d]", + itemID, qPrintable(mimeType), qPrintable(flavor), qPrintable(c->convertorName()), item); +#endif + } + } + } + } + } +} + +QStringList +QMacPasteboard::formats() const +{ + if (!paste) + return QStringList(); + + sync(); + + QStringList ret; + ItemCount cnt = 0; + if (PasteboardGetItemCount(paste, &cnt) || !cnt) + return ret; + +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: Formats [%d]", (int)cnt); +#endif + for (uint index = 1; index <= cnt; ++index) { + + PasteboardItemID id; + if (PasteboardGetItemIdentifier(paste, index, &id) != noErr) + continue; + + QCFType types; + if (PasteboardCopyItemFlavors(paste, id, &types ) != noErr) + continue; + + const int type_count = CFArrayGetCount(types); + for (int i = 0; i < type_count; ++i) { + const QString flavor = QCFString::toQString((CFStringRef)CFArrayGetValueAtIndex(types, i)); +#ifdef DEBUG_PASTEBOARD + qDebug(" -%s", qPrintable(QString(flavor))); +#endif + QString mimeType = QMacPasteboardMime::flavorToMime(mime_type, flavor); + if (!mimeType.isEmpty() && !ret.contains(mimeType)) { +#ifdef DEBUG_PASTEBOARD + qDebug(" -<%d> %s [%s]", ret.size(), qPrintable(mimeType), qPrintable(QString(flavor))); +#endif + ret << mimeType; + } + } + } + return ret; +} + +bool +QMacPasteboard::hasFormat(const QString &format) const +{ + if (!paste) + return false; + + sync(); + + ItemCount cnt = 0; + if (PasteboardGetItemCount(paste, &cnt) || !cnt) + return false; + +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: hasFormat [%s]", qPrintable(format)); +#endif + for (uint index = 1; index <= cnt; ++index) { + + PasteboardItemID id; + if (PasteboardGetItemIdentifier(paste, index, &id) != noErr) + continue; + + QCFType types; + if (PasteboardCopyItemFlavors(paste, id, &types ) != noErr) + continue; + + const int type_count = CFArrayGetCount(types); + for (int i = 0; i < type_count; ++i) { + const QString flavor = QCFString::toQString((CFStringRef)CFArrayGetValueAtIndex(types, i)); +#ifdef DEBUG_PASTEBOARD + qDebug(" -%s [0x%x]", qPrintable(QString(flavor)), mime_type); +#endif + QString mimeType = QMacPasteboardMime::flavorToMime(mime_type, flavor); +#ifdef DEBUG_PASTEBOARD + if (!mimeType.isEmpty()) + qDebug(" - %s", qPrintable(mimeType)); +#endif + if (mimeType == format) + return true; + } + } + return false; +} + +QVariant +QMacPasteboard::retrieveData(const QString &format, QVariant::Type) const +{ + if (!paste) + return QVariant(); + + sync(); + + ItemCount cnt = 0; + if (PasteboardGetItemCount(paste, &cnt) || !cnt) + return QByteArray(); + +#ifdef DEBUG_PASTEBOARD + qDebug("Pasteboard: retrieveData [%s]", qPrintable(format)); +#endif + const QList mimes = QMacPasteboardMime::all(mime_type); + for (int mime = 0; mime < mimes.size(); ++mime) { + QMacPasteboardMime *c = mimes.at(mime); + QString c_flavor = c->flavorFor(format); + if (!c_flavor.isEmpty()) { + // Handle text/plain a little differently. Try handling Unicode first. + bool checkForUtf16 = (c_flavor == QLatin1String("com.apple.traditional-mac-plain-text") + || c_flavor == QLatin1String("public.utf8-plain-text")); + if (checkForUtf16 || c_flavor == QLatin1String("public.utf16-plain-text")) { + // Try to get the NSStringPboardType from NSPasteboard, newlines are mapped + // correctly (as '\n') in this data. The 'public.utf16-plain-text' type + // usually maps newlines to '\r' instead. + QString str = qt_mac_get_pasteboardString(paste); + if (!str.isEmpty()) + return str; + } + if (checkForUtf16 && hasFlavor(QLatin1String("public.utf16-plain-text"))) + c_flavor = QLatin1String("public.utf16-plain-text"); + + QVariant ret; + QList retList; + for (uint index = 1; index <= cnt; ++index) { + PasteboardItemID id; + if (PasteboardGetItemIdentifier(paste, index, &id) != noErr) + continue; + + QCFType types; + if (PasteboardCopyItemFlavors(paste, id, &types ) != noErr) + continue; + + const int type_count = CFArrayGetCount(types); + for (int i = 0; i < type_count; ++i) { + CFStringRef flavor = static_cast(CFArrayGetValueAtIndex(types, i)); + if (c_flavor == QCFString::toQString(flavor)) { + QCFType macBuffer; + if (PasteboardCopyItemFlavorData(paste, id, flavor, &macBuffer) == noErr) { + QByteArray buffer((const char *)CFDataGetBytePtr(macBuffer), CFDataGetLength(macBuffer)); + if (!buffer.isEmpty()) { +#ifdef DEBUG_PASTEBOARD + qDebug(" - %s [%s] (%s)", qPrintable(format), qPrintable(QCFString::toQString(flavor)), qPrintable(c->convertorName())); +#endif + buffer.detach(); //detach since we release the macBuffer + retList.append(buffer); + break; //skip to next element + } + } + } else { +#ifdef DEBUG_PASTEBOARD + qDebug(" - NoMatch %s [%s] (%s)", qPrintable(c_flavor), qPrintable(QCFString::toQString(flavor)), qPrintable(c->convertorName())); +#endif + } + } + } + + if (!retList.isEmpty()) { + ret = c->convertToMime(format, retList, c_flavor); + return ret; + } + } + } + return QVariant(); +} + +void QMacPasteboard::clear_helper() +{ + if (paste) + PasteboardClear(paste); + promises.clear(); +} + +void +QMacPasteboard::clear() +{ +#ifdef DEBUG_PASTEBOARD + qDebug("PasteBoard: clear!"); +#endif + clear_helper(); +} + +bool +QMacPasteboard::sync() const +{ + if (!paste) + return false; + const bool fromGlobal = PasteboardSynchronize(paste) & kPasteboardModified; + + if (fromGlobal) + const_cast(this)->setMimeData(0); + +#ifdef DEBUG_PASTEBOARD + if (fromGlobal) + qDebug("Pasteboard: Synchronize!"); +#endif + return fromGlobal; +} + + +QString qt_mac_get_pasteboardString(PasteboardRef paste) +{ + QCocoaAutoReleasePool pool; + NSPasteboard *pb = nil; + CFStringRef pbname; + if (PasteboardCopyName(paste, &pbname) == noErr) { + pb = [NSPasteboard pasteboardWithName:const_cast(reinterpret_cast(pbname))]; + CFRelease(pbname); + } else { + pb = [NSPasteboard generalPasteboard]; + } + if (pb) { + NSString *text = [pb stringForType:NSStringPboardType]; + if (text) + return QCFString::toQString(text); + } + return QString(); +} + + + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qmacmime.h b/src/plugins/platforms/cocoa/qmacmime.h new file mode 100644 index 0000000000..842caa5f2f --- /dev/null +++ b/src/plugins/platforms/cocoa/qmacmime.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMACMIME_H +#define QMACMIME_H + +#include + +#include + +class Q_GUI_EXPORT QMacPasteboardMime { + char type; +public: + enum QMacPasteboardMimeType { MIME_DND=0x01, + MIME_CLIP=0x02, + MIME_QT_CONVERTOR=0x04, + MIME_QT3_CONVERTOR=0x08, + MIME_ALL=MIME_DND|MIME_CLIP + }; + explicit QMacPasteboardMime(char); + virtual ~QMacPasteboardMime(); + + static void initialize(); + + static QList all(uchar); + static QMacPasteboardMime *convertor(uchar, const QString &mime, QString flav); + static QString flavorToMime(uchar, QString flav); + + virtual QString convertorName() = 0; + + virtual bool canConvert(const QString &mime, QString flav) = 0; + virtual QString mimeFor(QString flav) = 0; + virtual QString flavorFor(const QString &mime) = 0; + virtual QVariant convertToMime(const QString &mime, QList data, QString flav) = 0; + virtual QList convertFromMime(const QString &mime, QVariant data, QString flav) = 0; +}; + +#endif + diff --git a/src/plugins/platforms/cocoa/qmacmime.mm b/src/plugins/platforms/cocoa/qmacmime.mm new file mode 100644 index 0000000000..db86deb91c --- /dev/null +++ b/src/plugins/platforms/cocoa/qmacmime.mm @@ -0,0 +1,938 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmacmime.h" +#include "qcocoahelpers.h" +#include "qmacclipboard.h" + +#include "qdebug.h" +#include "qpixmap.h" +#include "qimagewriter.h" +#include "qimagereader.h" +#include "qdatastream.h" +#include "qbuffer.h" +#include "qdatetime.h" +#include "qguiapplication.h" +#include "qtextcodec.h" +#include "qregexp.h" +#include "qurl.h" +#include "qmap.h" + +#include + +QT_BEGIN_NAMESPACE + +extern CGImageRef qt_mac_createCGImageFromQImage(const QImage &img, const QImage **imagePtr = 0); // qpaintengine_mac.cpp + +typedef QList MimeList; +Q_GLOBAL_STATIC(MimeList, globalMimeList) + +static void cleanup_mimes() +{ + MimeList *mimes = globalMimeList(); + while (!mimes->isEmpty()) + delete mimes->takeFirst(); +} + +Q_GLOBAL_STATIC(QStringList, globalDraggedTypesList) + +/*! + \fn void qRegisterDraggedTypes(const QStringList &types) + \relates QMacPasteboardMime + + Registers the given \a types as custom pasteboard types. + + This function should be called to enable the Drag and Drop events + for custom pasteboard types on Cocoa implementations. This is required + in addition to a QMacPasteboardMime subclass implementation. By default + drag and drop is enabled for all standard pasteboard types. + + \sa QMacPasteboardMime +*/ +Q_WIDGETS_EXPORT void qRegisterDraggedTypes(const QStringList &types) +{ + (*globalDraggedTypesList()) += types; +} + +const QStringList& qEnabledDraggedTypes() +{ + return (*globalDraggedTypesList()); +} + + +/***************************************************************************** + QDnD debug facilities + *****************************************************************************/ +//#define DEBUG_MIME_MAPS + +//functions +extern QString qt_mac_from_pascal_string(const Str255); //qglobal.cpp +extern void qt_mac_from_pascal_string(QString, Str255, TextEncoding encoding=0, int len=-1); //qglobal.cpp + +ScrapFlavorType qt_mac_mime_type = 'CUTE'; +CFStringRef qt_mac_mime_typeUTI = CFSTR("com.pasteboard.trolltech.marker"); + +/*! + \class QMacPasteboardMime + \brief The QMacPasteboardMime class converts between a MIME type and a + \l{http://developer.apple.com/macosx/uniformtypeidentifiers.html}{Uniform + Type Identifier (UTI)} format. + \since 4.2 + + \ingroup draganddrop + \inmodule QtWidgets + + Qt's drag and drop and clipboard facilities use the MIME + standard. On X11, this maps trivially to the Xdnd protocol. On + Mac, although some applications use MIME to describe clipboard + contents, it is more common to use Apple's UTI format. + + QMacPasteboardMime's role is to bridge the gap between MIME and UTI; + By subclasses this class, one can extend Qt's drag and drop + and clipboard handling to convert to and from unsupported, or proprietary, UTI formats. + + A subclass of QMacPasteboardMime will automatically be registered, and active, upon instantiation. + + Qt has predefined support for the following UTIs: + \list + \i public.utf8-plain-text - converts to "text/plain" + \i public.utf16-plain-text - converts to "text/plain" + \i public.html - converts to "text/html" + \i public.url - converts to "text/uri-list" + \i public.file-url - converts to "text/uri-list" + \i public.tiff - converts to "application/x-qt-image" + \i public.vcard - converts to "text/plain" + \i com.apple.traditional-mac-plain-text - converts to "text/plain" + \i com.apple.pict - converts to "application/x-qt-image" + \endlist + + When working with MIME data, Qt will interate through all instances of QMacPasteboardMime to + find an instance that can convert to, or from, a specific MIME type. It will do this by calling + canConvert() on each instance, starting with (and choosing) the last created instance first. + The actual conversions will be done by using convertToMime() and convertFromMime(). + + \note The API uses the term "flavor" in some cases. This is for backwards + compatibility reasons, and should now be understood as UTIs. +*/ + +/*! \enum QMacPasteboardMime::QMacPasteboardMimeType + \internal +*/ + +/*! + Constructs a new conversion object of type \a t, adding it to the + globally accessed list of available convertors. +*/ +QMacPasteboardMime::QMacPasteboardMime(char t) : type(t) +{ + globalMimeList()->append(this); +} + +/*! + Destroys a conversion object, removing it from the global + list of available convertors. +*/ +QMacPasteboardMime::~QMacPasteboardMime() +{ + if (!QGuiApplication::closingDown()) + globalMimeList()->removeAll(this); +} + +class QMacPasteboardMimeAny : public QMacPasteboardMime { +private: + +public: + QMacPasteboardMimeAny() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) { + } + ~QMacPasteboardMimeAny() { + } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList data, QString flav); + QList convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeAny::convertorName() +{ + return QLatin1String("Any-Mime"); +} + +QString QMacPasteboardMimeAny::flavorFor(const QString &mime) +{ + // do not handle the mime type name in the drag pasteboard + if (mime == QLatin1String("application/x-qt-mime-type-name")) + return QString(); + QString ret = QLatin1String("com.trolltech.anymime.") + mime; + return ret.replace(QLatin1Char('/'), QLatin1String("--")); +} + +QString QMacPasteboardMimeAny::mimeFor(QString flav) +{ + const QString any_prefix = QLatin1String("com.trolltech.anymime."); + if (flav.size() > any_prefix.length() && flav.startsWith(any_prefix)) + return flav.mid(any_prefix.length()).replace(QLatin1String("--"), QLatin1String("/")); + return QString(); +} + +bool QMacPasteboardMimeAny::canConvert(const QString &mime, QString flav) +{ + return mimeFor(flav) == mime; +} + +QVariant QMacPasteboardMimeAny::convertToMime(const QString &mime, QList data, QString) +{ + if (data.count() > 1) + qWarning("QMacPasteboardMimeAny: Cannot handle multiple member data"); + QVariant ret; + if (mime == QLatin1String("text/plain")) + ret = QString::fromUtf8(data.first()); + else + ret = data.first(); + return ret; +} + +QList QMacPasteboardMimeAny::convertFromMime(const QString &mime, QVariant data, QString) +{ + QList ret; + if (mime == QLatin1String("text/plain")) + ret.append(data.toString().toUtf8()); + else + ret.append(data.toByteArray()); + return ret; +} + +class QMacPasteboardMimeTypeName : public QMacPasteboardMime { +private: + +public: + QMacPasteboardMimeTypeName() : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL) { + } + ~QMacPasteboardMimeTypeName() { + } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList data, QString flav); + QList convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeTypeName::convertorName() +{ + return QLatin1String("Qt-Mime-Type"); +} + +QString QMacPasteboardMimeTypeName::flavorFor(const QString &mime) +{ + if (mime == QLatin1String("application/x-qt-mime-type-name")) + return QLatin1String("com.trolltech.qt.MimeTypeName"); + return QString(); +} + +QString QMacPasteboardMimeTypeName::mimeFor(QString) +{ + return QString(); +} + +bool QMacPasteboardMimeTypeName::canConvert(const QString &, QString) +{ + return false; +} + +QVariant QMacPasteboardMimeTypeName::convertToMime(const QString &, QList, QString) +{ + QVariant ret; + return ret; +} + +QList QMacPasteboardMimeTypeName::convertFromMime(const QString &, QVariant, QString) +{ + QList ret; + ret.append(QString("x-qt-mime-type-name").toUtf8()); + return ret; +} + +class QMacPasteboardMimePlainText : public QMacPasteboardMime { +public: + QMacPasteboardMimePlainText() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList data, QString flav); + QList convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimePlainText::convertorName() +{ + return QLatin1String("PlainText"); +} + +QString QMacPasteboardMimePlainText::flavorFor(const QString &mime) +{ + if (mime == QLatin1String("text/plain")) + return QLatin1String("com.apple.traditional-mac-plain-text"); + return QString(); +} + +QString QMacPasteboardMimePlainText::mimeFor(QString flav) +{ + if (flav == QLatin1String("com.apple.traditional-mac-plain-text")) + return QLatin1String("text/plain"); + return QString(); +} + +bool QMacPasteboardMimePlainText::canConvert(const QString &mime, QString flav) +{ + return flavorFor(mime) == flav; +} + +QVariant QMacPasteboardMimePlainText::convertToMime(const QString &mimetype, QList data, QString flavor) +{ + if (data.count() > 1) + qWarning("QMacPasteboardMimePlainText: Cannot handle multiple member data"); + const QByteArray &firstData = data.first(); + QVariant ret; + if (flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text"))) { + QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault, + reinterpret_cast(firstData.constData()), + firstData.size(), CFStringGetSystemEncoding(), false)); + ret = QString(str); + } else { + qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); + } + return ret; +} + +QList QMacPasteboardMimePlainText::convertFromMime(const QString &, QVariant data, QString flavor) +{ + QList ret; + QString string = data.toString(); + if (flavor == QCFString(QLatin1String("com.apple.traditional-mac-plain-text"))) + ret.append(string.toLatin1()); + return ret; +} + +class QMacPasteboardMimeUnicodeText : public QMacPasteboardMime { +public: + QMacPasteboardMimeUnicodeText() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList data, QString flav); + QList convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeUnicodeText::convertorName() +{ + return QLatin1String("UnicodeText"); +} + +QString QMacPasteboardMimeUnicodeText::flavorFor(const QString &mime) +{ + if (mime == QLatin1String("text/plain")) + return QLatin1String("public.utf16-plain-text"); + int i = mime.indexOf(QLatin1String("charset=")); + if (i >= 0) { + QString cs(mime.mid(i+8).toLower()); + i = cs.indexOf(QLatin1Char(';')); + if (i>=0) + cs = cs.left(i); + if (cs == QLatin1String("system")) + return QLatin1String("public.utf8-plain-text"); + else if (cs == QLatin1String("iso-10646-ucs-2") + || cs == QLatin1String("utf16")) + return QLatin1String("public.utf16-plain-text"); + } + return QString(); +} + +QString QMacPasteboardMimeUnicodeText::mimeFor(QString flav) +{ + if (flav == QLatin1String("public.utf16-plain-text") || flav == QLatin1String("public.utf8-plain-text")) + return QLatin1String("text/plain"); + return QString(); +} + +bool QMacPasteboardMimeUnicodeText::canConvert(const QString &mime, QString flav) +{ + return flavorFor(mime) == flav; +} + +QVariant QMacPasteboardMimeUnicodeText::convertToMime(const QString &mimetype, QList data, QString flavor) +{ + if (data.count() > 1) + qWarning("QMacPasteboardMimeUnicodeText: Cannot handle multiple member data"); + const QByteArray &firstData = data.first(); + // I can only handle two types (system and unicode) so deal with them that way + QVariant ret; + if (flavor == QLatin1String("public.utf8-plain-text")) { + QCFString str(CFStringCreateWithBytes(kCFAllocatorDefault, + reinterpret_cast(firstData.constData()), + firstData.size(), CFStringGetSystemEncoding(), false)); + ret = QString(str); + } else if (flavor == QLatin1String("public.utf16-plain-text")) { + ret = QString(reinterpret_cast(firstData.constData()), + firstData.size() / sizeof(QChar)); + } else { + qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); + } + return ret; +} + +QList QMacPasteboardMimeUnicodeText::convertFromMime(const QString &, QVariant data, QString flavor) +{ + QList ret; + QString string = data.toString(); + 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)); + return ret; +} + +class QMacPasteboardMimeHTMLText : public QMacPasteboardMime { +public: + QMacPasteboardMimeHTMLText() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList data, QString flav); + QList convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeHTMLText::convertorName() +{ + return QLatin1String("HTML"); +} + +QString QMacPasteboardMimeHTMLText::flavorFor(const QString &mime) +{ + if (mime == QLatin1String("text/html")) + return QLatin1String("public.html"); + return QString(); +} + +QString QMacPasteboardMimeHTMLText::mimeFor(QString flav) +{ + if (flav == QLatin1String("public.html")) + return QLatin1String("text/html"); + return QString(); +} + +bool QMacPasteboardMimeHTMLText::canConvert(const QString &mime, QString flav) +{ + return flavorFor(mime) == flav; +} + +QVariant QMacPasteboardMimeHTMLText::convertToMime(const QString &mimeType, QList data, QString flavor) +{ + if (!canConvert(mimeType, flavor)) + return QVariant(); + if (data.count() > 1) + qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data"); + return data.first(); +} + +QList QMacPasteboardMimeHTMLText::convertFromMime(const QString &mime, QVariant data, QString flavor) +{ + QList ret; + if (!canConvert(mime, flavor)) + return ret; + ret.append(data.toByteArray()); + return ret; +} + +class QMacPasteboardMimeTiff : public QMacPasteboardMime { +public: + QMacPasteboardMimeTiff() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList data, QString flav); + QList convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeTiff::convertorName() +{ + return QLatin1String("Tiff"); +} + +QString QMacPasteboardMimeTiff::flavorFor(const QString &mime) +{ + if (mime.startsWith(QLatin1String("application/x-qt-image"))) + return QLatin1String("public.tiff"); + return QString(); +} + +QString QMacPasteboardMimeTiff::mimeFor(QString flav) +{ + if (flav == QLatin1String("public.tiff")) + return QLatin1String("application/x-qt-image"); + return QString(); +} + +bool QMacPasteboardMimeTiff::canConvert(const QString &mime, QString flav) +{ + return flav == QLatin1String("public.tiff") && mime == QLatin1String("application/x-qt-image"); +} + +QVariant QMacPasteboardMimeTiff::convertToMime(const QString &mime, QList data, QString flav) +{ + if (data.count() > 1) + qWarning("QMacPasteboardMimeTiff: Cannot handle multiple member data"); + QVariant ret; + if (!canConvert(mime, flav)) + return ret; + const QByteArray &a = data.first(); + QCFType image; + QCFType tiffData = CFDataCreateWithBytesNoCopy(0, + reinterpret_cast(a.constData()), + a.size(), kCFAllocatorNull); + QCFType imageSource = CGImageSourceCreateWithData(tiffData, 0); + image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0); + + // ### TODO (msorvig) QPixmap conversion + //if (image != 0) + // ret = QVariant(QPixmap::fromMacCGImageRef(image).toImage()); + return ret; +} + +QList QMacPasteboardMimeTiff::convertFromMime(const QString &mime, QVariant variant, QString flav) +{ + QList ret; + if (!canConvert(mime, flav)) + return ret; + + QImage img = qvariant_cast(variant); + QCFType cgimage = qt_mac_image_to_cgimage(img); + + QCFType data = CFDataCreateMutable(0, 0); + QCFType imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0); + if (imageDestination != 0) { + CFTypeRef keys[2]; + QCFType values[2]; + QCFType options; + keys[0] = kCGImagePropertyPixelWidth; + keys[1] = kCGImagePropertyPixelHeight; + int width = img.width(); + int height = img.height(); + values[0] = CFNumberCreate(0, kCFNumberIntType, &width); + values[1] = CFNumberCreate(0, kCFNumberIntType, &height); + options = CFDictionaryCreate(0, reinterpret_cast(keys), + reinterpret_cast(values), 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CGImageDestinationAddImage(imageDestination, cgimage, options); + CGImageDestinationFinalize(imageDestination); + } + QByteArray ar(CFDataGetLength(data), 0); + CFDataGetBytes(data, + CFRangeMake(0, ar.size()), + reinterpret_cast(ar.data())); + ret.append(ar); + return ret; +} + + +class QMacPasteboardMimeFileUri : public QMacPasteboardMime { +public: + QMacPasteboardMimeFileUri() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList data, QString flav); + QList convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeFileUri::convertorName() +{ + return QLatin1String("FileURL"); +} + +QString QMacPasteboardMimeFileUri::flavorFor(const QString &mime) +{ + if (mime == QLatin1String("text/uri-list")) + return QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0)); + return QString(); +} + +QString QMacPasteboardMimeFileUri::mimeFor(QString flav) +{ + if (flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0))) + return QLatin1String("text/uri-list"); + return QString(); +} + +bool QMacPasteboardMimeFileUri::canConvert(const QString &mime, QString flav) +{ + return mime == QLatin1String("text/uri-list") + && flav == QCFString(UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, CFSTR("furl"), 0)); +} + +QVariant QMacPasteboardMimeFileUri::convertToMime(const QString &mime, QList data, QString flav) +{ + if (!canConvert(mime, flav)) + return QVariant(); + QList ret; + for (int i = 0; i < data.size(); ++i) { + QUrl url = QUrl::fromEncoded(data.at(i)); + if (url.host().toLower() == QLatin1String("localhost")) + url.setHost(QString()); + url.setPath(url.path().normalized(QString::NormalizationForm_C)); + ret.append(url); + } + return QVariant(ret); +} + +QList QMacPasteboardMimeFileUri::convertFromMime(const QString &mime, QVariant data, QString flav) +{ + QList ret; + if (!canConvert(mime, flav)) + return ret; + QList urls = data.toList(); + for (int i = 0; i < urls.size(); ++i) { + QUrl url = urls.at(i).toUrl(); + if (url.scheme().isEmpty()) + url.setScheme(QLatin1String("file")); + if (url.scheme().toLower() == QLatin1String("file")) { + if (url.host().isEmpty()) + url.setHost(QLatin1String("localhost")); + url.setPath(url.path().normalized(QString::NormalizationForm_D)); + } + ret.append(url.toEncoded()); + } + return ret; +} + +class QMacPasteboardMimeUrl : public QMacPasteboardMime { +public: + QMacPasteboardMimeUrl() : QMacPasteboardMime(MIME_ALL) { } + QString convertorName(); + + QString flavorFor(const QString &mime); + QString mimeFor(QString flav); + bool canConvert(const QString &mime, QString flav); + QVariant convertToMime(const QString &mime, QList data, QString flav); + QList convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeUrl::convertorName() +{ + return QLatin1String("URL"); +} + +QString QMacPasteboardMimeUrl::flavorFor(const QString &mime) +{ + if (mime.startsWith(QLatin1String("text/uri-list"))) + return QLatin1String("public.url"); + return QString(); +} + +QString QMacPasteboardMimeUrl::mimeFor(QString flav) +{ + if (flav == QLatin1String("public.url")) + return QLatin1String("text/uri-list"); + return QString(); +} + +bool QMacPasteboardMimeUrl::canConvert(const QString &mime, QString flav) +{ + return flav == QLatin1String("public.url") + && mime == QLatin1String("text/uri-list"); +} + +QVariant QMacPasteboardMimeUrl::convertToMime(const QString &mime, QList data, QString flav) +{ + if (!canConvert(mime, flav)) + return QVariant(); + + QList ret; + for (int i=0; i QMacPasteboardMimeUrl::convertFromMime(const QString &mime, QVariant data, QString flav) +{ + QList ret; + if (!canConvert(mime, flav)) + return ret; + + QList urls = data.toList(); + for (int i=0; i data, QString flav); + QList convertFromMime(const QString &mime, QVariant data, QString flav); +}; + +QString QMacPasteboardMimeVCard::convertorName() +{ + return QString("VCard"); +} + +bool QMacPasteboardMimeVCard::canConvert(const QString &mime, QString flav) +{ + return mimeFor(flav) == mime; +} + +QString QMacPasteboardMimeVCard::flavorFor(const QString &mime) +{ + if (mime.startsWith(QLatin1String("text/plain"))) + return QLatin1String("public.vcard"); + return QString(); +} + +QString QMacPasteboardMimeVCard::mimeFor(QString flav) +{ + if (flav == QLatin1String("public.vcard")) + return QLatin1String("text/plain"); + return QString(); +} + +QVariant QMacPasteboardMimeVCard::convertToMime(const QString &mime, QList data, QString) +{ + QByteArray cards; + if (mime == QLatin1String("text/plain")) { + for (int i=0; i QMacPasteboardMimeVCard::convertFromMime(const QString &mime, QVariant data, QString) +{ + QList ret; + if (mime == QLatin1String("text/plain")) + ret.append(data.toString().toUtf8()); + return ret; +} + + +/*! + \internal + + This is an internal function. +*/ +void QMacPasteboardMime::initialize() +{ + if (globalMimeList()->isEmpty()) { + qAddPostRoutine(cleanup_mimes); + + //standard types that we wrap + new QMacPasteboardMimeTiff; + new QMacPasteboardMimeUnicodeText; + new QMacPasteboardMimePlainText; + new QMacPasteboardMimeHTMLText; + new QMacPasteboardMimeFileUri; + new QMacPasteboardMimeUrl; + new QMacPasteboardMimeTypeName; + new QMacPasteboardMimeVCard; + //make sure our "non-standard" types are always last! --Sam + new QMacPasteboardMimeAny; + } +} + +/*! + Returns the most-recently created QMacPasteboardMime of type \a t that can convert + between the \a mime and \a flav formats. Returns 0 if no such convertor + exists. +*/ +QMacPasteboardMime* +QMacPasteboardMime::convertor(uchar t, const QString &mime, QString flav) +{ + MimeList *mimes = globalMimeList(); + for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) { +#ifdef DEBUG_MIME_MAPS + qDebug("QMacPasteboardMime::convertor: seeing if %s (%d) can convert %s to %d[%c%c%c%c] [%d]", + (*it)->convertorName().toLatin1().constData(), + (*it)->type & t, mime.toLatin1().constData(), + flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF, + (*it)->canConvert(mime,flav)); + for (int i = 0; i < (*it)->countFlavors(); ++i) { + int f = (*it)->flavor(i); + qDebug(" %d) %d[%c%c%c%c] [%s]", i, f, + (f >> 24) & 0xFF, (f >> 16) & 0xFF, (f >> 8) & 0xFF, (f) & 0xFF, + (*it)->convertorName().toLatin1().constData()); + } +#endif + if (((*it)->type & t) && (*it)->canConvert(mime, flav)) + return (*it); + } + return 0; +} +/*! + Returns a MIME type of type \a t for \a flav, or 0 if none exists. +*/ +QString QMacPasteboardMime::flavorToMime(uchar t, QString flav) +{ + MimeList *mimes = globalMimeList(); + for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) { +#ifdef DEBUG_MIME_MAPS + qDebug("QMacMIme::flavorToMime: attempting %s (%d) for flavor %d[%c%c%c%c] [%s]", + (*it)->convertorName().toLatin1().constData(), + (*it)->type & t, flav, (flav >> 24) & 0xFF, (flav >> 16) & 0xFF, (flav >> 8) & 0xFF, (flav) & 0xFF, + (*it)->mimeFor(flav).toLatin1().constData()); + +#endif + if ((*it)->type & t) { + QString mimeType = (*it)->mimeFor(flav); + if (!mimeType.isNull()) + return mimeType; + } + } + return QString(); +} + +/*! + Returns a list of all currently defined QMacPasteboardMime objects of type \a t. +*/ +QList QMacPasteboardMime::all(uchar t) +{ + MimeList ret; + MimeList *mimes = globalMimeList(); + for (MimeList::const_iterator it = mimes->constBegin(); it != mimes->constEnd(); ++it) { + if ((*it)->type & t) + ret.append((*it)); + } + return ret; +} + + +/*! + \fn QString QMacPasteboardMime::convertorName() + + Returns a name for the convertor. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn bool QMacPasteboardMime::canConvert(const QString &mime, QString flav) + + Returns true if the convertor can convert (both ways) between + \a mime and \a flav; otherwise returns false. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QString QMacPasteboardMime::mimeFor(QString flav) + + Returns the MIME UTI used for Mac flavor \a flav, or 0 if this + convertor does not support \a flav. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QString QMacPasteboardMime::flavorFor(const QString &mime) + + Returns the Mac UTI used for MIME type \a mime, or 0 if this + convertor does not support \a mime. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QVariant QMacPasteboardMime::convertToMime(const QString &mime, QList data, QString flav) + + Returns \a data converted from Mac UTI \a flav to MIME type \a + mime. + + Note that Mac flavors must all be self-terminating. The input \a + data may contain trailing data. + + All subclasses must reimplement this pure virtual function. +*/ + +/*! + \fn QList QMacPasteboardMime::convertFromMime(const QString &mime, QVariant data, QString flav) + + Returns \a data converted from MIME type \a mime + to Mac UTI \a flav. + + Note that Mac flavors must all be self-terminating. The return + value may contain trailing data. + + All subclasses must reimplement this pure virtual function. +*/ + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h index c61ff2bd02..1a1a1cd3b9 100644 --- a/src/plugins/platforms/cocoa/qnsview.h +++ b/src/plugins/platforms/cocoa/qnsview.h @@ -57,6 +57,7 @@ QT_END_NAMESPACE QCocoaWindow *m_platformWindow; Qt::MouseButtons m_buttons; QAccessibleInterface *m_accessibleRoot; + QStringList *currentCustomDragTypes; } - (id)init; @@ -91,6 +92,9 @@ QT_END_NAMESPACE - (void)keyDown:(NSEvent *)theEvent; - (void)keyUp:(NSEvent *)theEvent; +- (void)registerDragTypes; +- (NSDragOperation)handleDrag:(id )sender; + @end #endif //QNSVIEW_H diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index a43b3fe893..9ed3332ba5 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -46,7 +46,9 @@ #include "qnsview.h" #include "qcocoawindow.h" #include "qcocoahelpers.h" +#include "qcocoaautoreleasepool.h" #include "qmultitouch_mac_p.h" +#include "qcocoadrag.h" #include #include @@ -72,6 +74,7 @@ static QTouchDevice *touchDevice = 0; m_cgImage = 0; m_window = 0; m_buttons = Qt::NoButton; + currentCustomDragTypes = 0; if (!touchDevice) { touchDevice = new QTouchDevice; touchDevice->setType(QTouchDevice::TouchPad); @@ -109,6 +112,7 @@ static QTouchDevice *touchDevice = 0; m_accessibleRoot = window->accessibleRoot(); #endif + [self registerDragTypes]; [self setPostsFrameChangedNotifications : YES]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateGeometry) @@ -471,4 +475,74 @@ static QTouchDevice *touchDevice = 0; [self handleKeyEvent : theEvent eventType :int(QEvent::KeyRelease)]; } +-(void)registerDragTypes +{ + QCocoaAutoReleasePool pool; + // ### Custom types disabled. + QStringList customTypes; // = qEnabledDraggedTypes(); + if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) { + if (currentCustomDragTypes == 0) + currentCustomDragTypes = new QStringList(); + *currentCustomDragTypes = customTypes; + const NSString* mimeTypeGeneric = @"com.trolltech.qt.MimeTypeName"; + NSMutableArray *supportedTypes = [NSMutableArray arrayWithObjects:NSColorPboardType, + NSFilenamesPboardType, NSStringPboardType, + NSFilenamesPboardType, NSPostScriptPboardType, NSTIFFPboardType, + NSRTFPboardType, NSTabularTextPboardType, NSFontPboardType, + NSRulerPboardType, NSFileContentsPboardType, NSColorPboardType, + NSRTFDPboardType, NSHTMLPboardType, NSPICTPboardType, + NSURLPboardType, NSPDFPboardType, NSVCardPboardType, + NSFilesPromisePboardType, NSInkTextPboardType, + NSMultipleTextSelectionPboardType, mimeTypeGeneric, nil]; + // Add custom types supported by the application. + for (int i = 0; i < customTypes.size(); i++) { + [supportedTypes addObject:QCFString::toNSString(customTypes[i])]; + } + [self registerForDraggedTypes:supportedTypes]; + } +} + +- (NSDragOperation)draggingEntered:(id )sender +{ + return [self handleDrag : sender]; +} + +- (NSDragOperation)draggingUpdated:(id )sender +{ + return [self handleDrag : sender]; +} + +// Sends drag update to Qt, return the action +- (NSDragOperation)handleDrag:(id )sender +{ + NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; + QPoint qt_windowPoint(windowPoint.x, windowPoint.y); + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); + QCocoaDropData mimeData([sender draggingPasteboard]); + + QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_window, &mimeData, qt_windowPoint, qtAllowed); + return qt_mac_mapDropAction(response.acceptedAction()); +} + +- (void)draggingExited:(id )sender +{ + NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; + QPoint qt_windowPoint(windowPoint.x, windowPoint.y); + + // Send 0 mime data to indicate drag exit + QWindowSystemInterface::handleDrag(m_window, 0 ,qt_windowPoint, Qt::IgnoreAction); +} + +// called on drop, send the drop to Qt and return if it was accepted. +- (BOOL)performDragOperation:(id )sender +{ + NSPoint windowPoint = [self convertPoint: [sender draggingLocation] fromView: nil]; + QPoint qt_windowPoint(windowPoint.x, windowPoint.y); + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations([sender draggingSourceOperationMask]); + QCocoaDropData mimeData([sender draggingPasteboard]); + + QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_window, &mimeData, qt_windowPoint, qtAllowed); + return response.isAccepted(); +} + @end diff --git a/src/plugins/platforms/cocoa/qt_mac_p.h b/src/plugins/platforms/cocoa/qt_mac_p.h index 8e94880c5a..b75e6e2bcb 100644 --- a/src/plugins/platforms/cocoa/qt_mac_p.h +++ b/src/plugins/platforms/cocoa/qt_mac_p.h @@ -196,44 +196,6 @@ public: class QMacPasteboardMime; class QMimeData; -class QMacPasteboard -{ - struct Promise { - Promise() : itemId(0), convertor(0) { } - Promise(int itemId, QMacPasteboardMime *c, QString m, QVariant d, int o=0) : itemId(itemId), offset(o), convertor(c), mime(m), data(d) { } - int itemId, offset; - QMacPasteboardMime *convertor; - QString mime; - QVariant data; - }; - QList promises; - - OSPasteboardRef paste; - uchar mime_type; - mutable QPointer mime; - mutable bool mac_mime_source; - static OSStatus promiseKeeper(OSPasteboardRef, PasteboardItemID, CFStringRef, void *); - void clear_helper(); -public: - QMacPasteboard(OSPasteboardRef p, uchar mime_type=0); - QMacPasteboard(uchar mime_type); - QMacPasteboard(CFStringRef name=0, uchar mime_type=0); - ~QMacPasteboard(); - - bool hasFlavor(QString flavor) const; - bool hasOSType(int c_flavor) const; - - OSPasteboardRef pasteBoard() const; - QMimeData *mimeData() const; - void setMimeData(QMimeData *mime); - - QStringList formats() const; - bool hasFormat(const QString &format) const; - QVariant retrieveData(const QString &format, QVariant::Type) const; - - void clear(); - bool sync() const; -}; extern QPaintDevice *qt_mac_safe_pdev; //qapplication_mac.cpp -- cgit v1.2.3 From 83d113feb01be72f9a0f1714b69541e7ebc7c4ca Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Wed, 7 Mar 2012 13:31:30 +0200 Subject: Windows: Fix override cursor logic Fixed a logic error that prevented override cursors being applied in Windows plugin. The logic for override cursor handling is already in crossplatform code, so no need to do extra checks in plugin. Task-number: QTBUG-24657 Change-Id: Ied9b36b57f22607ef5bb5c30f2926b0053eebca5 Reviewed-by: Friedemann Kleint --- src/plugins/platforms/windows/qwindowswindow.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index b2ebe06a58..f5cac4d4f0 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1309,16 +1309,14 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const } /*! - \brief Applies to cursor property set on the window to the global cursor - unless there is an override cursor. + \brief Applies to cursor property set on the window to the global cursor. \sa QWindowsCursor */ void QWindowsWindow::applyCursor() { - if (!QGuiApplication::overrideCursor()) - SetCursor(m_cursor.handle()); + SetCursor(m_cursor.handle()); } void QWindowsWindow::setCursor(const QWindowsWindowCursor &c) -- cgit v1.2.3 From 7bc576771de0b3c96905a6d11a75a01917334dc9 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 6 Mar 2012 13:58:31 +0200 Subject: Fix sending simulated keyboard events to popup widgets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Popup widgets steal the keyboard grab stealthily without it being visible via QWidget::keyboardGrabber(). To more accurately simulate a real keyboard event, prioritize sending simulated keyboard events to the active popup widget over QGuiApplication::focusWindow(). Task-number: QTBUG-24326 Change-Id: Id7a75c613d934e24657b521f1684ce7cce92556a Reviewed-by: Morten Johan Sørvig Reviewed-by: Friedemann Kleint Reviewed-by: Jason McDonald --- src/testlib/qtestkeyboard.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/testlib/qtestkeyboard.h b/src/testlib/qtestkeyboard.h index 897c732f45..5625bd606c 100644 --- a/src/testlib/qtestkeyboard.h +++ b/src/testlib/qtestkeyboard.h @@ -191,18 +191,20 @@ namespace QTest if (!widget) widget = QWidget::keyboardGrabber(); + if (!widget) { + // Popup widgets stealthily steal the keyboard grab + if (QWidget *apw = QApplication::activePopupWidget()) + widget = apw->focusWidget() ? apw->focusWidget() : apw; + } if (!widget) { QWindow *window = QGuiApplication::focusWindow(); if (window) { sendKeyEvent(action, window, code, text, modifier, delay); return; } - - if (QWidget *apw = QApplication::activePopupWidget()) - widget = apw->focusWidget() ? apw->focusWidget() : apw; - else - widget = QApplication::focusWidget(); } + if (!widget) + widget = QApplication::focusWidget(); if (!widget) widget = QApplication::activeWindow(); -- cgit v1.2.3 From 3bb902495291c50a2f06e8e03a62a647db3e5cd4 Mon Sep 17 00:00:00 2001 From: Debao Zhang Date: Tue, 6 Mar 2012 16:44:52 -0800 Subject: QWidget: fix wrong mouse behavior Widgets will receive extra mouse press events when double clicked. This is a side effect of change Id Ief6af12c666b23e544da4a68cb835cd577265469 which has partially fixed the folowing bug. Task-number:QTBUG-24649 Change-Id: I030ac6ba641050d40ac8989720a1c261ab15f849 Reviewed-by: Laszlo Agocs Reviewed-by: Friedemann Kleint --- src/widgets/kernel/qwidget.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 5eee5752cc..bf864503a4 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -8390,7 +8390,6 @@ void QWidget::mouseReleaseEvent(QMouseEvent *event) void QWidget::mouseDoubleClickEvent(QMouseEvent *event) { - mousePressEvent(event); // try mouse press event } #ifndef QT_NO_WHEELEVENT -- cgit v1.2.3 From eb709333989a7e3d8eb662a0e167ac81ca19d882 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Wed, 22 Feb 2012 03:33:00 +0000 Subject: QRegularExpression: add optimizations autotest Exporting the counter that controls the optimization of a compiled pattern lets us to forcibly optimize all patterns. Therefore, two tests are now run: one with default optimization values and another one which always optimizes the pattern. The counter itself was renamed with a qt_ prefix and put inside the Qt compilation namespace (thanks to rohanpm for pointing it out). Change-Id: I56602433d37adc127772b2d0d2cdaf2e49d43c71 Reviewed-by: Rohan McGovern Reviewed-by: Thiago Macieira --- src/corelib/tools/qregularexpression.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 0252a30c89..0fa7d6459e 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -50,9 +50,6 @@ #include -// after how many usages we optimize the regexp -static const unsigned int OPTIMIZE_AFTER_USE_COUNT = 10; - QT_BEGIN_NAMESPACE /*! @@ -726,6 +723,13 @@ QT_BEGIN_NAMESPACE contain any metacharacter that anchors the match at that point. */ +// after how many usages we optimize the regexp +#ifdef QT_BUILD_INTERNAL +Q_AUTOTEST_EXPORT unsigned int qt_qregularexpression_optimize_after_use_count = 10; +#else +static const unsigned int qt_qregularexpression_optimize_after_use_count = 10; +#endif // QT_BUILD_INTERNAL + /*! \internal */ @@ -1012,7 +1016,7 @@ static bool isJitEnabled() setting the studyData member variable to the result of the study. It gets called by doMatch() every time a match is performed. As of now, the optimizations on the pattern are performed after a certain number of usages - (i.e. the OPTIMIZE_AFTER_USE_COUNT constant). + (i.e. the qt_qregularexpression_optimize_after_use_count constant). Notice that although the method is protected by a mutex, one thread may invoke this function and return immediately (i.e. not study the pattern, @@ -1028,7 +1032,7 @@ pcre16_extra *QRegularExpressionPrivate::optimizePattern() QMutexLocker lock(&mutex); - if (studyData || (++usedCount != OPTIMIZE_AFTER_USE_COUNT)) + if (studyData || (++usedCount != qt_qregularexpression_optimize_after_use_count)) return studyData; static const bool enableJit = isJitEnabled(); -- cgit v1.2.3 From b7915a4d0d97a60fc30e55eb4cc1b10e7fe9ce2f Mon Sep 17 00:00:00 2001 From: Debao Zhang Date: Fri, 23 Dec 2011 00:27:32 +0800 Subject: Fix dockwidgets behavior when window resized or central widgets is set. When adding and showing a central widget in a QMainWindow then the layout does not respect the size policy of the central widget. This is a side effect of 059be19781a22d2e41f22072152589857d0fabf9 After the layout of QMainWindow is restored or the separator between central widget and dock widgets is moved by user, dock widgets should keep their size when the window if resized. Task-number: QTBUG-15689 Change-Id: Idfccb7b4ae057a99f431c2ed54e3b9fcfb6ef54c Reviewed-by: Friedemann Kleint --- src/widgets/widgets/qdockarealayout.cpp | 2 -- src/widgets/widgets/qdockarealayout_p.h | 2 +- src/widgets/widgets/qmainwindowlayout.cpp | 2 ++ 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/widgets/widgets/qdockarealayout.cpp b/src/widgets/widgets/qdockarealayout.cpp index a07ab73ad0..5adb2f1c95 100644 --- a/src/widgets/widgets/qdockarealayout.cpp +++ b/src/widgets/widgets/qdockarealayout.cpp @@ -2631,8 +2631,6 @@ void QDockAreaLayout::getGrid(QVector *_ver_struct_list, QSize bottom_max = docks[QInternal::BottomDock].maximumSize(); bottom_hint = bottom_hint.boundedTo(bottom_max).expandedTo(bottom_min); - fallbackToSizeHints = false; - if (_ver_struct_list != 0) { QVector &ver_struct_list = *_ver_struct_list; ver_struct_list.resize(3); diff --git a/src/widgets/widgets/qdockarealayout_p.h b/src/widgets/widgets/qdockarealayout_p.h index 17078f3709..10eb0da651 100644 --- a/src/widgets/widgets/qdockarealayout_p.h +++ b/src/widgets/widgets/qdockarealayout_p.h @@ -232,7 +232,7 @@ public: QDockAreaLayout(QMainWindow *win); QDockAreaLayoutInfo docks[4]; int sep; // separator extent - bool fallbackToSizeHints; //determines if we should use the sizehint for the dock areas (true until the layout is restored or the central widget is set) + bool fallbackToSizeHints; //determines if we should use the sizehint for the dock areas (true until the layout is restored or the separator is moved by user) mutable QVector separatorWidgets; bool isValid() const; diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index c67881e652..ff05503540 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -1351,6 +1351,8 @@ bool QMainWindowLayout::startSeparatorMove(const QPoint &pos) if (movingSeparator.isEmpty()) return false; + layoutState.dockAreaLayout.fallbackToSizeHints = false; + savedState = layoutState; movingSeparatorPos = movingSeparatorOrigin = pos; -- cgit v1.2.3 From b319d44798fe4c5aa8b859966d64f28b08be28dd Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 5 Mar 2012 14:42:42 +0100 Subject: QCursor: Associate cursor with screen. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Introduce cursor() accessor to QPlatformScreen. - Remove screen member of QPlatformCursor (a cursor can be shared by multiple screens of a virtual desktop). - Add QCursor::pos()/ QCursor::setPos() taking a QScreen-parameter, use primaryScreen() for old overloads. QCursor::pos() can then query the platform cursor for the position and return the position even if the mouse position is outside the windows owned by the Qt application. - Fix tests Reviewed-by: Samuel Rødal Task-number: QTBUG-22457 Task-number: QTBUG-22565 Task-number: QTBUG-20753 Change-Id: Ia69f37343f95772e934eab1cd806bd54cbdbbe51 Reviewed-by: Friedemann Kleint --- src/gui/kernel/qcursor.cpp | 46 +++++++++++++++++++--- src/gui/kernel/qcursor.h | 4 ++ src/gui/kernel/qcursor_qpa.cpp | 40 +++++++++++-------- src/gui/kernel/qguiapplication.cpp | 22 ++++------- src/gui/kernel/qplatformcursor_qpa.cpp | 15 +++++-- src/gui/kernel/qplatformcursor_qpa.h | 10 ++--- src/gui/kernel/qplatformscreen_qpa.cpp | 11 ++++++ src/gui/kernel/qplatformscreen_qpa.h | 2 + src/plugins/platforms/cocoa/qcocoacursor.h | 2 +- src/plugins/platforms/cocoa/qcocoacursor.mm | 3 +- src/plugins/platforms/cocoa/qcocoaintegration.h | 1 + src/plugins/platforms/cocoa/qcocoaintegration.mm | 2 +- src/plugins/platforms/directfb/qdirectfbcursor.cpp | 4 +- src/plugins/platforms/directfb/qdirectfbcursor.h | 1 + src/plugins/platforms/directfb/qdirectfbscreen.h | 1 + src/plugins/platforms/kms/qkmscursor.cpp | 2 +- src/plugins/platforms/kms/qkmsscreen.cpp | 5 +++ src/plugins/platforms/kms/qkmsscreen.h | 1 + src/plugins/platforms/windows/qwindowscursor.cpp | 5 --- src/plugins/platforms/windows/qwindowscursor.h | 2 +- src/plugins/platforms/windows/qwindowsscreen.cpp | 10 ++++- src/plugins/platforms/windows/qwindowsscreen.h | 9 +++-- src/plugins/platforms/windows/qwindowswindow.cpp | 2 +- src/plugins/platforms/xcb/qxcbcursor.cpp | 2 +- src/plugins/platforms/xcb/qxcbscreen.cpp | 5 +++ src/plugins/platforms/xcb/qxcbscreen.h | 1 + src/plugins/platforms/xlib/qxlibcursor.cpp | 8 +--- src/plugins/platforms/xlib/qxlibcursor.h | 5 ++- src/plugins/platforms/xlib/qxlibscreen.cpp | 5 +++ src/plugins/platforms/xlib/qxlibscreen.h | 1 + src/widgets/kernel/qwidget_qpa.cpp | 14 +++---- 31 files changed, 157 insertions(+), 84 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qcursor.cpp b/src/gui/kernel/qcursor.cpp index f16e5c85de..95b2b4a28e 100644 --- a/src/gui/kernel/qcursor.cpp +++ b/src/gui/kernel/qcursor.cpp @@ -185,10 +185,10 @@ QT_BEGIN_NAMESPACE */ /*! - \fn QPoint QCursor::pos() + \fn QPoint QCursor::pos(const QScreen *screen) - Returns the position of the cursor (hot spot) in global screen - coordinates. + Returns the position of the cursor (hot spot) of the \a screen + in global screen coordinates. You can call QWidget::mapFromGlobal() to translate it to widget coordinates. @@ -197,10 +197,23 @@ QT_BEGIN_NAMESPACE */ /*! - \fn void QCursor::setPos(int x, int y) + \fn QPoint QCursor::pos() + + Returns the position of the cursor (hot spot) of + the primary screen in global screen coordinates. + + You can call QWidget::mapFromGlobal() to translate it to widget + coordinates. + + \sa setPos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal(), QGuiApplication::primaryScreen() +*/ - Moves the cursor (hot spot) to the global screen position (\a x, - \a y). + +/*! + \fn void QCursor::setPos(QScreen *screen, int x, int y) + + Moves the cursor (hot spot) of the \a screen to the global + screen position (\a x, \a y). You can call QWidget::mapToGlobal() to translate widget coordinates to global screen coordinates. @@ -208,6 +221,18 @@ QT_BEGIN_NAMESPACE \sa pos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal() */ +/*! + \fn void QCursor::setPos(int x, int y) + + Moves the cursor (hot spot) of the primary screen + to the global screen position (\a x, \a y). + + You can call QWidget::mapToGlobal() to translate widget + coordinates to global screen coordinates. + + \sa pos(), QWidget::mapFromGlobal(), QWidget::mapToGlobal(), QGuiApplication::primaryScreen() +*/ + /*! \fn void QCursor::setPos (const QPoint &p) @@ -217,6 +242,15 @@ QT_BEGIN_NAMESPACE \a p. */ +/*! + \fn void QCursor::setPos (QScreen *screen,const QPoint &p) + + \overload + + Moves the cursor (hot spot) to the global screen position of the + \a screen at point \a p. +*/ + /***************************************************************************** QCursor stream functions *****************************************************************************/ diff --git a/src/gui/kernel/qcursor.h b/src/gui/kernel/qcursor.h index 00b709b308..4820bcee9b 100644 --- a/src/gui/kernel/qcursor.h +++ b/src/gui/kernel/qcursor.h @@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE class QVariant; +class QScreen; /* ### The fake cursor has to go first with old qdoc. @@ -101,8 +102,11 @@ public: QPoint hotSpot() const; static QPoint pos(); + static QPoint pos(const QScreen *screen); static void setPos(int x, int y); + static void setPos(QScreen *screen, int x, int y); inline static void setPos(const QPoint &p) { setPos(p.x(), p.y()); } + inline static void setPos(QScreen *screen, const QPoint &p) { setPos(screen, p.x(), p.y()); } #ifdef qdoc HCURSOR_or_HANDLE handle() const; diff --git a/src/gui/kernel/qcursor_qpa.cpp b/src/gui/kernel/qcursor_qpa.cpp index 7ba2e1c580..1c719c839e 100644 --- a/src/gui/kernel/qcursor_qpa.cpp +++ b/src/gui/kernel/qcursor_qpa.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include +#include #include #include #include @@ -110,27 +111,34 @@ void QCursorData::update() QPoint QCursor::pos() { + return QCursor::pos(QGuiApplication::primaryScreen()); +} + +QPoint QCursor::pos(const QScreen *screen) +{ + if (screen) + if (const QPlatformCursor *cursor = screen->handle()->cursor()) + return cursor->pos(); return QGuiApplicationPrivate::lastCursorPosition.toPoint(); } -void QCursor::setPos(int x, int y) +void QCursor::setPos(QScreen *screen, int x, int y) { - QPoint target(x, y); - - // Need to check, since some X servers generate null mouse move - // events, causing looping in applications which call setPos() on - // every mouse move event. - // - if (pos() == target) - return; - - QList > cursors = QPlatformCursorPrivate::getInstances(); - int cursorCount = cursors.count(); - for (int i = 0; i < cursorCount; ++i) { - const QWeakPointer &cursor(cursors.at(i)); - if (cursor) - cursor.data()->setPos(target); + if (screen) { + if (QPlatformCursor *cursor = screen->handle()->cursor()) { + const QPoint pos = QPoint(x, y); + // Need to check, since some X servers generate null mouse move + // events, causing looping in applications which call setPos() on + // every mouse move event. + if (pos != cursor->pos()) + cursor->setPos(pos); + } } } +void QCursor::setPos(int x, int y) +{ + QCursor::setPos(QGuiApplication::primaryScreen(), x, y); +} + QT_END_NAMESPACE diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index f5aea654fc..42ce3745de 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -978,10 +978,9 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo QMouseEvent ev(type, localPoint, localPoint, globalPoint, button, buttons, e->modifiers); ev.setTimestamp(e->timestamp); #ifndef QT_NO_CURSOR - QList > cursors = QPlatformCursorPrivate::getInstances(); - for (int i = 0; i < cursors.count(); ++i) - if (cursors.at(i)) - cursors.at(i).data()->pointerEvent(ev); + if (const QScreen *screen = window->screen()) + if (QPlatformCursor *cursor = screen->handle()->cursor()) + cursor->pointerEvent(ev); #endif QGuiApplication::sendSpontaneousEvent(window, &ev); if (!e->synthetic && !ev.isAccepted() && qApp->testAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents)) { @@ -1814,16 +1813,11 @@ void QGuiApplication::changeOverrideCursor(const QCursor &cursor) #ifndef QT_NO_CURSOR -static void applyCursor(QWindow *w, const QCursor &c) -{ - QCursor cc = c; - QList > cursors = QPlatformCursorPrivate::getInstances(); - int cursorCount = cursors.count(); - for (int i = 0; i < cursorCount; ++i) { - const QWeakPointer &cursor(cursors.at(i)); - if (cursor) - cursor.data()->changeCursor(&cc, w); - } +static inline void applyCursor(QWindow *w, QCursor c) +{ + if (const QScreen *screen = w->screen()) + if (QPlatformCursor *cursor = screen->handle()->cursor()) + cursor->changeCursor(&c, w); } static inline void applyCursor(const QList &l, const QCursor &c) diff --git a/src/gui/kernel/qplatformcursor_qpa.cpp b/src/gui/kernel/qplatformcursor_qpa.cpp index fd7bcdcf18..a4e998ddbc 100644 --- a/src/gui/kernel/qplatformcursor_qpa.cpp +++ b/src/gui/kernel/qplatformcursor_qpa.cpp @@ -43,13 +43,22 @@ #include #include #include +#include +#include #include #include QT_BEGIN_NAMESPACE -QList > QPlatformCursorPrivate::instances; +QList QPlatformCursorPrivate::getInstances() +{ + QList result; + foreach (const QScreen *screen, QGuiApplicationPrivate::screen_list) + if (QPlatformCursor *cursor = screen->handle()->cursor()) + result.push_back(cursor); + return result; +} /*! \class QPlatformCursor @@ -93,10 +102,8 @@ QList > QPlatformCursorPrivate::instances; Constructs a QPlatformCursor for the given \a screen. */ -QPlatformCursor::QPlatformCursor(QPlatformScreen *scr ) - : screen(scr) +QPlatformCursor::QPlatformCursor() { - QPlatformCursorPrivate::instances.append(this); } QPoint QPlatformCursor::pos() const diff --git a/src/gui/kernel/qplatformcursor_qpa.h b/src/gui/kernel/qplatformcursor_qpa.h index a8cbb282fa..e29cf87d03 100644 --- a/src/gui/kernel/qplatformcursor_qpa.h +++ b/src/gui/kernel/qplatformcursor_qpa.h @@ -74,13 +74,12 @@ class QPlatformCursor; class Q_GUI_EXPORT QPlatformCursorPrivate { public: - static QList > getInstances() { return instances; } - static QList > instances; + static QList getInstances(); }; class Q_GUI_EXPORT QPlatformCursor : public QObject { public: - QPlatformCursor(QPlatformScreen *); + QPlatformCursor(); // input methods virtual void pointerEvent(const QMouseEvent & event) { Q_UNUSED(event); } @@ -88,11 +87,8 @@ public: virtual QPoint pos() const; virtual void setPos(const QPoint &pos); -protected: - QPlatformScreen* screen; // Where to request an update - private: - Q_DECLARE_PRIVATE(QPlatformCursor); + Q_DECLARE_PRIVATE(QPlatformCursor) friend void qt_qpa_set_cursor(QWidget * w, bool force); friend class QApplicationPrivate; }; diff --git a/src/gui/kernel/qplatformscreen_qpa.cpp b/src/gui/kernel/qplatformscreen_qpa.cpp index c832d853f4..022f198073 100644 --- a/src/gui/kernel/qplatformscreen_qpa.cpp +++ b/src/gui/kernel/qplatformscreen_qpa.cpp @@ -41,6 +41,7 @@ #include "qplatformscreen_qpa.h" #include +#include #include #include #include @@ -250,4 +251,14 @@ QPlatformScreenPageFlipper *QPlatformScreen::pageFlipper() const return 0; } +/*! + Reimplement this function in subclass to return the cursor of the screen. + + The default implementation returns 0. +*/ +QPlatformCursor *QPlatformScreen::cursor() const +{ + return 0; +} + QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformscreen_qpa.h b/src/gui/kernel/qplatformscreen_qpa.h index 7d74698074..b6eb91f9b2 100644 --- a/src/gui/kernel/qplatformscreen_qpa.h +++ b/src/gui/kernel/qplatformscreen_qpa.h @@ -63,6 +63,7 @@ class QPlatformBackingStore; class QPlatformOpenGLContext; class QPlatformScreenPrivate; class QPlatformWindow; +class QPlatformCursor; class QPlatformScreenPageFlipper; class QScreen; class QSurfaceFormat; @@ -103,6 +104,7 @@ public: virtual QString name() const { return QString(); } virtual QPlatformScreenPageFlipper *pageFlipper() const; + virtual QPlatformCursor *cursor() const; protected: QScopedPointer d_ptr; diff --git a/src/plugins/platforms/cocoa/qcocoacursor.h b/src/plugins/platforms/cocoa/qcocoacursor.h index bccaa1e06b..85892ee820 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.h +++ b/src/plugins/platforms/cocoa/qcocoacursor.h @@ -52,7 +52,7 @@ QT_BEGIN_NAMESPACE class QCocoaCursor : public QPlatformCursor { public: - explicit QCocoaCursor(QPlatformScreen *); + QCocoaCursor(); virtual void changeCursor(QCursor * widgetCursor, QWindow * widget); virtual QPoint pos() const; diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm index bddfaa752e..56f0dcf72e 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.mm +++ b/src/plugins/platforms/cocoa/qcocoacursor.mm @@ -47,8 +47,7 @@ QT_BEGIN_NAMESPACE -QCocoaCursor::QCocoaCursor(QPlatformScreen *s) : - QPlatformCursor(s) +QCocoaCursor::QCocoaCursor() { // release cursors QHash::const_iterator i = m_cursors.constBegin(); diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index aa0c933fab..bf54915365 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -63,6 +63,7 @@ public: int depth() const { return m_depth; } QImage::Format format() const { return m_format; } QSizeF physicalSize() const { return m_physicalSize; } + QPlatformCursor *cursor() const { return m_cursor; } public: NSScreen *m_screen; diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 7921cc6ae7..8411a795c1 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -79,7 +79,7 @@ QCocoaScreen::QCocoaScreen(int screenIndex) const qreal inch = 25.4; m_physicalSize = QSizeF(m_geometry.size()) * inch / dpi; - m_cursor = new QCocoaCursor(this); + m_cursor = new QCocoaCursor; }; QCocoaScreen::~QCocoaScreen() diff --git a/src/plugins/platforms/directfb/qdirectfbcursor.cpp b/src/plugins/platforms/directfb/qdirectfbcursor.cpp index e1660a998a..a63bc48133 100644 --- a/src/plugins/platforms/directfb/qdirectfbcursor.cpp +++ b/src/plugins/platforms/directfb/qdirectfbcursor.cpp @@ -45,7 +45,7 @@ QT_BEGIN_NAMESPACE QDirectFBCursor::QDirectFBCursor(QPlatformScreen *screen) - : QPlatformCursor(screen) + : m_screen(screen) { m_image.reset(new QPlatformCursorImage(0, 0, 0, 0, 0, 0)); } @@ -70,7 +70,7 @@ void QDirectFBCursor::changeCursor(QCursor *cursor, QWindow *) } DFBResult res; - IDirectFBDisplayLayer *layer = toDfbLayer(screen); + IDirectFBDisplayLayer *layer = toDfbLayer(m_screen); IDirectFBSurface* surface(QDirectFbConvenience::dfbSurfaceForPlatformPixmap(map.handle())); res = layer->SetCooperativeLevel(layer, DLSCL_ADMINISTRATIVE); diff --git a/src/plugins/platforms/directfb/qdirectfbcursor.h b/src/plugins/platforms/directfb/qdirectfbcursor.h index 3cc2825b28..f28e225dc7 100644 --- a/src/plugins/platforms/directfb/qdirectfbcursor.h +++ b/src/plugins/platforms/directfb/qdirectfbcursor.h @@ -60,6 +60,7 @@ public: private: QScopedPointer m_image; + QPlatformScreen *m_screen; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/directfb/qdirectfbscreen.h b/src/plugins/platforms/directfb/qdirectfbscreen.h index ad80576c8f..8535239088 100644 --- a/src/plugins/platforms/directfb/qdirectfbscreen.h +++ b/src/plugins/platforms/directfb/qdirectfbscreen.h @@ -61,6 +61,7 @@ public: int depth() const { return m_depth; } QImage::Format format() const { return m_format; } QSizeF physicalSize() const { return m_physicalSize; } + QPlatformCursor *cursor() const { return m_cursor.data(); } // DirectFb helpers IDirectFBDisplayLayer *dfbLayer() const; diff --git a/src/plugins/platforms/kms/qkmscursor.cpp b/src/plugins/platforms/kms/qkmscursor.cpp index a38f66bd02..37817af39d 100644 --- a/src/plugins/platforms/kms/qkmscursor.cpp +++ b/src/plugins/platforms/kms/qkmscursor.cpp @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE QKmsCursor::QKmsCursor(QKmsScreen *screen) - : QPlatformCursor(screen), m_screen(screen), + : m_screen(screen), m_graphicsBufferManager(screen->device()->gbmDevice()) { gbm_bo *bo = gbm_bo_create(m_graphicsBufferManager, 64, 64, diff --git a/src/plugins/platforms/kms/qkmsscreen.cpp b/src/plugins/platforms/kms/qkmsscreen.cpp index f1b9baf120..76b9bce28f 100644 --- a/src/plugins/platforms/kms/qkmsscreen.cpp +++ b/src/plugins/platforms/kms/qkmsscreen.cpp @@ -96,6 +96,11 @@ QSizeF QKmsScreen::physicalSize() const return m_physicalSize; } +QPlatformCursor *QKmsScreen::cursor() const +{ + return m_cursor; +} + GLuint QKmsScreen::framebufferObject() const { return m_bufferManager.framebufferObject(); diff --git a/src/plugins/platforms/kms/qkmsscreen.h b/src/plugins/platforms/kms/qkmsscreen.h index 4cb547b424..058314a515 100644 --- a/src/plugins/platforms/kms/qkmsscreen.h +++ b/src/plugins/platforms/kms/qkmsscreen.h @@ -61,6 +61,7 @@ public: int depth() const; QImage::Format format() const; QSizeF physicalSize() const; + QPlatformCursor *cursor() const; GLuint framebufferObject() const; quint32 crtcId() const { return m_crtcId; } diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp index f75cbeb078..337ba9ab27 100644 --- a/src/plugins/platforms/windows/qwindowscursor.cpp +++ b/src/plugins/platforms/windows/qwindowscursor.cpp @@ -72,11 +72,6 @@ Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap); \sa QWindowsWindowCursor */ -QWindowsCursor::QWindowsCursor(QPlatformScreen *s) : - QPlatformCursor(s) -{ -} - HCURSOR QWindowsCursor::createPixmapCursor(const QPixmap &pixmap, int hotX, int hotY) { HCURSOR cur = 0; diff --git a/src/plugins/platforms/windows/qwindowscursor.h b/src/plugins/platforms/windows/qwindowscursor.h index a03c77a3b6..61c43dc4d2 100644 --- a/src/plugins/platforms/windows/qwindowscursor.h +++ b/src/plugins/platforms/windows/qwindowscursor.h @@ -70,7 +70,7 @@ private: class QWindowsCursor : public QPlatformCursor { public: - explicit QWindowsCursor(QPlatformScreen *); + QWindowsCursor() {} virtual void changeCursor(QCursor * widgetCursor, QWindow * widget); virtual QPoint pos() const { return mousePosition(); } diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 1dc5175515..2476e15169 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -147,6 +147,14 @@ static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d) return dbg; } +// Return the cursor to be shared by all screens (virtual desktop). +static inline QSharedPointer sharedCursor() +{ + if (const QScreen *primaryScreen = QGuiApplication::primaryScreen()) + return static_cast(primaryScreen->handle())->windowsCursor(); + return QSharedPointer(new QWindowsCursor); +} + /*! \class QWindowsScreen \brief Windows screen. @@ -155,7 +163,7 @@ static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d) */ QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) : - m_data(data), m_cursor(this) + m_data(data), m_cursor(sharedCursor()) { } diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index 77a327a62a..5b9a50b2ab 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.h +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -46,6 +46,7 @@ #include #include +#include #include QT_BEGIN_NAMESPACE @@ -74,6 +75,8 @@ struct QWindowsScreenData class QWindowsScreen : public QPlatformScreen { public: + typedef QSharedPointer WindowsCursorPtr; + explicit QWindowsScreen(const QWindowsScreenData &data); static QWindowsScreen *screenOf(const QWindow *w = 0); @@ -98,14 +101,14 @@ public: inline void handleChanges(const QWindowsScreenData &newData); - const QWindowsCursor &cursor() const { return m_cursor; } - QWindowsCursor &cursor() { return m_cursor; } + QPlatformCursor *cursor() const { return m_cursor.data(); } + const WindowsCursorPtr &windowsCursor() const { return m_cursor; } const QWindowsScreenData &data() const { return m_data; } private: QWindowsScreenData m_data; - QWindowsCursor m_cursor; + const WindowsCursorPtr m_cursor; }; class QWindowsScreenManager diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index f5cac4d4f0..1edb243f4e 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -618,7 +618,7 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : m_windowState(aWindow->windowState()), m_opacity(1.0), m_mouseGrab(false), - m_cursor(QWindowsScreen::screenOf(aWindow)->cursor().standardWindowCursor()), + m_cursor(QWindowsScreen::screenOf(aWindow)->windowsCursor()->standardWindowCursor()), m_dropTarget(0), m_savedStyle(0) { diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index ed7c22b1e6..7e1b66829a 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -261,7 +261,7 @@ static const char * const cursorNames[] = { }; QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) - : QXcbObject(conn), QPlatformCursor(screen), m_screen(screen) + : QXcbObject(conn), m_screen(screen) { if (cursorCount++) return; diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 15ffc5b8ff..8b66ef4603 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -237,6 +237,11 @@ QSizeF QXcbScreen::physicalSize() const return QSizeF(m_screen->width_in_millimeters, m_screen->height_in_millimeters); } +QPlatformCursor *QXcbScreen::cursor() const +{ + return m_cursor; +} + int QXcbScreen::screenNumber() const { return m_number; diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 870d4d5662..ac4ecb1c8d 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -68,6 +68,7 @@ public: int depth() const; QImage::Format format() const; QSizeF physicalSize() const; + QPlatformCursor *cursor() const; int screenNumber() const; diff --git a/src/plugins/platforms/xlib/qxlibcursor.cpp b/src/plugins/platforms/xlib/qxlibcursor.cpp index 8ab40e31ef..a714f82371 100644 --- a/src/plugins/platforms/xlib/qxlibcursor.cpp +++ b/src/plugins/platforms/xlib/qxlibcursor.cpp @@ -52,8 +52,7 @@ QT_BEGIN_NAMESPACE -QXlibCursor::QXlibCursor(QXlibScreen *screen) - : QPlatformCursor(screen) +QXlibCursor::QXlibCursor(QXlibScreen *screen) : m_screen(screen) { } @@ -191,9 +190,4 @@ Cursor QXlibCursor::createCursorShape(int cshape) return cursor; } -QXlibScreen * QXlibCursor::testLiteScreen() const -{ - return static_cast(screen); -} - QT_END_NAMESPACE diff --git a/src/plugins/platforms/xlib/qxlibcursor.h b/src/plugins/platforms/xlib/qxlibcursor.h index 92e42ac7a1..0056aa9554 100644 --- a/src/plugins/platforms/xlib/qxlibcursor.h +++ b/src/plugins/platforms/xlib/qxlibcursor.h @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE -class QXlibCursor : QPlatformCursor +class QXlibCursor : public QPlatformCursor { public: QXlibCursor(QXlibScreen *screen); @@ -59,8 +59,9 @@ private: Cursor createCursorBitmap(QCursor * cursor); Cursor createCursorShape(int cshape); - QXlibScreen *testLiteScreen() const; + QXlibScreen *testLiteScreen() const { return m_screen; } QMap cursorMap; + QXlibScreen *m_screen; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xlib/qxlibscreen.cpp b/src/plugins/platforms/xlib/qxlibscreen.cpp index fc903708b3..62c80b6ad4 100644 --- a/src/plugins/platforms/xlib/qxlibscreen.cpp +++ b/src/plugins/platforms/xlib/qxlibscreen.cpp @@ -241,6 +241,11 @@ QXlibScreen::~QXlibScreen() delete mDisplay; } +QPlatformCursor *QXlibScreen::cursor() const +{ + return mCursor; +} + Window QXlibScreen::rootWindow() { return RootWindow(mDisplay->nativeDisplay(), mScreen); diff --git a/src/plugins/platforms/xlib/qxlibscreen.h b/src/plugins/platforms/xlib/qxlibscreen.h index c6672c3540..5bb6cc7735 100644 --- a/src/plugins/platforms/xlib/qxlibscreen.h +++ b/src/plugins/platforms/xlib/qxlibscreen.h @@ -63,6 +63,7 @@ public: int depth() const { return mDepth; } QImage::Format format() const { return mFormat; } QSizeF physicalSize() const { return mPhysicalSize; } + QPlatformCursor *cursor() const; Window rootWindow(); unsigned long blackPixel(); diff --git a/src/widgets/kernel/qwidget_qpa.cpp b/src/widgets/kernel/qwidget_qpa.cpp index a37de8547a..3d23b04ddf 100644 --- a/src/widgets/kernel/qwidget_qpa.cpp +++ b/src/widgets/kernel/qwidget_qpa.cpp @@ -905,16 +905,12 @@ void QWidgetPrivate::setModal_sys() } #ifndef QT_NO_CURSOR -static void applyCursor(QWidget *w, const QCursor &c) +static inline void applyCursor(QWidget *w, QCursor c) { - QCursor cc = c; - QList > cursors = QPlatformCursorPrivate::getInstances(); - int cursorCount = cursors.count(); - for (int i = 0; i < cursorCount; ++i) { - const QWeakPointer &cursor(cursors.at(i)); - if (cursor) - cursor.data()->changeCursor(&cc, w->window()->windowHandle()); - } + if (QWindow *window = w->windowHandle()) + if (const QScreen *screen = window->screen()) + if (QPlatformCursor *cursor = screen->handle()->cursor()) + cursor->changeCursor(&c, window); } void qt_qpa_set_cursor(QWidget *w, bool force) -- cgit v1.2.3