diff options
220 files changed, 8822 insertions, 992 deletions
diff --git a/config.tests/unix/iconv/iconv.pro b/config.tests/unix/iconv/iconv.pro index ec573ce590..876c6f3589 100644 --- a/config.tests/unix/iconv/iconv.pro +++ b/config.tests/unix/iconv/iconv.pro @@ -1,3 +1,3 @@ SOURCES = iconv.cpp CONFIG -= qt dylib app_bundle -mac|win32-g++*|blackberry-*-qcc:LIBS += -liconv +mac|win32-g++*|qnx-*-qcc:LIBS += -liconv diff --git a/config.tests/unix/libudev/libudev.pro b/config.tests/unix/libudev/libudev.pro index 7f571b5a6d..28b8980e2e 100644 --- a/config.tests/unix/libudev/libudev.pro +++ b/config.tests/unix/libudev/libudev.pro @@ -1,3 +1,4 @@ SOURCES = libudev.cpp CONFIG -= qt -LIBS += -ludev +LIBS += $$QMAKE_LIBS_LIBUDEV +INCLUDEPATH += $$QMAKE_INCDIR_LIBUDEV @@ -5294,7 +5294,13 @@ if [ "$PLATFORM_QPA" = "yes" ]; then fi if [ "$CFG_LIBUDEV" != "no" ]; then - if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/unix/libudev "libudev" $L_FLAGS $I_FLAGS $l_FLAGS; then + if [ -n "$PKG_CONFIG" ] && $PKG_CONFIG --exists libudev 2>/dev/null; then + QMAKE_INCDIR_LIBUDEV=`$PKG_CONFIG --cflags-only-I libudev 2>/dev/null | sed -e 's,^-I,,g' -e 's, -I, ,g'` + QMAKE_LIBS_LIBUDEV=`$PKG_CONFIG --libs libudev 2>/dev/null` + QMakeVar set QMAKE_INCDIR_LIBUDEV "$QMAKE_INCDIR_LIBUDEV" + QMakeVar set QMAKE_LIBS_LIBUDEV "$QMAKE_LIBS_LIBUDEV" + fi + if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/unix/libudev "libudev" $L_FLAGS $I_FLAGS $l_FLAGS $QMAKE_INCDIR_LIBUDEV $QMAKE_LIBS_LIBUDEV; then CFG_LIBUDEV=yes QT_CONFIG="$QT_CONFIG libudev" elif [ "$CFG_LIBUDEV" = "yes" ]; then @@ -6890,6 +6896,7 @@ else fi echo "zlib support ........... $CFG_ZLIB" echo "Session management ..... $CFG_SM" +echo "libudev support ........ $CFG_LIBUDEV" if [ "$CFG_OPENGL" = "desktop" ]; then echo "OpenGL support ......... yes (Desktop OpenGL)" diff --git a/dist/changes-5.0.0 b/dist/changes-5.0.0 index 47994aa774..500e97085b 100644 --- a/dist/changes-5.0.0 +++ b/dist/changes-5.0.0 @@ -203,6 +203,8 @@ information about a particular change. - qmake * Projects which explicitly set an empty TARGET are considered broken now. * The makespec and .qmake.cache do not see build pass specific variables any more. + * load()/include() with a target namespace and infile()/$$fromfile() now start with + an entirely pristine context. * Configure's -sysroot and -hostprefix are now handled slightly differently. The QT_INSTALL_... properties are now automatically prefixed with the sysroot; the raw values are available as QT_RAW_INSTALL_... and the sysroot as QT_SYSROOT. @@ -308,9 +310,18 @@ QtCore libraries, creating uncertainty/bugs in using QString easily, and (to a lesser extent) performance issues. +* QTextCodec::codecForTr() and QTextCodec::setCodecForTr() have been removed, + QCoreApplication::Encoding value CodecForTr is now obsolete, use + DefaultCodec instead. For reasoning, see the codecForCStrings() removal above. + * QIntValidator and QDoubleValidator no longer fall back to using the C locale if the requested locale fails to validate the input. +* A new set of classes for doing pattern matching with Perl-compatible regular + expressions has been added: QRegularExpression, QRegularExpressionMatch and + QRegularExpressionMatchIterator. They aim to replace QRegExp with a more + powerful and flexible regular expression engine. + QtGui ----- * Accessibility has been refactored. The hierachy of accessible objects is implemented via @@ -395,6 +406,17 @@ ignore the rest of the range. QSqlTableModel::indexInQuery() as example of how to implement in a subclass. +* QSqlTableModel::selectRow(): This is a new method that refreshes a single +row in the model from the database. + +* QSqlTableModel edit strategies OnFieldChange/OnRowChange QTBUG-2875 +Previously, after changes were submitted in these edit strategies, select() +was called which removed and inserted all rows. This ruined navigation +in QTableView. Now, with these edit strategies, there is no implicit select() +done after committing. This includes deleted rows which remain in +the model as blank rows until the application calls select(). Instead, +selectRow() is called to refresh only the affected row. + **************************************************************************** * Database Drivers * **************************************************************************** diff --git a/doc/src/examples/widgets/mousebuttons.qdoc b/doc/src/examples/widgets/mousebuttons.qdoc new file mode 100644 index 0000000000..bf63cd5076 --- /dev/null +++ b/doc/src/examples/widgets/mousebuttons.qdoc @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2012 Rick Stockton <rickstockton@reno-computerhelp.com> +** Contact: http://www.qt-project.org/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. +** +** 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$ +** +****************************************************************************/ + +/*! + \example widgets/mousebuttons/buttontester + \title Mouse Button Tester + + \brief The 'Mouse Button Tester' example demonstrates how to reimplement + mouse events within a custom class. You can also use this program to + verify that Qt is actually receiving mouse events from your mouse. + + Many 'gamer' mouse devices are configured with high-numbered "buttons" + sending text shortcuts for certain games. With such a mouse, no mouse + button events occur: The "mouse" sends keystrokes, and the + 'Mouse Button Tester' Window will not see the event. Receving no event, + it will not repaint the Window with new text describing a \button event. + + And so, in addition to it's use as Qt example code, the program may be + useful s a mouse device tester. Note that there is another example + muouse buttons example which provides the same function, written in QML. + + This program (the Widget-based example) consists of three classes, + in addition to the main() parent program: + + \list + \o \c A QPushButton, "Quit". + \o \c ButtonTester. This is derived from Qt's TextArea class, for + purpose of customizing/re-implementing the mouse and wheel event + member functions. + \o \c A simple QVBoxLayout layout. + \endlist + + First we will review the main program, with it's layout and "Quit" + QPushButton. Then we will take a look at the \c ButtonTester class. + + \section1 The Main Program + + Note that the QPushButton, "Quit", is defined directly within the main() + program, rather than another class. This is a correct way of defining a + "Quit" QPushButton: A "Quit" Button defined inside another + class would result in the destructor of that second class being + called twice. This "Quit" Button uses the traditional Signal/Slot + connection to invoke termination of the QApp, which will properly destroy + its child classes \before terminating itself. + + The remainder of the main() program is concerned with defining the layout, + and applying a minimum size to the customized ButtonTester. + + \section1 ButtonTester Class Definition + + The \c ButtonTester class inherits from QTextEdit, and listens for + mouse events on all possible Qt::MouseButton values. It also listens for + wheel events from the mouse, and indicates the direction of wheel motion + ("up", down", "left", or "right"). It prints short debug messages into + the Window, and also on the console QDebug() stream, when mouse button + and wheel events occur. Our reimplementation of mousePressEvent(), + mouseReleaseEvent(), mouseDoubleClickEvent(), and wheelEvent() "drive" + the program; the other functions simply convert the Qt::MouseButton + values into text strings. + + You should call the ignore() function on any mouse event (or other event) + which your widget-based classes do not use and consume. This function + assures that Qt will propagate the event through each parent widget, + until it is used or propagated to the Window Manager. (Qt attempts to do + this automatically, but it is better programming practice to explicitly + invoke the function.) + + \image widgets/mousebutton-buttontester.png +*/ diff --git a/doc/src/images/widgets/mousebutton-buttontester.png b/doc/src/images/widgets/mousebutton-buttontester.png Binary files differnew file mode 100644 index 0000000000..82fcb7617f --- /dev/null +++ b/doc/src/images/widgets/mousebutton-buttontester.png diff --git a/doc/src/network/files-and-resources/datastreamformat.qdoc b/doc/src/network/files-and-resources/datastreamformat.qdoc index 17a0044a69..8ff31371a2 100644 --- a/doc/src/network/files-and-resources/datastreamformat.qdoc +++ b/doc/src/network/files-and-resources/datastreamformat.qdoc @@ -298,6 +298,11 @@ \li Regular expression syntax (quint8) \li Minimal matching (quint8) \endlist + \row \li QRegularExpression + \li \list + \li The regular expression pattern (QString) + \li The pattern options (quint32) + \endlist \row \li QRegion \li \list \li The size of the data, i.e. 8 + 16 * (number of rectangles) (quint32) diff --git a/doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp b/doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp new file mode 100644 index 0000000000..cab89d9c9e --- /dev/null +++ b/doc/src/snippets/code/src_corelib_tools_qregularexpression.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. +** Contact: http://www.qt-project.org/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [0] +QRegularExpression re("a pattern"); +//! [0] + + +//! [1] +QRegularExpression re; +re.setPattern("another pattern"); +//! [1] + + +//! [2] +// matches two digits followed by a space and a word +QRegularExpression re("\\d\\d \\w+"); + +// matches a backslash +QRegularExpression re2("\\\\"); +//! [2] + + +//! [3] +QRegularExpression re("a third pattern"); +QString pattern = re.pattern(); // pattern == "a third pattern" +//! [3] + + +//! [4] +// matches "Qt rocks", but also "QT rocks", "QT ROCKS", "qT rOcKs", etc. +QRegularExpression re("Qt rocks", QRegularExpression::CaseInsensitiveOption); +//! [4] + + +//! [5] +QRegularExpression re("^\\d+$"); +re.setPatternOptions(QRegularExpression::MultilineOption); +// re matches any line in the subject string that contains only digits (but at least one) +//! [5] + + +//! [6] +QRegularExpression re = QRegularExpression("^two.*words$", QRegularExpression::MultilineOption + | QRegularExpression::DotMatchesEverythingOption); + +QRegularExpression::PatternOptions options = re.patternOptions(); +// options == QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption +//! [6] + + +//! [7] +// match two digits followed by a space and a word +QRegularExpression re("\\d\\d \\w+"); +QRegularExpressionMatch match = re.match("abc123 def"); +bool hasMatch = match.hasMatch(); // true +//! [7] + + +//! [8] +QRegularExpression re("\\d\\d \\w+"); +QRegularExpressionMatch match = re.match("abc123 def"); +if (match.hasMatch()) { + QString matched = match.captured(0); // matched == "23 def" + // ... +} +//! [8] + + +//! [9] +QRegularExpression re("\\d\\d \\w+"); +QRegularExpressionMatch match = re.match("12 abc 45 def", 1); +if (match.hasMatch()) { + QString matched = match.captured(0); // matched == "45 def" + // ... +} +//! [9] + + +//! [10] +QRegularExpression re("^(\\d\\d)/(\\d\\d)/(\\d\\d\\d\\d)$"); +QRegularExpressionMatch match = re.match("08/12/1985"); +if (match.hasMatch()) { + QString day = re.captured(1); // day == "08" + QString month = re.captured(2); // month == "12" + QString year = re.captured(3); // year == "1985" + // ... +} +//! [10] + + +//! [11] +QRegularExpression re("abc(\\d+)def"); +QRegularExpressionMatch match = re.match("XYZabc123defXYZ"); +if (match.hasMatch()) { + int startOffset = re.capturedStart(1); // startOffset == 6 + int endOffset = re.capturedEnd(1); // endOffset == 9 + // ... +} +//! [11] + + +//! [12] +QRegularExpression re("^(?<date>\\d\\d)/(?<month>\\d\\d)/(?<year>\\d\\d\\d\\d)$"); +QRegularExpressionMatch match = re.match("08/12/1985"); +if (match.hasMatch()) { + QString date = match.captured("date"); // date == "08" + QString month = match.captured("month"); // month == "12" + QString year = match.captured("year"); // year == 1985 +} +//! [12] + + +//! [13] +QRegularExpression re("(\\w+)"); +QRegularExpressionMatchIterator i = re.globalMatch("the quick fox"); +//! [13] + + +//! [14] +QStringList words; +while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + QString word = match.captured(1); + words << word; +} +// words contains "the", "quick", "fox" +//! [14] + + +//! [15] +QString pattern("^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \\d\\d?, \\d\\d\\d\\d$"); +QRegularExpression re(pattern); + +QString input("Jan 21,"); +QRegularExpressionMatch match = re.match(input, 0, QRegularExpressionMatch::PartialPreferCompleteMatch); +bool hasMatch = match.hasMatch(); // false +bool hasPartialMatch = match.hasPartialMatch(); // true +//! [15] + + +//! [16] +QString input("Dec 8, 1985"); +QRegularExpressionMatch match = re.match(input, 0, QRegularExpressionMatch::PartialPreferCompleteMatch); +bool hasMatch = match.hasMatch(); // true +bool hasPartialMatch = match.hasPartialMatch(); // false +//! [16] + + +//! [17] +QRegularExpression re("abc\\w+X|def"); +QRegularExpressionMatch match = re.match("abcdef", 0, QRegularExpressionMatch::PartialPreferCompleteMatch); +bool hasMatch = match.hasMatch(); // true +bool hasPartialMatch = match.hasPartialMatch(); // false +QString captured = match.captured(0); // captured == "def" +//! [17] + + +//! [18] +QRegularExpression re("abc\\w+X|defY"); +QRegularExpressionMatch match = re.match("abcdef", 0, QRegularExpressionMatch::PartialPreferCompleteMatch); +bool hasMatch = match.hasMatch(); // false +bool hasPartialMatch = match.hasPartialMatch(); // true +QString captured = match.captured(0); // captured == "abcdef" +//! [18] + + +//! [19] +QRegularExpression re("abc|ab"); +QRegularExpressionMatch match = re.match("ab", 0, QRegularExpressionMatch::PartialPreferFirstMatch); +bool hasMatch = match.hasMatch(); // false +bool hasPartialMatch = match.hasPartialMatch(); // true +//! [19] + + +//! [20] +QRegularExpression re("abc(def)?"); +QRegularExpressionMatch match = re.match("abc", 0, QRegularExpressionMatch::PartialPreferFirstMatch); +bool hasMatch = match.hasMatch(); // false +bool hasPartialMatch = match.hasPartialMatch(); // true +//! [20] + +//! [21] +QRegularExpression re("(abc)*"); +QRegularExpressionMatch match = re.match("abc", 0, QRegularExpressionMatch::PartialPreferFirstMatch); +bool hasMatch = match.hasMatch(); // false +bool hasPartialMatch = match.hasPartialMatch(); // true +//! [21] + +//! [22] +QRegularExpression invalidRe("(unmatched|parenthesis"); +bool isValid = invalidRe.isValid(); // false +//! [22] + +//! [23] +QRegularExpression invalidRe("(unmatched|parenthesis"); +if (!invalidRe.isValid()) { + QString errorString = invalidRe.errorString(); // errorString == "missing )" + int errorOffset = invalidRe.patternErrorOffset(); // errorOffset == 22 + // ... +} +//! [23] + +//! [24] +QRegularExpression re("^this pattern must match exactly$"); +//! [24] + +//! [25] +QString p("a .*|pattern"); +QRegularExpression re("\\A(?:" + p + ")\\z"); // re matches exactly the pattern string p +//! [25] + +//! [26] +QString escaped = QRegularExpression::escape("a(x) = f(x) + g(x)"); +// escaped == "a\\(x\\)\\ \\=\\ f\\(x\\)\\ \\+\\ g\\(x\\)" +//! [26] + +//! [27] +QString pattern = "(" + QRegularExpression::escape(name) + + "|" + QRegularExpression::escape(nickname) + ")"; +QRegularExpression re(pattern); +//! [27] + +//! [28] +QRegularExpressionMatch match = re.match(...); +for (int i = 0; i <= match.lastCapturedIndex(); ++i) { + QString captured = match.captured(i); + // ... +} +//! [28] + +//! [29] +QRegularExpression("(\d\d) (?<name>\w+)"); +QRegularExpressionMatch match = re.match("23 Jordan"); +if (match.hasMatch()) { + QString number = match.captured(1); // first == "23" + QString name = match.captured("name"); // name == "Jordan" +} +//! [29] + +//! [30] +// extracts the words +QRegularExpression re("(\w+)"); +QString subject("the quick fox"); +QRegularExpressionMatchIterator i = re.globalMatch(subject); +while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + // ... +} +//! [30] + + diff --git a/examples/sql/sqlbrowser/browser.cpp b/examples/sql/sqlbrowser/browser.cpp index 3aa330e227..eaaec25b16 100644 --- a/examples/sql/sqlbrowser/browser.cpp +++ b/examples/sql/sqlbrowser/browser.cpp @@ -52,6 +52,12 @@ Browser::Browser(QWidget *parent) table->addAction(insertRowAction); table->addAction(deleteRowAction); + table->addAction(fieldStrategyAction); + table->addAction(rowStrategyAction); + table->addAction(manualStrategyAction); + table->addAction(submitAction); + table->addAction(revertAction); + table->addAction(selectAction); if (QSqlDatabase::drivers().isEmpty()) QMessageBox::information(this, tr("No database drivers found"), @@ -144,7 +150,7 @@ void Browser::addConnection() void Browser::showTable(const QString &t) { - QSqlTableModel *model = new QSqlTableModel(table, connectionWidget->currentDatabase()); + QSqlTableModel *model = new CustomModel(table, connectionWidget->currentDatabase()); model->setEditStrategy(QSqlTableModel::OnRowChange); model->setTable(connectionWidget->currentDatabase().driver()->escapeIdentifier(t, QSqlDriver::TableName)); model->select(); @@ -215,8 +221,6 @@ void Browser::deleteRow() if (!model) return; - model->setEditStrategy(QSqlTableModel::OnManualSubmit); - QModelIndexList currentSelection = table->selectionModel()->selectedIndexes(); for (int i = 0; i < currentSelection.count(); ++i) { if (currentSelection.at(i).column() != 0) @@ -224,24 +228,79 @@ void Browser::deleteRow() model->removeRow(currentSelection.at(i).row()); } - model->submitAll(); - model->setEditStrategy(QSqlTableModel::OnRowChange); - updateActions(); } void Browser::updateActions() { - bool enableIns = qobject_cast<QSqlTableModel *>(table->model()); + QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); + bool enableIns = tm; bool enableDel = enableIns && table->currentIndex().isValid(); insertRowAction->setEnabled(enableIns); deleteRowAction->setEnabled(enableDel); + + fieldStrategyAction->setEnabled(tm); + rowStrategyAction->setEnabled(tm); + manualStrategyAction->setEnabled(tm); + submitAction->setEnabled(tm); + revertAction->setEnabled(tm); + selectAction->setEnabled(tm); + + if (tm) { + QSqlTableModel::EditStrategy es = tm->editStrategy(); + fieldStrategyAction->setChecked(es == QSqlTableModel::OnFieldChange); + rowStrategyAction->setChecked(es == QSqlTableModel::OnRowChange); + manualStrategyAction->setChecked(es == QSqlTableModel::OnManualSubmit); + } } void Browser::about() { QMessageBox::about(this, tr("About"), tr("The SQL Browser demonstration " "shows how a data browser can be used to visualize the results of SQL" - "statements on a live database")); + "statements on a live database")); +} + +void Browser::on_fieldStrategyAction_triggered() +{ + QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); + if (tm) + tm->setEditStrategy(QSqlTableModel::OnFieldChange); +} + +void Browser::on_rowStrategyAction_triggered() +{ + QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); + if (tm) + tm->setEditStrategy(QSqlTableModel::OnRowChange); +} + +void Browser::on_manualStrategyAction_triggered() +{ + QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); + if (tm) + tm->setEditStrategy(QSqlTableModel::OnManualSubmit); +} + +void Browser::on_submitAction_triggered() +{ + QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); + if (tm) + tm->submitAll(); +} + +void Browser::on_revertAction_triggered() +{ + QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); + if (tm) + tm->revertAll(); +} + +void Browser::on_selectAction_triggered() +{ + QSqlTableModel * tm = qobject_cast<QSqlTableModel *>(table->model()); + if (tm) + tm->select(); } + diff --git a/examples/sql/sqlbrowser/browser.h b/examples/sql/sqlbrowser/browser.h index 0769812ec8..e9f45345df 100644 --- a/examples/sql/sqlbrowser/browser.h +++ b/examples/sql/sqlbrowser/browser.h @@ -43,6 +43,7 @@ #define BROWSER_H #include <QWidget> +#include <QSqlTableModel> #include "ui_browserwidget.h" class ConnectionWidget; @@ -77,6 +78,12 @@ public slots: { insertRow(); } void on_deleteRowAction_triggered() { deleteRow(); } + void on_fieldStrategyAction_triggered(); + void on_rowStrategyAction_triggered(); + void on_manualStrategyAction_triggered(); + void on_submitAction_triggered(); + void on_revertAction_triggered(); + void on_selectAction_triggered(); void on_connectionWidget_tableActivated(const QString &table) { showTable(table); } void on_connectionWidget_metaDataRequested(const QString &table) @@ -96,4 +103,17 @@ signals: void statusMessage(const QString &message); }; +class CustomModel: public QSqlTableModel +{ + Q_OBJECT +public: + CustomModel(QObject *parent = 0, QSqlDatabase db = QSqlDatabase()):QSqlTableModel(parent, db) {} + QVariant data(const QModelIndex &idx, int role) const + { + if (role == Qt::BackgroundRole && isDirty(idx)) + return QBrush(QColor(Qt::yellow)); + return QSqlTableModel::data(idx, role); + } +}; + #endif diff --git a/examples/sql/sqlbrowser/browserwidget.ui b/examples/sql/sqlbrowser/browserwidget.ui index 20946f0ede..ff65b1f199 100644 --- a/examples/sql/sqlbrowser/browserwidget.ui +++ b/examples/sql/sqlbrowser/browserwidget.ui @@ -1,10 +1,8 @@ -<ui version="4.0" > - <author></author> - <comment></comment> - <exportmacro></exportmacro> +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> <class>Browser</class> - <widget class="QWidget" name="Browser" > - <property name="geometry" > + <widget class="QWidget" name="Browser"> + <property name="geometry"> <rect> <x>0</x> <y>0</y> @@ -12,100 +10,90 @@ <height>515</height> </rect> </property> - <property name="windowTitle" > + <property name="windowTitle"> <string>Qt SQL Browser</string> </property> - <layout class="QVBoxLayout" > - <property name="margin" > - <number>8</number> - </property> - <property name="spacing" > + <layout class="QVBoxLayout"> + <property name="spacing"> <number>6</number> </property> + <property name="margin"> + <number>8</number> + </property> <item> - <widget class="QSplitter" name="splitter_2" > - <property name="sizePolicy" > - <sizepolicy> - <hsizetype>7</hsizetype> - <vsizetype>7</vsizetype> + <widget class="QSplitter" name="splitter_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="orientation" > + <property name="orientation"> <enum>Qt::Horizontal</enum> </property> - <widget class="ConnectionWidget" name="connectionWidget" > - <property name="sizePolicy" > - <sizepolicy> - <hsizetype>13</hsizetype> - <vsizetype>7</vsizetype> + <widget class="ConnectionWidget" name="connectionWidget"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Ignored" vsizetype="Expanding"> <horstretch>1</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> </widget> - <widget class="QTableView" name="table" > - <property name="sizePolicy" > - <sizepolicy> - <hsizetype>7</hsizetype> - <vsizetype>7</vsizetype> + <widget class="QTableView" name="table"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <horstretch>2</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="contextMenuPolicy" > + <property name="contextMenuPolicy"> <enum>Qt::ActionsContextMenu</enum> </property> - <property name="selectionBehavior" > + <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> </widget> </widget> </item> <item> - <widget class="QGroupBox" name="groupBox" > - <property name="sizePolicy" > - <sizepolicy> - <hsizetype>5</hsizetype> - <vsizetype>3</vsizetype> + <widget class="QGroupBox" name="groupBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="maximumSize" > + <property name="maximumSize"> <size> <width>16777215</width> <height>180</height> </size> </property> - <property name="title" > + <property name="title"> <string>SQL Query</string> </property> - <layout class="QVBoxLayout" > - <property name="margin" > - <number>9</number> - </property> - <property name="spacing" > + <layout class="QVBoxLayout"> + <property name="spacing"> <number>6</number> </property> + <property name="margin"> + <number>9</number> + </property> <item> - <widget class="QTextEdit" name="sqlEdit" > - <property name="sizePolicy" > - <sizepolicy> - <hsizetype>7</hsizetype> - <vsizetype>3</vsizetype> + <widget class="QTextEdit" name="sqlEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> - <property name="minimumSize" > + <property name="minimumSize"> <size> <width>0</width> <height>18</height> </size> </property> - <property name="baseSize" > + <property name="baseSize"> <size> <width>0</width> <height>120</height> @@ -114,19 +102,19 @@ </widget> </item> <item> - <layout class="QHBoxLayout" > - <property name="margin" > - <number>1</number> - </property> - <property name="spacing" > + <layout class="QHBoxLayout"> + <property name="spacing"> <number>6</number> </property> + <property name="margin"> + <number>1</number> + </property> <item> <spacer> - <property name="orientation" > + <property name="orientation"> <enum>Qt::Horizontal</enum> </property> - <property name="sizeHint" > + <property name="sizeHint" stdset="0"> <size> <width>40</width> <height>20</height> @@ -135,15 +123,15 @@ </spacer> </item> <item> - <widget class="QPushButton" name="clearButton" > - <property name="text" > + <widget class="QPushButton" name="clearButton"> + <property name="text"> <string>&Clear</string> </property> </widget> </item> <item> - <widget class="QPushButton" name="submitButton" > - <property name="text" > + <widget class="QPushButton" name="submitButton"> + <property name="text"> <string>&Submit</string> </property> </widget> @@ -154,37 +142,91 @@ </widget> </item> </layout> - <action name="insertRowAction" > - <property name="enabled" > + <action name="insertRowAction"> + <property name="enabled"> <bool>false</bool> </property> - <property name="text" > + <property name="text"> <string>&Insert Row</string> </property> - <property name="statusTip" > + <property name="statusTip"> <string>Inserts a new Row</string> </property> </action> - <action name="deleteRowAction" > - <property name="enabled" > + <action name="deleteRowAction"> + <property name="enabled"> <bool>false</bool> </property> - <property name="text" > + <property name="text"> <string>&Delete Row</string> </property> - <property name="statusTip" > + <property name="statusTip"> <string>Deletes the current Row</string> </property> </action> + <action name="fieldStrategyAction"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Submit on &Field Change</string> + </property> + <property name="toolTip"> + <string>Commit on Field Change</string> + </property> + </action> + <action name="rowStrategyAction"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Submit on &Row Change</string> + </property> + <property name="toolTip"> + <string>Commit on Row Change</string> + </property> + </action> + <action name="manualStrategyAction"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="text"> + <string>Submit &Manually</string> + </property> + <property name="toolTip"> + <string>Commit Manually</string> + </property> + </action> + <action name="submitAction"> + <property name="text"> + <string>&Submit All</string> + </property> + <property name="toolTip"> + <string>Submit Changes</string> + </property> + </action> + <action name="revertAction"> + <property name="text"> + <string>&Revert All</string> + </property> + <property name="toolTip"> + <string>Revert</string> + </property> + </action> + <action name="selectAction"> + <property name="text"> + <string>S&elect</string> + </property> + <property name="toolTip"> + <string>Refresh Data from Database</string> + </property> + </action> </widget> - <pixmapfunction></pixmapfunction> <customwidgets> <customwidget> <class>ConnectionWidget</class> <extends>QTreeView</extends> <header>connectionwidget.h</header> - <container>0</container> - <pixmap></pixmap> </customwidget> </customwidgets> <tabstops> diff --git a/examples/widgets/mousebuttons/buttontester.cpp b/examples/widgets/mousebuttons/buttontester.cpp new file mode 100644 index 0000000000..32f73ebb72 --- /dev/null +++ b/examples/widgets/mousebuttons/buttontester.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2012 Rick Stockton <rickstockton@reno-computerhelp.com> +** Contact: http://www.qt-project.org/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "buttontester.h" + +void ButtonTester::mousePressEvent(QMouseEvent *e) +{ + int j = ButtonTester::buttonByNumber (e->button()); + QString result = "Mouse Press: raw button=" + QString::number(j) + " Qt=" + enumNameFromValue(e->button()); + qDebug() << result; + this->setText(result); + if (j == 2) { + this->repaint(); + } +} + +void ButtonTester::mouseReleaseEvent(QMouseEvent *e) +{ + int j = ButtonTester::buttonByNumber (e->button()); + QString result = "Mouse Release: raw button=" + QString::number(j) + " Qt=" + enumNameFromValue(e->button()); + qDebug() << result; + this->setText(result); +} + +void ButtonTester::mouseDoubleClickEvent(QMouseEvent *e) +{ + int j = ButtonTester::buttonByNumber (e->button()); + QString result = "Mouse DoubleClick: raw button=" + QString::number(j) + " Qt=" + enumNameFromValue(e->button()); + qDebug() << result; + this->setText(result); +} + +void ButtonTester::wheelEvent (QWheelEvent *e) +{ + QString result; + if (e->delta() > 0) { + + if (e->orientation() == Qt::Vertical) { + result = "Mouse Wheel Event: UP"; + } else { + result = "Mouse Wheel Event: LEFT"; + } + } else if (e->delta() < 0) { + if (e->orientation() == Qt::Vertical) { + result = "Mouse Wheel Event: DOWN"; + } else { + result = "Mouse Wheel Event: RIGHT"; + } + } + qDebug() << result; + this->setText(result); +} + +int ButtonTester::buttonByNumber(const Qt::MouseButton button) +{ + if (button == Qt::NoButton) return 0; + if (button == Qt::LeftButton) return 1; + if (button == Qt::RightButton) return 2; + if (button == Qt::MiddleButton) return 3; + +/* Please note that Qt Button #4 corresponds to button #8 on all + * platforms which EMULATE wheel events by creating button events + * (Button #4 = Scroll Up; Button #5 = Scroll Down; Button #6 = Scroll + * Left; and Button #7 = Scroll Right.) This includes X11, with both + * Xlib and XCB. So, the "raw button" for "Qt::BackButton" is + * usually described as "Button #8". + + * If your platform supports "smooth scrolling", then, for the cases of + * Qt::BackButton and higher, this program will show "raw button" with a + * value which is too large. Subtract 4 to get the correct button ID for + * your platform. + */ + + if (button == Qt::BackButton) return 8; + if (button == Qt::ForwardButton) return 9; + if (button == Qt::TaskButton) return 10; + if (button == Qt::ExtraButton4) return 11; + if (button == Qt::ExtraButton5) return 12; + if (button == Qt::ExtraButton6) return 13; + if (button == Qt::ExtraButton7) return 14; + if (button == Qt::ExtraButton8) return 15; + if (button == Qt::ExtraButton9) return 16; + if (button == Qt::ExtraButton10) return 17; + if (button == Qt::ExtraButton11) return 18; + if (button == Qt::ExtraButton12) return 19; + if (button == Qt::ExtraButton13) return 20; + if (button == Qt::ExtraButton14) return 21; + if (button == Qt::ExtraButton15) return 22; + if (button == Qt::ExtraButton16) return 23; + if (button == Qt::ExtraButton17) return 24; + if (button == Qt::ExtraButton18) return 25; + if (button == Qt::ExtraButton19) return 26; + if (button == Qt::ExtraButton20) return 27; + if (button == Qt::ExtraButton21) return 28; + if (button == Qt::ExtraButton22) return 29; + if (button == Qt::ExtraButton23) return 30; + if (button == Qt::ExtraButton24) return 31; + qDebug("QMouseShortcutEntry::addShortcut contained Invalid Qt::MouseButton value"); + return 0; +} + +QString ButtonTester::enumNameFromValue(const Qt::MouseButton button) +{ + if (button == Qt::NoButton) return "NoButton"; + if (button == Qt::LeftButton) return "LeftButton"; + if (button == Qt::RightButton) return "RightButton"; + if (button == Qt::MiddleButton) return "MiddleButton"; + if (button == Qt::BackButton) return "BackButton"; + if (button == Qt::ForwardButton) return "ForwardButton"; + if (button == Qt::TaskButton) return "TaskButton"; + if (button == Qt::ExtraButton4) return "ExtraButton4"; + if (button == Qt::ExtraButton5) return "ExtraButton5"; + if (button == Qt::ExtraButton6) return "ExtraButton6"; + if (button == Qt::ExtraButton7) return "ExtraButton7"; + if (button == Qt::ExtraButton8) return "ExtraButton8"; + if (button == Qt::ExtraButton9) return "ExtraButton9"; + if (button == Qt::ExtraButton10) return "ExtraButton10"; + if (button == Qt::ExtraButton11) return "ExtraButton11"; + if (button == Qt::ExtraButton12) return "ExtraButton12"; + if (button == Qt::ExtraButton13) return "ExtraButton13"; + if (button == Qt::ExtraButton14) return "ExtraButton14"; + if (button == Qt::ExtraButton15) return "ExtraButton15"; + if (button == Qt::ExtraButton16) return "ExtraButton16"; + if (button == Qt::ExtraButton17) return "ExtraButton17"; + if (button == Qt::ExtraButton18) return "ExtraButton18"; + if (button == Qt::ExtraButton19) return "ExtraButton19"; + if (button == Qt::ExtraButton20) return "ExtraButton20"; + if (button == Qt::ExtraButton21) return "ExtraButton21"; + if (button == Qt::ExtraButton22) return "ExtraButton22"; + if (button == Qt::ExtraButton23) return "ExtraButton23"; + if (button == Qt::ExtraButton24) return "ExtraButton24"; + qDebug("QMouseShortcutEntry::addShortcut contained Invalid Qt::MouseButton value"); + return "NoButton"; +} diff --git a/examples/widgets/mousebuttons/buttontester.h b/examples/widgets/mousebuttons/buttontester.h new file mode 100644 index 0000000000..0801a9ae5c --- /dev/null +++ b/examples/widgets/mousebuttons/buttontester.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2012 Rick Stockton <rickstockton@reno-computerhelp.com> +** Contact: http://www.qt-project.org/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef BUTTONTESTER_H +#define BUTTONTESTER_H + +#include <QtWidgets> +#include <QTextEdit> +#include <QString> +#include <QDebug> +#include <QMouseEvent> +#include <QWheelEvent> + +class ButtonTester : public QTextEdit +{ + Q_OBJECT + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + void wheelEvent(QWheelEvent * event); + int buttonByNumber(const Qt::MouseButton button); + QString enumNameFromValue(const Qt::MouseButton button); +}; + +#endif // BUTTONTESTER_H diff --git a/examples/widgets/mousebuttons/main.cpp b/examples/widgets/mousebuttons/main.cpp new file mode 100644 index 0000000000..7c9674a9d9 --- /dev/null +++ b/examples/widgets/mousebuttons/main.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2012 Rick Stockton <rickstockton@reno-computerhelp.com> +** Contact: http://www.qt-project.org/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "buttontester.h" +#include <QtGui> + +int main(int argv, char **args) +{ + QApplication app(argv, args); + + ButtonTester *testArea = new ButtonTester; + testArea->setMinimumSize(500, 350); + testArea->setText("To test your mouse with Qt, press buttons in this area.\nYou may also scroll or tilt your mouse wheel."); + QPushButton *quitButton = new QPushButton("Quit"); + + QObject::connect(quitButton, SIGNAL(clicked()), qApp, SLOT(quit())); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(testArea); + layout->addWidget(quitButton); + + QWidget window; + window.setLayout(layout); + window.setWindowTitle("Mouse Button Tester"); + window.show(); + + return app.exec(); +} diff --git a/examples/widgets/mousebuttons/mousebuttons.pro b/examples/widgets/mousebuttons/mousebuttons.pro new file mode 100644 index 0000000000..823b04657e --- /dev/null +++ b/examples/widgets/mousebuttons/mousebuttons.pro @@ -0,0 +1,18 @@ +TEMPLATE = app + +TARGET = buttontester +TEMPLATE = app + +SOURCES += \ + main.cpp\ + buttontester.cpp \ + +HEADERS += \ + buttontester.h \ + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/qtbase/examples/widgets/mousebuttons +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS buttontester.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/qtbase/examples/widgets/mousebuttons +INSTALLS += target sources +QT += core widgets diff --git a/examples/widgets/widgets.pro b/examples/widgets/widgets.pro index 51a92bcc29..f5c98c3c77 100644 --- a/examples/widgets/widgets.pro +++ b/examples/widgets/widgets.pro @@ -12,6 +12,7 @@ SUBDIRS = analogclock \ imageviewer \ lineedits \ movie \ + mousebuttons \ orientation \ scribble \ shapedclock \ diff --git a/mkspecs/cmake/Qt5BasicConfig.cmake.in b/mkspecs/cmake/Qt5BasicConfig.cmake.in index 45a0722ef5..0334b6f6e2 100644 --- a/mkspecs/cmake/Qt5BasicConfig.cmake.in +++ b/mkspecs/cmake/Qt5BasicConfig.cmake.in @@ -35,6 +35,57 @@ if (Qt5$${CMAKE_MODULE_NAME}_EXECUTABLE_COMPILE_FLAGS) list(REMOVE_DUPLICATES Qt5$${CMAKE_MODULE_NAME}_EXECUTABLE_COMPILE_FLAGS) endif() +!!IF !isEmpty(CMAKE_STATIC_TYPE) +# For static builds, we also list the dependencies of +# Qt so that consumers can build. + +!!IF isEqual(CMAKE_MODULE_NAME, Core) + +set(Qt5Core_LIB_DEPENDENCIES) +!!IF contains(QT_CONFIG, system-zlib) +find_package(ZLIB REQUIRED) +list(APPEND Qt5Core_LIB_DEPENDENCIES ${ZLIB_LIBRARIES}) +!!ENDIF + +!!IF contains(QT_CONFIG, glib) +find_package(GTK2 REQUIRED glib-2.0 gthread-2.0) +list(APPEND Qt5Core_LIB_DEPENDENCIES ${GTK2_LIBRARIES}) +!!ENDIF + +!!IF contains(QT_CONFIG, clock-monotonic) +find_library(QT_RT_LIBRARY NAMES rt) +mark_as_advanced(QT_RT_LIBRARY) +list(APPEND Qt5Core_LIB_DEPENDENCIES ${QT_RT_LIBRARY}) +!!ENDIF + +set(CMAKE_THREAD_PREFER_PTHREADS 1) +find_package(Threads) +if(CMAKE_USE_PTHREADS_INIT) + list(APPEND Qt5Core_LIB_DEPENDENCIES ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) +endif() + +!!ENDIF # Core + +!!IF isEqual(CMAKE_MODULE_NAME, Gui) + +set(Qt5Gui_LIB_DEPENDENCIES) + +!!IF contains(QT_CONFIG, system-png) +find_package(PNG REQUIRED) +list(APPEND Qt5Gui_LIB_DEPENDENCIES ${PNG_LIBRARIES}) +!!ENDIF + +!!IF contains(QT_CONFIG, system-jpeg) +find_package(JPEG REQUIRED) +list(APPEND Qt5Gui_LIB_DEPENDENCIES ${JPEG_LIBRARIES}) +!!ENDIF + +!!ENDIF # Gui + +!!ENDIF # Static + +list(APPEND Qt5$${CMAKE_MODULE_NAME}_LIB_DEPENDENCIES "$${CMAKE_QT5_MODULE_DEPS}") + if (NOT _Qt5$${CMAKE_MODULE_NAME}_target) set(_Qt5$${CMAKE_MODULE_NAME}_target 1) !!IF !isEmpty(CMAKE_STATIC_TYPE) @@ -50,7 +101,7 @@ endif() !!IF !isEmpty(debug_type) set_property(TARGET Qt5::$${CMAKE_MODULE_NAME} APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) set_target_properties(Qt5::$${CMAKE_MODULE_NAME} PROPERTIES - IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG \"$${CMAKE_QT5_MODULE_DEPS}\" + IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG \"${Qt5$${CMAKE_MODULE_NAME}_LIB_DEPENDENCIES}\" !!IF isEmpty(CMAKE_LIB_DIR_IS_ABSOLUTE) IMPORTED_LOCATION_DEBUG \"${_qt5_install_prefix}/$${CMAKE_LIB_DIR}$${CMAKE_LIB_FILE_LOCATION_DEBUG}\" !!ELSE @@ -67,7 +118,7 @@ set_target_properties(Qt5::$${CMAKE_MODULE_NAME} PROPERTIES !!IF !isEmpty(release_type) set_property(TARGET Qt5::$${CMAKE_MODULE_NAME} APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) set_target_properties(Qt5::$${CMAKE_MODULE_NAME} PROPERTIES - IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE \"$${CMAKE_QT5_MODULE_DEPS}\" + IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE \"${Qt5$${CMAKE_MODULE_NAME}_LIB_DEPENDENCIES}\" !!IF isEmpty(CMAKE_LIB_DIR_IS_ABSOLUTE) IMPORTED_LOCATION_RELEASE \"${_qt5_install_prefix}/$${CMAKE_LIB_DIR}$${CMAKE_LIB_FILE_LOCATION_RELEASE}\" !!ELSE diff --git a/mkspecs/common/linux.conf b/mkspecs/common/linux.conf index a809a6b6f9..384c013309 100644 --- a/mkspecs/common/linux.conf +++ b/mkspecs/common/linux.conf @@ -34,6 +34,7 @@ QMAKE_LIBS_OPENGL_ES1 = -lGLES_CM QMAKE_LIBS_OPENGL_ES2 = -lGLESv2 QMAKE_LIBS_OPENVG = -lOpenVG QMAKE_LIBS_THREAD = -lpthread +QMAKE_LIBS_LIBUDEV = -ludev QMAKE_CFLAGS_WAYLAND = QMAKE_INCDIR_WAYLAND = diff --git a/mkspecs/features/qt.prf b/mkspecs/features/qt.prf index 5328e6ccff..21443aa5b5 100644 --- a/mkspecs/features/qt.prf +++ b/mkspecs/features/qt.prf @@ -152,21 +152,22 @@ for(QT_CURRENT_VERIFY, $$list($$QT_PLUGIN_VERIFY)) { !isEmpty(QT_BUILD_TREE):QMAKE_LIBDIR = $$QT_BUILD_TREE/lib $$QMAKE_LIBDIR #as above, prepending prevents us from picking up "stale" libs QMAKE_LIBDIR += $$QMAKE_LIBDIR_QT -# Topological ordering of modules based on their QT.<module>.depends variable -QT = $$sort_depends(QT, "QT.") - -QT_DEPENDS= - +# Figure out from which modules we're wanting to use the private headers unset(using_privates) +NEWQT = for(QTLIB, QT) { - # Figure out if we're wanting to use the private headers of a module - contains(QTLIB, .*-private) { - QTLIB ~= s/-private// - use_private = UsePrivate - } else { - use_private = NoPrivate + QTLIBRAW = $$replace(QTLIB, -private$, ) + !isEqual(QTLIBRAW, $$QTLIB) { + want_var = QT.$${QTLIBRAW}.want_private + $$want_var = UsePrivate + using_privates = true } - + NEWQT += $$QTLIBRAW +} +# Topological resolution of modules based on their QT.<module>.depends variable +QT = $$resolve_depends(NEWQT, "QT.") +# Finally actually add the modules +for(QTLIB, QT) { isEmpty(QT.$${QTLIB}.name) { message("Warning: unknown QT module: $$QTLIB") next() @@ -176,14 +177,9 @@ for(QTLIB, QT) { warning($$TARGET cannot have a QT of $$QTLIB) next() } - qtAddModule($$QTLIB, $$use_private) - QT_DEPENDS += $$eval(QT.$${QTLIB}.depends) - isEqual(use_private, UsePrivate):using_privates = true -} -# add include paths for all .depends, since module/application might need f.ex. template specializations etc. -QT_DEPENDS -= $$QT -for(QTLIB, $$list($$lower($$unique(QT_DEPENDS)))):INCLUDEPATH *= $$INCLUDEPATH $$eval(QT.$${QTLIB}.includes) + qtAddModule($$QTLIB, $$eval(QT.$${QTLIB}.want_private)) +} !isEmpty(using_privates):!no_private_qt_headers_warning:if(!debug_and_release|!build_pass) { message("This project is using private headers and will therefore be tied to this specific Qt module build version.") diff --git a/mkspecs/unsupported/qnx-g++/qmake.conf b/mkspecs/unsupported/qnx-X11-g++/qmake.conf index bc96c21e9f..94f207aff6 100644 --- a/mkspecs/unsupported/qnx-g++/qmake.conf +++ b/mkspecs/unsupported/qnx-X11-g++/qmake.conf @@ -1,5 +1,5 @@ # -# qmake configuration for qnx-g++ +# qmake configuration for qnx-x11-g++ # # Written for QNX RTOS v6 with X11 # diff --git a/mkspecs/unsupported/qnx-g++/qplatformdefs.h b/mkspecs/unsupported/qnx-X11-g++/qplatformdefs.h index 489e1be114..489e1be114 100644 --- a/mkspecs/unsupported/qnx-g++/qplatformdefs.h +++ b/mkspecs/unsupported/qnx-X11-g++/qplatformdefs.h diff --git a/mkspecs/unsupported/blackberry-armv7le-qcc/qmake.conf b/mkspecs/unsupported/qnx-armv7le-qcc/qmake.conf index f2407a6c13..ea206e28ec 100644 --- a/mkspecs/unsupported/blackberry-armv7le-qcc/qmake.conf +++ b/mkspecs/unsupported/qnx-armv7le-qcc/qmake.conf @@ -1,5 +1,5 @@ # -# qmake configuration for blackberry-qcc +# qmake configuration for qnx-qcc armv7 targets # MAKEFILE_GENERATOR = UNIX diff --git a/mkspecs/unsupported/blackberry-armv7le-qcc/qplatformdefs.h b/mkspecs/unsupported/qnx-armv7le-qcc/qplatformdefs.h index 8ef80c4bd7..8ef80c4bd7 100644 --- a/mkspecs/unsupported/blackberry-armv7le-qcc/qplatformdefs.h +++ b/mkspecs/unsupported/qnx-armv7le-qcc/qplatformdefs.h diff --git a/mkspecs/unsupported/blackberry-x86-qcc/qmake.conf b/mkspecs/unsupported/qnx-x86-qcc/qmake.conf index 93f1df2e07..fef8d443c1 100644 --- a/mkspecs/unsupported/blackberry-x86-qcc/qmake.conf +++ b/mkspecs/unsupported/qnx-x86-qcc/qmake.conf @@ -1,5 +1,5 @@ # -# qmake configuration for blackberry-qcc +# qmake configuration for qnx-qcc x86 targets # MAKEFILE_GENERATOR = UNIX diff --git a/mkspecs/unsupported/blackberry-x86-qcc/qplatformdefs.h b/mkspecs/unsupported/qnx-x86-qcc/qplatformdefs.h index 8ef80c4bd7..8ef80c4bd7 100644 --- a/mkspecs/unsupported/blackberry-x86-qcc/qplatformdefs.h +++ b/mkspecs/unsupported/qnx-x86-qcc/qplatformdefs.h diff --git a/qmake/generators/mac/pbuilder_pbx.cpp b/qmake/generators/mac/pbuilder_pbx.cpp index 70efff0bcd..770a1ad34e 100644 --- a/qmake/generators/mac/pbuilder_pbx.cpp +++ b/qmake/generators/mac/pbuilder_pbx.cpp @@ -178,7 +178,6 @@ ProjectBuilderMakefileGenerator::writeSubDirs(QTextStream &t) } if(tmp_proj.first("TEMPLATE") == "subdirs") { QMakeProject *pp = new QMakeProject(&tmp_proj); - pp->read(0); pb_subdirs += new ProjectBuilderSubDirs(pp, dir); } else if(tmp_proj.first("TEMPLATE") == "app" || tmp_proj.first("TEMPLATE") == "lib") { QString pbxproj = qmake_getpwd() + Option::dir_sep + tmp_proj.first("TARGET") + projectSuffix(); diff --git a/qmake/generators/metamakefile.cpp b/qmake/generators/metamakefile.cpp index 39dd4ab797..b855585500 100644 --- a/qmake/generators/metamakefile.cpp +++ b/qmake/generators/metamakefile.cpp @@ -447,6 +447,8 @@ QT_END_INCLUDE_NAMESPACE MakefileGenerator * MetaMakefileGenerator::createMakefileGenerator(QMakeProject *proj, bool noIO) { + Option::postProcessProject(proj); + MakefileGenerator *mkfile = NULL; if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) { mkfile = new ProjectGenerator; @@ -492,6 +494,8 @@ MetaMakefileGenerator::createMakefileGenerator(QMakeProject *proj, bool noIO) MetaMakefileGenerator * MetaMakefileGenerator::createMetaGenerator(QMakeProject *proj, const QString &name, bool op, bool *success) { + Option::postProcessProject(proj); + MetaMakefileGenerator *ret = 0; if ((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || Option::qmake_mode == Option::QMAKE_GENERATE_PRL)) { diff --git a/qmake/generators/win32/mingw_make.cpp b/qmake/generators/win32/mingw_make.cpp index 03460551a9..c088e8e480 100644 --- a/qmake/generators/win32/mingw_make.cpp +++ b/qmake/generators/win32/mingw_make.cpp @@ -404,14 +404,14 @@ void MingwMakefileGenerator::writeObjectsPart(QTextStream &t) QString ar_cmd = project->values("QMAKE_LIB").join(" "); if (ar_cmd.isEmpty()) ar_cmd = "armar --create"; - objectsLinkLine = ar_cmd + " " + var("DEST_TARGET") + " --via " + ar_script_file; + objectsLinkLine = ar_cmd + " " + var("DEST_TARGET") + " --via " + escapeFilePath(ar_script_file); } else { // Strip off any options since the ar commands will be read from file. QString ar_cmd = var("QMAKE_LIB").section(" ", 0, 0);; if (ar_cmd.isEmpty()) ar_cmd = "ar"; createArObjectScriptFile(ar_script_file, var("DEST_TARGET"), project->values("OBJECTS")); - objectsLinkLine = ar_cmd + " -M < " + ar_script_file; + objectsLinkLine = ar_cmd + " -M < " + escapeFilePath(ar_script_file); } } else { QString ld_script_file = var("QMAKE_LINK_OBJECT_SCRIPT") + "." + var("TARGET"); @@ -420,10 +420,10 @@ void MingwMakefileGenerator::writeObjectsPart(QTextStream &t) } if (project->isActiveConfig("rvct_linker")) { createRvctObjectScriptFile(ld_script_file, project->values("OBJECTS")); - objectsLinkLine = QString::fromLatin1("--via ") + ld_script_file; + objectsLinkLine = QString::fromLatin1("--via ") + escapeFilePath(ld_script_file); } else { createLdObjectScriptFile(ld_script_file, project->values("OBJECTS")); - objectsLinkLine = ld_script_file; + objectsLinkLine = escapeFilePath(ld_script_file); } } Win32MakefileGenerator::writeObjectsPart(t); diff --git a/qmake/option.cpp b/qmake/option.cpp index 4e0d5b198e..b2a1e6982f 100644 --- a/qmake/option.cpp +++ b/qmake/option.cpp @@ -149,8 +149,11 @@ static QString detectProjectFile(const QString &path) static QString cleanSpec(const QString &spec) { QString ret = QDir::cleanPath(spec); - if (ret.contains('/')) - ret = QDir::cleanPath(QFileInfo(ret).absoluteFilePath()); + if (ret.contains('/')) { + const QFileInfo specDirInfo(ret); + if (specDirInfo.exists() && specDirInfo.isDir()) + ret = QDir::cleanPath(specDirInfo.absoluteFilePath()); + } return ret; } @@ -591,10 +594,12 @@ QStringList Option::mkspecPaths() for (QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) ret << ((*it) + concat); } - ret << Option::mkfile::project_build_root + concat; + if (!Option::mkfile::project_build_root.isEmpty()) + ret << Option::mkfile::project_build_root + concat; if (!Option::mkfile::project_root.isEmpty()) ret << Option::mkfile::project_root + concat; ret << QLibraryInfo::location(QLibraryInfo::HostDataPath) + concat; + ret.removeDuplicates(); return ret; } diff --git a/qmake/project.cpp b/qmake/project.cpp index f6e26254c5..7655f05f2e 100644 --- a/qmake/project.cpp +++ b/qmake/project.cpp @@ -593,6 +593,7 @@ QStringList qmake_feature_paths(QMakeProperty *prop=0) concat_it != concat.end(); ++concat_it) feature_roots << (QLibraryInfo::location(QLibraryInfo::HostDataPath) + mkspecs_concat + (*concat_it)); + feature_roots.removeDuplicates(); return feature_roots; } @@ -627,10 +628,11 @@ QMakeProject::init(QMakeProperty *p) reset(); } -QMakeProject::QMakeProject(QMakeProject *p, const QHash<QString, QStringList> *vars) +// Duplicate project. It is *not* allowed to call the complex read() functions on the copy. +QMakeProject::QMakeProject(QMakeProject *p, const QHash<QString, QStringList> *_vars) { init(p->properties()); - base_vars = vars ? *vars : p->variables(); + vars = _vars ? *_vars : p->variables(); for(QHash<QString, FunctionBlock*>::iterator it = p->replaceFunctions.begin(); it != p->replaceFunctions.end(); ++it) { it.value()->ref(); replaceFunctions.insert(it.key(), it.value()); @@ -1256,7 +1258,7 @@ QMakeProject::read(const QString &project, uchar cmd) bool QMakeProject::read(uchar cmd) { - if(cfile.isEmpty()) { + if ((cmd & ReadSetup) && base_vars.isEmpty()) { // hack to get the Option stuff in there base_vars["QMAKE_EXT_CPP"] = Option::cpp_ext; base_vars["QMAKE_EXT_C"] = Option::c_ext; @@ -1265,12 +1267,12 @@ QMakeProject::read(uchar cmd) if(!Option::user_template_prefix.isEmpty()) base_vars["TEMPLATE_PREFIX"] = QStringList(Option::user_template_prefix); - if ((cmd & ReadSetup) && Option::mkfile::do_cache) { // parse the cache + if (Option::mkfile::do_cache) { // parse the cache if (Option::output_dir.startsWith(Option::mkfile::project_build_root)) Option::mkfile::cachefile_depth = Option::output_dir.mid(Option::mkfile::project_build_root.length()).count('/'); } - if (cmd & ReadSetup) { // parse mkspec + { // parse mkspec QString qmakespec = Option::mkfile::qmakespec; while(qmakespec.endsWith(QLatin1Char('/'))) qmakespec.truncate(qmakespec.length()-1); @@ -1306,7 +1308,6 @@ QMakeProject::read(uchar cmd) //before commandline if (cmd & ReadSetup) { - cfile = pfile; parser.file = "(internal)"; parser.from_file = false; parser.line_no = 1; //really arg count now.. duh @@ -1385,7 +1386,6 @@ QMakeProject::read(uchar cmd) break; } } - Option::postProcessProject(this); // let Option post-process return true; } @@ -1633,12 +1633,8 @@ QMakeProject::doProjectInclude(QString file, uchar flags, QHash<QString, QString if(flags & (IncludeFlagNewProject|IncludeFlagNewParser)) { // The "project's variables" are used in other places (eg. export()) so it's not // possible to use "place" everywhere. Instead just set variables and grab them later - QMakeProject proj(this, &place); + QMakeProject proj(prop); if(flags & IncludeFlagNewParser) { -#if 1 - if(proj.doProjectInclude("default_pre", IncludeFlagFeature, proj.variables()) == IncludeNoExist) - proj.doProjectInclude("default", IncludeFlagFeature, proj.variables()); -#endif parsed = proj.read(file, proj.variables()); // parse just that file (fromfile, infile) } else { parsed = proj.read(file); // parse all aux files (load/include into) diff --git a/qmake/project.h b/qmake/project.h index 6422ed1f32..979f1480bd 100644 --- a/qmake/project.h +++ b/qmake/project.h @@ -81,7 +81,7 @@ class QMakeProject bool recursive; bool own_prop; bool backslashWarned; - QString pfile, cfile; + QString pfile; QMakeProperty *prop; void reset(); QStringList extra_configs; diff --git a/qtbase.pro b/qtbase.pro index 01a5aa33dc..9290d5adcd 100644 --- a/qtbase.pro +++ b/qtbase.pro @@ -39,9 +39,6 @@ confclean.depends += clean confclean.commands = unix { confclean.commands += (cd config.tests/unix/stl && $(MAKE) distclean); \ - (cd config.tests/unix/endian && $(MAKE) distclean); \ - (cd config.tests/unix/ipv6 && $(MAKE) distclean); \ - (cd config.tests/unix/largefile && $(MAKE) distclean); \ (cd config.tests/unix/ptrsize && $(MAKE) distclean); \ (cd config.tests/x11/notype && $(MAKE) distclean); \ (cd config.tests/unix/getaddrinfo && $(MAKE) distclean); \ @@ -50,7 +47,6 @@ unix { (cd config.tests/unix/mysql && $(MAKE) distclean); \ (cd config.tests/unix/mysql_r && $(MAKE) distclean); \ (cd config.tests/unix/nis && $(MAKE) distclean); \ - (cd config.tests/unix/nix && $(MAKE) distclean); \ (cd config.tests/unix/iodbc && $(MAKE) distclean); \ (cd config.tests/unix/odbc && $(MAKE) distclean); \ (cd config.tests/unix/oci && $(MAKE) distclean); \ 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/animation/qpropertyanimation.cpp b/src/corelib/animation/qpropertyanimation.cpp index fc51a20a15..f7ba49c3cd 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 <private/qmutexpool_p.h> +#include <QtCore/QMutex> #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<QObject *, QByteArray> QPropertyAnimationPair; typedef QHash<QPropertyAnimationPair, QPropertyAnimation*> QPropertyAnimationHash; @@ -280,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())); 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 <QtCore/qrect.h> #include <QtCore/qline.h> #include <QtCore/qmutex.h> -#include <private/qmutexpool_p.h> #ifndef QT_NO_ANIMATION @@ -399,6 +398,7 @@ void QVariantAnimation::setEasingCurve(const QEasingCurve &easing) typedef QVector<QVariantAnimation::Interpolator> 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<typename T> 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) 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/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", "" }, 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__) 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(); } 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; diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 515732bc68..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; @@ -1438,11 +1433,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 +1610,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 +1621,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 +1650,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/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/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 <private/qmutexpool_p.h> #include <private/qthread_p.h> #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); } 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/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<void>, qMetaTypeConstructHelper<void>, 0, TypeFlags()); } /*! \internal @@ -474,7 +474,7 @@ int QMetaType::registerType(const char *typeName, Deleter deleter, int size, TypeFlags flags) { QVector<QCustomTypeInfo> *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); } diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 06ada136a6..8dc45bb2ed 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; @@ -494,6 +494,7 @@ inline int qMetaTypeId( #endif ) { + Q_STATIC_ASSERT_X(QMetaTypeId2<T>::Defined, "Type is not registered, please use Q_DECLARE_METATYPE macro to make it know to Qt's meta-object system"); return QMetaTypeId2<T>::qt_metatype_id(); } 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 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. 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<QFactoryInterface*>(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 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; }; 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(); 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 @@ -308,31 +308,6 @@ bool QThreadPoolPrivate::waitForDone(int msecs) } /*! \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 has completed. 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; 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 <stdint.h>' 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 <qiodevice.h> 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); } 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; } 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/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp new file mode 100644 index 0000000000..0fa7d6459e --- /dev/null +++ b/src/corelib/tools/qregularexpression.cpp @@ -0,0 +1,2134 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. +** 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 <QtCore/qcoreapplication.h> +#include <QtCore/qmutex.h> +#include <QtCore/qvector.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdebug.h> + +#include <pcre.h> + +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 \li Validation + \li A regexp can test whether a substring meets some criteria, + e.g. is an integer or contains no whitespace. + \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 \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 \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 + + 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 + \li \e {Mastering Regular Expressions} (Third Edition) by Jeffrey E. F. + Friedl, ISBN 0-596-52812-4; + \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); + \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 + + \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 + \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 + 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 + \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 + + 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. + + \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 +*/ + +/*! + \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 (\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 + 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. +*/ + +// 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 +*/ +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(); + pcre16_extra *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; + + const QRegularExpression regularExpression; + const QString subject; + // the capturedOffsets vector contains pairs of (start, end) positions + // for each captured substring + QVector<int> capturedOffsets; + + const QRegularExpression::MatchType matchType; + const 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; + const QRegularExpression regularExpression; + const QRegularExpression::MatchType matchType; + const 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 +*/ +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 + + 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 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, + 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. +*/ +pcre16_extra *QRegularExpressionPrivate::optimizePattern() +{ + Q_ASSERT(compiledPattern); + + QMutexLocker lock(&mutex); + + if (studyData || (++usedCount != qt_qregularexpression_optimize_after_use_count)) + return studyData; + + static const bool enableJit = isJitEnabled(); + + int studyOptions = 0; + if (enableJit) + 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); + + return studyData; +} + +/*! + \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<QRegularExpressionPrivate *>(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 pcre16_extra *currentStudyData = const_cast<QRegularExpressionPrivate *>(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, currentStudyData, + subjectUtf16, subjectLength, + offset, pcreOptions, + captureOffsets, captureOffsetsCount); + } else { + result = pcre16_exec(compiledPattern, currentStudyData, + 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, currentStudyData, + 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 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 + 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 (d == re.d) || + (d->pattern == re.d->pattern && d->patternOptions == re.d->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 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 + + 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..3ca83c9e27 --- /dev/null +++ b/src/corelib/tools/qregularexpression.h @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. +** 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 <QtCore/qstring.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qvariant.h> + +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; + + int captureCount() 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<QRegularExpressionPrivate> 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); +Q_CORE_EXPORT QDebug operator<<(QDebug debug, QRegularExpression::PatternOptions patternOptions); +#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<QRegularExpressionMatchPrivate> 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<QRegularExpressionMatchIteratorPrivate> 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/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<int N> 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<int N> struct QConstStringData { 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 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/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. @@ -209,6 +222,18 @@ QT_BEGIN_NAMESPACE */ /*! + \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) \overload @@ -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 <qcursor.h> +#include <qscreen.h> #include <private/qcursor_p.h> #include <qplatformcursor_qpa.h> #include <private/qguiapplication_p.h> @@ -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<QWeakPointer<QPlatformCursor> > cursors = QPlatformCursorPrivate::getInstances(); - int cursorCount = cursors.count(); - for (int i = 0; i < cursorCount; ++i) { - const QWeakPointer<QPlatformCursor> &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/qevent.cpp b/src/gui/kernel/qevent.cpp index f1bf07635c..7b84f4a982 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<QPointF> QTouchEvent::TouchPoint::rawScreenPositions() const diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index be82005a54..42ce3745de 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()); @@ -933,6 +938,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 +946,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,33 +961,26 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo } buttons = e->buttons; if (button & e->buttons) { - if ((e->timestamp - mousePressTime) < static_cast<ulong>(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<ulong>(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); #ifndef QT_NO_CURSOR - QList<QWeakPointer<QPlatformCursor> > 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)) { @@ -1017,12 +1015,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 +1043,6 @@ void QGuiApplicationPrivate::processWheelEvent(QWindowSystemInterfacePrivate::Wh } } - - // Remember, Qt convention is: keyboard state is state *before* void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyEvent *e) @@ -1813,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<QWeakPointer<QPlatformCursor> > cursors = QPlatformCursorPrivate::getInstances(); - int cursorCount = cursors.count(); - for (int i = 0; i < cursorCount; ++i) { - const QWeakPointer<QPlatformCursor> &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<QWindow *> &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 <QPainter> #include <QBitmap> #include <QGuiApplication> +#include <QScreen> +#include <QPlatformScreen> #include <private/qguiapplication_p.h> #include <QDebug> QT_BEGIN_NAMESPACE -QList <QWeakPointer<QPlatformCursor> > QPlatformCursorPrivate::instances; +QList<QPlatformCursor *> QPlatformCursorPrivate::getInstances() +{ + QList<QPlatformCursor *> 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 <QWeakPointer<QPlatformCursor> > 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<QWeakPointer<QPlatformCursor> > getInstances() { return instances; } - static QList<QWeakPointer<QPlatformCursor> > instances; + static QList<QPlatformCursor *> 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 <QtGui/qguiapplication.h> +#include <QtGui/qplatformcursor_qpa.h> #include <QtGui/private/qguiapplication_p.h> #include <QtGui/private/qplatformscreen_qpa_p.h> #include <QtGui/qplatformintegration_qpa.h> @@ -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<QPlatformScreenPrivate> d_ptr; 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 3610a3c1c8..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 { @@ -86,9 +87,47 @@ public: enum Palette { SystemPalette, ToolTipPalette, + ToolButtonPalette, + ButtonPalette, + HeaderPalette, + ComboBoxPalette, + ItemViewPalette, + MessageBoxLabelPelette, + TabBarPalette, + LabelPalette, + GroupBoxPalette, + MenuPalette, + MenuBarPalette, + TextEditPalette, + TextLineEditPalette, 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, @@ -109,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/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() */ 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<QFontEngineMulti *>(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<QFontEngineMulti *>(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/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 @@ -375,18 +375,6 @@ QFont QPlatformFontDatabase::defaultFont() const } /*! - Returns fonts for class names. - - \sa QGuiApplication::font() - \since 5.0 -*/ - -QHash<QByteArray, QFont> QPlatformFontDatabase::defaultFonts() const -{ - return QHash<QByteArray, QFont>(); -} - -/*! Resolve alias to actual font family names. \since 5.0 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<QByteArray, QFont> defaultFonts() const; + virtual QString resolveFontFamilyAlias(const QString &family) const; //callback 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; 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<QSslSocket*>(channels[i].socket)); - else + if (encrypt) + QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(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<QSslSocket*>(channels[i].socket)); - else + if (encrypt) + QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(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<HttpMessagePair> &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<QSslSocket *>(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<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors(); - d->channels[i].ignoreAllSslErrors = true; + d->channels[i].ignoreSslErrors(); } } else { - static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors(); - d->channels[channel].ignoreAllSslErrors = true; + d->channels[channel].ignoreSslErrors(); } } @@ -1219,13 +1239,11 @@ void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int if (channel == -1) { // ignore for all channels for (int i = 0; i < d->channelCount; ++i) { - static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors(errors); - d->channels[i].ignoreSslErrorsList = errors; + d->channels[i].ignoreSslErrors(errors); } } else { - static_cast<QSslSocket *>(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<QSslSocket *>(socket)->ignoreSslErrors(); + + ignoreAllSslErrors = true; +} + + +void QHttpNetworkConnectionChannel::ignoreSslErrors(const QList<QSslError> &errors) +{ + if (socket) + static_cast<QSslSocket *>(socket)->ignoreSslErrors(errors); + + ignoreSslErrorsList = errors; +} + +void QHttpNetworkConnectionChannel::setSslConfiguration(const QSslConfiguration &config) +{ + if (socket) + static_cast<QSslSocket *>(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 <QtNetwork/qsslsocket.h> # include <QtNetwork/qsslerror.h> +# include <QtNetwork/qsslconfiguration.h> #else # include <QtNetwork/qtcpsocket.h> #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<QSslError> ignoreSslErrorsList; + QSslConfiguration sslConfiguration; + void ignoreSslErrors(); + void ignoreSslErrors(const QList<QSslError> &errors); + void setSslConfiguration(const QSslConfiguration &config); #endif #ifndef QT_NO_BEARERMANAGEMENT QSharedPointer<QNetworkSession> networkSession; @@ -144,6 +150,11 @@ public: void setConnection(QHttpNetworkConnection *c); QPointer<QHttpNetworkConnection> connection; +#ifndef QT_NO_NETWORKPROXY + QNetworkProxy proxy; + void setProxy(const QNetworkProxy &networkProxy); +#endif + void init(); void close(); 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 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; } 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 <QtCore/qfileinfo.h> #include <QtCore/qmap.h> #include <QtCore/qmutex.h> +#include <QtCore/private/qmutexpool_p.h> #include <QtCore/qstring.h> #include <QtCore/qstringlist.h> #include <QtCore/qvarlengtharray.h> @@ -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<QByteArray> QSslCertificate::subjectInfoAttributes() const { + QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); // lazy init if (d->subjectInfo.isEmpty() && d->x509) d->subjectInfo = @@ -417,6 +425,7 @@ QList<QByteArray> QSslCertificate::subjectInfoAttributes() const */ QList<QByteArray> QSslCertificate::issuerInfoAttributes() const { + QMutexLocker lock(QMutexPool::globalInstanceGet(d.data())); // lazy init if (d->issuerInfo.isEmpty() && d->x509) d->issuerInfo = 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; 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); 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 <QtGui/QPalette> +#include <QtGui/QFont> #include <QtGui/QGuiApplication> #include <QtCore/QDir> #include <QtCore/QFileInfo> @@ -54,6 +55,20 @@ QT_BEGIN_NAMESPACE +ResourceHelper::ResourceHelper() +{ + qFill(palettes, palettes + QPlatformTheme::NPalettes, static_cast<QPalette *>(0)); + qFill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(0)); +} + +void ResourceHelper::clear() +{ + qDeleteAll(palettes, palettes + QPlatformTheme::NPalettes); + qDeleteAll(fonts, fonts + QPlatformTheme::NFonts); + qFill(palettes, palettes + QPlatformTheme::NPalettes, static_cast<QPalette *>(0)); + qFill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(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<QPalette *>(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<QPalette *>(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/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 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) 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/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<Qt::CursorShape, NSCursor *>::const_iterator i = m_cursors.constBegin(); 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 <Cocoa/Cocoa.h> +#include <QtGui> +#include <qplatformdrag_qpa.h> +#include <QtPlatformSupport/private/qsimpledrag_p.h> + +#include <QtGui/private/qdnd_p.h> + +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<CFStringRef>(const_cast<const NSString *>([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..bf54915365 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 <QtCore/QScopedPointer> #include <QtGui/QPlatformIntegration> @@ -62,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; @@ -88,6 +90,7 @@ public: QPlatformNativeInterface *nativeInterface() const; QPlatformAccessibility *accessibility() const; + QPlatformDrag *drag() const; QPlatformTheme *platformTheme() const; private: @@ -98,6 +101,7 @@ private: QScopedPointer<QPlatformAccessibility> mAccessibility; QScopedPointer<QPlatformTheme> mPlatformTheme; QList<QCocoaScreen *> mScreens; + QScopedPointer<QCocoaDrag> mCocoaDrag; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 626a7fe0f9..8411a795c1 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 <QtGui/qplatformaccessibility_qpa.h> #include <QtCore/qcoreapplication.h> @@ -78,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() @@ -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/qcocoasystemsettings.h b/src/plugins/platforms/cocoa/qcocoasystemsettings.h index 84a66d7193..10cac27dcd 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.h +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.h @@ -42,12 +42,15 @@ #ifndef QCOCOASYSTEMSETTINGS_H #define QCOCOASYSTEMSETTINGS_H -#include <QtCore/qglobal.h> +#include <QtCore/qhash.h> #include <QtGui/qpalette.h> +#include <QtGui/qplatformtheme_qpa.h> QT_BEGIN_NAMESPACE QPalette * qt_mac_createSystemPalette(); +QHash<QPlatformTheme::Palette, QPalette*> qt_mac_createRolePalettes(); +QHash<QPlatformTheme::Font, QFont *> qt_mac_createRoleFonts(); QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm index 5170c0bc8a..c2f5df4d38 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm @@ -41,8 +41,12 @@ #include "qcocoasystemsettings.h" -#include <Carbon/Carbon.h> #include <QtCore/private/qcore_mac_p.h> +#include <QtGui/qfont.h> + +#include <Carbon/Carbon.h> + +QT_BEGIN_NAMESPACE QColor qt_mac_colorFromCGColor(CGColorRef cgcolor) { @@ -143,3 +147,126 @@ 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<QPlatformTheme::Palette, QPalette*> qt_mac_createRolePalettes() +{ + QHash<QPlatformTheme::Palette, QPalette*> 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; +} + +QFont *qt_mac_qfontForThemeFont(ThemeFontID themeID) +{ + CTFontUIFontType ctID = HIThemeGetUIFontType(themeID); + QCFType<CTFontRef> ctfont = CTFontCreateUIFontForLanguage(ctID, 0, 0); + QString familyName = QCFString(CTFontCopyFamilyName(ctfont)); + QCFType<CFDictionaryRef> dict = CTFontCopyTraits(ctfont); + CFNumberRef num = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kCTFontWeightTrait)); + float fW; + CFNumberGetValue(num, kCFNumberFloat32Type, &fW); + QFont::Weight wght = fW > 0. ? QFont::Bold : QFont::Normal; + num = static_cast<CFNumberRef>(CFDictionaryGetValue(dict, kCTFontSlantTrait)); + CFNumberGetValue(num, kCFNumberFloatType, &fW); + bool italic = (fW != 0.0); + return new QFont(familyName, CTFontGetSize(ctfont), wght, italic); +} + +QHash<QPlatformTheme::Font, QFont *> qt_mac_createRoleFonts() +{ + QHash<QPlatformTheme::Font, QFont *> 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 fa235b6be0..030db1822c 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 <Cocoa/Cocoa.h> - +#include <QtCore/QHash> #include <QtGui/QPlatformTheme> QT_BEGIN_NAMESPACE @@ -62,10 +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<QPlatformTheme::Palette, QPalette*> m_palettes; + mutable QHash<QPlatformTheme::Font, QFont*> m_fonts; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index 6b0e04acf8..8ec6e3801e 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -109,12 +109,23 @@ 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; } +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/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]; } } 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 <QtGui> +#include "qmacmime.h" + +#undef slots + +#import <Cocoa/Cocoa.h> + +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<Promise> promises; + + PasteboardRef paste; + uchar mime_type; + mutable QPointer<QMimeData> 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 <stdlib.h> +#include <string.h> +#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<CFDataRef> 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<QByteArray> md = promise.convertor->convertFromMime(promise.mime, promise.data, flavorAsQString); + if (md.size() <= promise.offset) + return cantGetFlavorErr; + const QByteArray &ba = md[promise.offset]; + QCFType<CFDataRef> 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<CFArrayRef> 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<QMacPasteboardMime*> 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<QMacPasteboardMime *>::Iterator it = availableConverters.begin(); it != availableConverters.end(); ++it) { + QMacPasteboardMime *c = (*it); + QString flavor(c->flavorFor(mimeType)); + if (!flavor.isEmpty()) { + QVariant mimeData = static_cast<QMacMimeData*>(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<CFArrayRef> 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<CFArrayRef> 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<QMacPasteboardMime *> 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<QByteArray> retList; + for (uint index = 1; index <= cnt; ++index) { + PasteboardItemID id; + if (PasteboardGetItemIdentifier(paste, index, &id) != noErr) + continue; + + QCFType<CFArrayRef> 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<CFStringRef>(CFArrayGetValueAtIndex(types, i)); + if (c_flavor == QCFString::toQString(flavor)) { + QCFType<CFDataRef> 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<QMacPasteboard *>(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<NSString *>(reinterpret_cast<const NSString *>(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 <QtCore> + +#include <CoreFoundation/CoreFoundation.h> + +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<QMacPasteboardMime*> 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<QByteArray> data, QString flav) = 0; + virtual QList<QByteArray> 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 <Cocoa/Cocoa.h> + +QT_BEGIN_NAMESPACE + +extern CGImageRef qt_mac_createCGImageFromQImage(const QImage &img, const QImage **imagePtr = 0); // qpaintengine_mac.cpp + +typedef QList<QMacPasteboardMime*> 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<QByteArray> data, QString flav); + QList<QByteArray> 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<QByteArray> 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<QByteArray> QMacPasteboardMimeAny::convertFromMime(const QString &mime, QVariant data, QString) +{ + QList<QByteArray> 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<QByteArray> data, QString flav); + QList<QByteArray> 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<QByteArray>, QString) +{ + QVariant ret; + return ret; +} + +QList<QByteArray> QMacPasteboardMimeTypeName::convertFromMime(const QString &, QVariant, QString) +{ + QList<QByteArray> 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<QByteArray> data, QString flav); + QList<QByteArray> 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<QByteArray> 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<const UInt8 *>(firstData.constData()), + firstData.size(), CFStringGetSystemEncoding(), false)); + ret = QString(str); + } else { + qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); + } + return ret; +} + +QList<QByteArray> QMacPasteboardMimePlainText::convertFromMime(const QString &, QVariant data, QString flavor) +{ + QList<QByteArray> 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<QByteArray> data, QString flav); + QList<QByteArray> 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<QByteArray> 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<const UInt8 *>(firstData.constData()), + firstData.size(), CFStringGetSystemEncoding(), false)); + ret = QString(str); + } else if (flavor == QLatin1String("public.utf16-plain-text")) { + ret = QString(reinterpret_cast<const QChar *>(firstData.constData()), + firstData.size() / sizeof(QChar)); + } else { + qWarning("QMime::convertToMime: unhandled mimetype: %s", qPrintable(mimetype)); + } + return ret; +} + +QList<QByteArray> QMacPasteboardMimeUnicodeText::convertFromMime(const QString &, QVariant data, QString flavor) +{ + QList<QByteArray> 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<QByteArray> data, QString flav); + QList<QByteArray> 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<QByteArray> data, QString flavor) +{ + if (!canConvert(mimeType, flavor)) + return QVariant(); + if (data.count() > 1) + qWarning("QMacPasteboardMimeHTMLText: Cannot handle multiple member data"); + return data.first(); +} + +QList<QByteArray> QMacPasteboardMimeHTMLText::convertFromMime(const QString &mime, QVariant data, QString flavor) +{ + QList<QByteArray> 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<QByteArray> data, QString flav); + QList<QByteArray> 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<QByteArray> 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<CGImageRef> image; + QCFType<CFDataRef> tiffData = CFDataCreateWithBytesNoCopy(0, + reinterpret_cast<const UInt8 *>(a.constData()), + a.size(), kCFAllocatorNull); + QCFType<CGImageSourceRef> 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<QByteArray> QMacPasteboardMimeTiff::convertFromMime(const QString &mime, QVariant variant, QString flav) +{ + QList<QByteArray> ret; + if (!canConvert(mime, flav)) + return ret; + + QImage img = qvariant_cast<QImage>(variant); + QCFType<CGImageRef> cgimage = qt_mac_image_to_cgimage(img); + + QCFType<CFMutableDataRef> data = CFDataCreateMutable(0, 0); + QCFType<CGImageDestinationRef> imageDestination = CGImageDestinationCreateWithData(data, kUTTypeTIFF, 1, 0); + if (imageDestination != 0) { + CFTypeRef keys[2]; + QCFType<CFTypeRef> values[2]; + QCFType<CFDictionaryRef> 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<const void **>(keys), + reinterpret_cast<const void **>(values), 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CGImageDestinationAddImage(imageDestination, cgimage, options); + CGImageDestinationFinalize(imageDestination); + } + QByteArray ar(CFDataGetLength(data), 0); + CFDataGetBytes(data, + CFRangeMake(0, ar.size()), + reinterpret_cast<UInt8 *>(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<QByteArray> data, QString flav); + QList<QByteArray> 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<QByteArray> data, QString flav) +{ + if (!canConvert(mime, flav)) + return QVariant(); + QList<QVariant> 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<QByteArray> QMacPasteboardMimeFileUri::convertFromMime(const QString &mime, QVariant data, QString flav) +{ + QList<QByteArray> ret; + if (!canConvert(mime, flav)) + return ret; + QList<QVariant> 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<QByteArray> data, QString flav); + QList<QByteArray> 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<QByteArray> data, QString flav) +{ + if (!canConvert(mime, flav)) + return QVariant(); + + QList<QVariant> 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<QByteArray> QMacPasteboardMimeUrl::convertFromMime(const QString &mime, QVariant data, QString flav) +{ + QList<QByteArray> ret; + if (!canConvert(mime, flav)) + return ret; + + QList<QVariant> 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 QMacPasteboardMimeVCard : public QMacPasteboardMime +{ +public: + QMacPasteboardMimeVCard() : 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<QByteArray> data, QString flav); + QList<QByteArray> 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<QByteArray> data, QString) +{ + QByteArray cards; + if (mime == QLatin1String("text/plain")) { + for (int i=0; i<data.size(); ++i) + cards += data[i]; + } + return QVariant(cards); +} + +QList<QByteArray> QMacPasteboardMimeVCard::convertFromMime(const QString &mime, QVariant data, QString) +{ + QList<QByteArray> 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*> 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<QByteArray> 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<QByteArray> 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 <NSDraggingInfo>)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 <QtGui/QWindowSystemInterface> #include <QtCore/QDebug> @@ -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 <NSDraggingInfo>)sender +{ + return [self handleDrag : sender]; +} + +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender +{ + return [self handleDrag : sender]; +} + +// Sends drag update to Qt, return the action +- (NSDragOperation)handleDrag:(id <NSDraggingInfo>)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 <NSDraggingInfo>)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 <NSDraggingInfo>)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<Promise> promises; - - OSPasteboardRef paste; - uchar mime_type; - mutable QPointer<QMimeData> 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 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<QPlatformCursorImage> 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/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/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<QObject*, int> 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/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/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<QByteArray, QFont> QWindowsFontDatabase::defaultFonts() const -{ - QHash<QByteArray, QFont> 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<QByteArray, QFont>::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<QByteArray, QFont> 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<QByteArray, QFont> QWindowsFontDatabaseFT::defaultFonts() const -{ - QHash<QByteArray, QFont> 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<QByteArray, QFont>::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<QByteArray, QFont> 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/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<QWindowsCursor> sharedCursor() +{ + if (const QScreen *primaryScreen = QGuiApplication::primaryScreen()) + return static_cast<const QWindowsScreen *>(primaryScreen->handle())->windowsCursor(); + return QSharedPointer<QWindowsCursor>(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 <QtCore/QList> #include <QtCore/QPair> +#include <QtCore/QSharedPointer> #include <QtGui/QPlatformScreen> QT_BEGIN_NAMESPACE @@ -74,6 +75,8 @@ struct QWindowsScreenData class QWindowsScreen : public QPlatformScreen { public: + typedef QSharedPointer<QWindowsCursor> 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/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 <QtCore/QVariant> #include <QtCore/QCoreApplication> @@ -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<QFont *>(0)); qFill(m_palettes, m_palettes + NPalettes, static_cast<QPalette *>(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<QPalette *>(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<QPalette *>(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<QFont *>(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/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index b2ebe06a58..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) { @@ -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) 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/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()); 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<QXlibScreen *>(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<int, Cursor> 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/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/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<QPrinterInfo> 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); 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<QPlatformPrinterSupportPlugin *>(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<QPlatformPrinterSupportPlugin *>(loader()->instance(k.first())); + if (!plugin) + return 0; + singleton = plugin->create(k.first()); + } + return singleton; } QT_END_NAMESPACE 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 <private/qprintengine_win_p.h> -#elif defined (Q_WS_MAC) -#include <private/qprintengine_mac_p.h> #elif defined (QTOPIA_PRINTENGINE) #include <private/qprintengine_qws_p.h> #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: { 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 } 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<const QChar *>( 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); } diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 571c28f515..d39df1d710 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<int, QSqlTableModelPrivate::ModifiedRow>::Iterator it = cache.find(row); @@ -201,16 +199,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); } /*! @@ -382,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(); @@ -403,6 +392,51 @@ 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. + + 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 */ QVariant QSqlTableModel::data(const QModelIndex &index, int role) const @@ -411,29 +445,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); } @@ -499,18 +513,21 @@ 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()]; if (row.op() == QSqlTableModelPrivate::None) row = QSqlTableModelPrivate::ModifiedRow(QSqlTableModelPrivate::Update, - d->rec); + record(index.row())); row.setValue(index.column(), value); emit dataChanged(index, index); @@ -648,8 +665,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 @@ -662,6 +679,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()) @@ -669,25 +688,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(true); + + 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; } /*! @@ -704,8 +733,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() */ @@ -788,8 +817,10 @@ void QSqlTableModel::revertAll() { Q_D(QSqlTableModel); - while (!d->cache.isEmpty()) - revertRow(d->cache.constBegin().key()); + const QList<int> rows(d->cache.keys()); + for (int i = rows.size() - 1; i >= 0; --i) { + revertRow(rows.value(i)); + } } /*! @@ -996,15 +1027,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); + 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); } @@ -1187,6 +1220,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; } @@ -1215,8 +1250,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(); @@ -1235,7 +1273,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.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 0ae6b53742..323964afe8 100644 --- a/src/sql/models/qsqltablemodel_p.h +++ b/src/sql/models/qsqltablemodel_p.h @@ -101,25 +101,88 @@ public: { public: inline ModifiedRow(Op o = None, const QSqlRecord &r = QSqlRecord()) - : m_op(o), m_rec(r), m_submitted(false) + : 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) { - for (int i = m_rec.count() - 1; i >= 0; --i) - m_rec.setGenerated(i, false); + 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 Op op() const { return m_op; } 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(bool b) { m_submitted = b; } + 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 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() + { + 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) + 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: + inline static void setGenerated(QSqlRecord& r, bool g) + { + 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<int, ModifiedRow> CacheMap; 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 @@ -192,18 +192,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(); QTEST_ASSERT(widget); diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 095b58eeaf..90b64db579 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()); } @@ -428,11 +431,6 @@ PaletteHash *qt_app_palettes_hash() return app_palettes(); } -FontHash::FontHash() -{ - QHash<QByteArray, QFont>::operator=(QGuiApplicationPrivate::platformIntegration()->fontDatabase()->defaultFonts()); -} - Q_GLOBAL_STATIC(FontHash, app_fonts) FontHash *qt_app_fonts_hash() { @@ -3124,7 +3122,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/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<QByteArray, QFont> -{ - FontHash(); -}; +typedef QHash<QByteArray, QFont> FontHash; FontHash *qt_app_fonts_hash(); typedef QHash<QByteArray, QPalette> 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 54c5c39396..97fc794252 100644 --- a/src/widgets/kernel/qapplication_qpa.cpp +++ b/src/widgets/kernel/qapplication_qpa.cpp @@ -289,8 +289,85 @@ 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"); +} + +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 @@ -391,6 +468,7 @@ QPlatformNativeInterface *QApplication::platformNativeInterface() void qt_init(QApplicationPrivate *priv, int type) { + Q_UNUSED(priv); Q_UNUSED(type); qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); @@ -398,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/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 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<QWeakPointer<QPlatformCursor> > cursors = QPlatformCursorPrivate::getInstances(); - int cursorCount = cursors.count(); - for (int i = 0; i < cursorCount; ++i) { - const QWeakPointer<QPlatformCursor> &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) 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<QInputMethodEvent*>(e)); - case QEvent::AccessibilityPrepare: - return new QEvent(*e); case QEvent::LocaleChange: return new QEvent(*e); case QEvent::LanguageChange: 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<QLayoutStruct> *_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<QLayoutStruct> &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<QWidget*> 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; 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()) 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); diff --git a/tests/auto/corelib/animation/qpropertyanimation/tst_qpropertyanimation.cpp b/tests/auto/corelib/animation/qpropertyanimation/tst_qpropertyanimation.cpp index 534dec8160..05d1569988 100644 --- a/tests/auto/corelib/animation/qpropertyanimation/tst_qpropertyanimation.cpp +++ b/tests/auto/corelib/animation/qpropertyanimation/tst_qpropertyanimation.cpp @@ -123,6 +123,7 @@ private slots: void deletedInUpdateCurrentTime(); void totalDuration(); void zeroLoopCount(); + void recursiveAnimations(); }; void tst_QPropertyAnimation::initTestCase() @@ -1237,5 +1238,49 @@ void tst_QPropertyAnimation::zeroLoopCount() QCOMPARE(finishedSpy.count(), 0); } + +class RecursiveObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal x READ x WRITE setX) + Q_PROPERTY(qreal y READ y WRITE setY) +public: + RecursiveObject() : m_x(0), m_y(0) { + animation.setTargetObject(this); + animation.setPropertyName("y"); + animation.setDuration(30); + } + qreal x() const { return m_x; } + void setX(qreal x) { + m_x = x; + animation.setEndValue(x); + animation.start(); + } + qreal y() const { return m_y; } + void setY(qreal y) { m_y = y; } + + qreal m_x; + qreal m_y; + QPropertyAnimation animation; +}; + + +void tst_QPropertyAnimation::recursiveAnimations() +{ + RecursiveObject o; + QPropertyAnimation anim; + anim.setTargetObject(&o); + anim.setPropertyName("x"); + anim.setDuration(30); + + anim.setEndValue(4000); + anim.start(); + QTest::qWait(anim.duration() + o.animation.duration()); + QTRY_COMPARE(anim.state(), QAbstractAnimation::Stopped); + QTRY_COMPARE(o.animation.state(), QAbstractAnimation::Stopped); + QCOMPARE(o.y(), qreal(4000)); +} + + QTEST_MAIN(tst_QPropertyAnimation) #include "tst_qpropertyanimation.moc" diff --git a/tests/auto/corelib/io/qsettings/qsettings.pro b/tests/auto/corelib/io/qsettings/qsettings.pro index 9de476fe13..1772f4b65f 100644 --- a/tests/auto/corelib/io/qsettings/qsettings.pro +++ b/tests/auto/corelib/io/qsettings/qsettings.pro @@ -5,5 +5,3 @@ SOURCES = tst_qsettings.cpp RESOURCES += qsettings.qrc win32-msvc*:LIBS += advapi32.lib - -win32: CONFIG += insignificant_test # QTBUG-24145 diff --git a/tests/auto/corelib/json/test.bjson b/tests/auto/corelib/json/test.bjson Binary files differindex aa412eec67..9a0515f3ef 100644 --- a/tests/auto/corelib/json/test.bjson +++ b/tests/auto/corelib/json/test.bjson diff --git a/tests/auto/corelib/json/tst_qtjson.cpp b/tests/auto/corelib/json/tst_qtjson.cpp index f5b4f17732..f35831c900 100644 --- a/tests/auto/corelib/json/tst_qtjson.cpp +++ b/tests/auto/corelib/json/tst_qtjson.cpp @@ -115,6 +115,8 @@ private Q_SLOTS: void testCompaction(); void testDebugStream(); void testCompactionError(); + + void parseUnicodeEscapes(); private: QString testDataDir; }; @@ -1758,5 +1760,19 @@ void TestQtJson::testCompactionError() } } +void TestQtJson::parseUnicodeEscapes() +{ + const QByteArray json = "[ \"A\\u00e4\\u00C4\" ]"; + + QJsonDocument doc = QJsonDocument::fromJson(json); + QJsonArray array = doc.array(); + + QString result = QLatin1String("A"); + result += QChar(0xe4); + result += QChar(0xc4); + + QCOMPARE(array.first().toString(), result); +} + QTEST_MAIN(TestQtJson) #include "tst_qtjson.moc" diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp index 63adcadb86..9076f37c7c 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +++ b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp @@ -207,6 +207,7 @@ void tst_QMimeDatabase::mimeTypeForFileName_data() QTest::newRow("directory") << "/" << "inode/directory"; QTest::newRow("doesn't exist, no extension") << "IDontExist" << "application/octet-stream"; QTest::newRow("doesn't exist but has known extension") << "IDontExist.txt" << "text/plain"; + QTest::newRow("empty") << "" << "application/octet-stream"; } static inline QByteArray msgMimeTypeForFileNameFailed(const QList<QMimeType> &actual, diff --git a/tests/auto/corelib/tools/qregularexpression/.gitignore b/tests/auto/corelib/tools/qregularexpression/.gitignore new file mode 100644 index 0000000000..c9249e090e --- /dev/null +++ b/tests/auto/corelib/tools/qregularexpression/.gitignore @@ -0,0 +1,2 @@ +tst_qregularexpression_alwaysoptimize +tst_qregularexpression_defaultoptimize diff --git a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/alwaysoptimize.pro b/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/alwaysoptimize.pro new file mode 100644 index 0000000000..f48b1ee96a --- /dev/null +++ b/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/alwaysoptimize.pro @@ -0,0 +1,7 @@ +CONFIG += testcase parallel_test +TARGET = tst_qregularexpression_alwaysoptimize +QT = core testlib +HEADERS = ../tst_qregularexpression.h +SOURCES = \ + tst_qregularexpression_alwaysoptimize.cpp \ + ../tst_qregularexpression.cpp diff --git a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/tst_qregularexpression_alwaysoptimize.cpp b/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/tst_qregularexpression_alwaysoptimize.cpp new file mode 100644 index 0000000000..9190f183a1 --- /dev/null +++ b/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/tst_qregularexpression_alwaysoptimize.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite 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 <QtTest/QtTest> +#include "../tst_qregularexpression.h" + +class tst_QRegularExpression_AlwaysOptimize : public tst_QRegularExpression +{ + Q_OBJECT + +private slots: + void initTestCase(); +}; + +QT_BEGIN_NAMESPACE +extern Q_CORE_EXPORT unsigned int qt_qregularexpression_optimize_after_use_count; // from qregularexpression.cpp +QT_END_NAMESPACE + +void tst_QRegularExpression_AlwaysOptimize::initTestCase() +{ + qt_qregularexpression_optimize_after_use_count = 1; +} + +QTEST_APPLESS_MAIN(tst_QRegularExpression_AlwaysOptimize) + +#include "tst_qregularexpression_alwaysoptimize.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/defaultoptimize.pro b/tests/auto/corelib/tools/qregularexpression/defaultoptimize/defaultoptimize.pro new file mode 100644 index 0000000000..dd1a90cfbc --- /dev/null +++ b/tests/auto/corelib/tools/qregularexpression/defaultoptimize/defaultoptimize.pro @@ -0,0 +1,7 @@ +CONFIG += testcase parallel_test +TARGET = tst_qregularexpression_defaultoptimize +QT = core testlib +HEADERS = ../tst_qregularexpression.h +SOURCES = \ + tst_qregularexpression_defaultoptimize.cpp \ + ../tst_qregularexpression.cpp diff --git a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/tst_qregularexpression_defaultoptimize.cpp b/tests/auto/corelib/tools/qregularexpression/defaultoptimize/tst_qregularexpression_defaultoptimize.cpp new file mode 100644 index 0000000000..d0b5bee4b7 --- /dev/null +++ b/tests/auto/corelib/tools/qregularexpression/defaultoptimize/tst_qregularexpression_defaultoptimize.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite 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 <QtTest/QtTest> +#include "../tst_qregularexpression.h" + +class tst_QRegularExpression_DefaultOptimize : public tst_QRegularExpression +{ + Q_OBJECT +}; + +QTEST_APPLESS_MAIN(tst_QRegularExpression_DefaultOptimize) + +#include "tst_qregularexpression_defaultoptimize.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro b/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro new file mode 100644 index 0000000000..0cae10112f --- /dev/null +++ b/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = defaultoptimize +contains(QT_CONFIG,private_tests):SUBDIRS += alwaysoptimize diff --git a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp new file mode 100644 index 0000000000..72157c0536 --- /dev/null +++ b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp @@ -0,0 +1,1198 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite 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 <QtTest/QtTest> +#include <qstring.h> +#include <qlist.h> +#include <qstringlist.h> +#include <qhash.h> + +#include "tst_qregularexpression.h" + +struct Match +{ + Match() + { + clear(); + } + + void clear() + { + isValid = false; + hasMatch = false; + hasPartialMatch = false; + captured.clear(); + namedCaptured.clear(); + } + + bool isValid; + bool hasMatch; + bool hasPartialMatch; + QStringList captured; + QHash<QString, QString> namedCaptured; +}; + +Q_DECLARE_METATYPE(Match) +Q_DECLARE_METATYPE(QList<Match>) + +bool operator==(const QRegularExpressionMatch &rem, const Match &m) +{ + if (rem.isValid() != m.isValid) + return false; + if (!rem.isValid()) + return true; + if ((rem.hasMatch() != m.hasMatch) || (rem.hasPartialMatch() != m.hasPartialMatch)) + return false; + if (rem.hasMatch() || rem.hasPartialMatch()) { + if (rem.lastCapturedIndex() != (m.captured.size() - 1)) + return false; + for (int i = 0; i <= rem.lastCapturedIndex(); ++i) { + QString remCaptured = rem.captured(i); + QString mCaptured = m.captured.at(i); + if (remCaptured != mCaptured + || remCaptured.isNull() != mCaptured.isNull() + || remCaptured.isEmpty() != mCaptured.isEmpty()) { + return false; + } + } + + Q_FOREACH (const QString &name, m.namedCaptured.keys()) { + if (rem.captured(name) != m.namedCaptured.value(name)) + return false; + } + } + + return true; +} + +bool operator==(const Match &m, const QRegularExpressionMatch &rem) +{ + return operator==(rem, m); +} + +bool operator!=(const QRegularExpressionMatch &rem, const Match &m) +{ + return !operator==(rem, m); +} + +bool operator!=(const Match &m, const QRegularExpressionMatch &rem) +{ + return !operator==(m, rem); +} + + +bool operator==(const QRegularExpressionMatchIterator &iterator, const QList<Match> &expectedMatchList) +{ + QRegularExpressionMatchIterator i = iterator; + if (i.isValid() != (!expectedMatchList.isEmpty())) + return false; + + foreach (const Match &expectedMatch, expectedMatchList) + { + if (!i.hasNext()) + return false; + + QRegularExpressionMatch match = i.next(); + if (match != expectedMatch) + return false; + } + + if (i.hasNext()) + return false; + + return true; +} + +bool operator==(const QList<Match> &expectedMatchList, const QRegularExpressionMatchIterator &iterator) +{ + return operator==(iterator, expectedMatchList); +} + +bool operator!=(const QRegularExpressionMatchIterator &iterator, const QList<Match> &expectedMatchList) +{ + return !operator==(iterator, expectedMatchList); +} + +bool operator!=(const QList<Match> &expectedMatchList, const QRegularExpressionMatchIterator &iterator) +{ + return !operator==(expectedMatchList, iterator); +} + +void consistencyCheck(const QRegularExpressionMatch &match) +{ + if (match.isValid()) { + QVERIFY(match.regularExpression().isValid()); + QVERIFY(!(match.hasMatch() && match.hasPartialMatch())); + + if (match.hasMatch() || match.hasPartialMatch()) { + QVERIFY(match.lastCapturedIndex() >= 0); + if (match.hasPartialMatch()) + QVERIFY(match.lastCapturedIndex() == 0); + + for (int i = 0; i <= match.lastCapturedIndex(); ++i) { + int startPos = match.capturedStart(i); + int endPos = match.capturedEnd(i); + int length = match.capturedLength(i); + QString captured = match.captured(i); + QStringRef capturedRef = match.capturedRef(i); + + if (!captured.isNull()) { + QVERIFY(startPos >= 0); + QVERIFY(endPos >= 0); + QVERIFY(length >= 0); + QVERIFY(endPos >= startPos); + QVERIFY((endPos - startPos) == length); + QVERIFY(captured == capturedRef); + } else { + QVERIFY(startPos == -1); + QVERIFY(endPos == -1); + QVERIFY((endPos - startPos) == length); + QVERIFY(capturedRef.isNull()); + } + } + } + } else { + QVERIFY(!match.hasMatch()); + QVERIFY(!match.hasPartialMatch()); + QVERIFY(match.captured(0).isNull()); + QVERIFY(match.capturedStart(0) == -1); + QVERIFY(match.capturedEnd(0) == -1); + QVERIFY(match.capturedLength(0) == 0); + } +} + +void consistencyCheck(const QRegularExpressionMatchIterator &iterator) +{ + QRegularExpressionMatchIterator i(iterator); // make a copy, we modify it + if (i.isValid()) { + while (i.hasNext()) { + QRegularExpressionMatch peeked = i.peekNext(); + QRegularExpressionMatch match = i.next(); + consistencyCheck(peeked); + consistencyCheck(match); + QVERIFY(match.isValid()); + QVERIFY(match.hasMatch() || match.hasPartialMatch()); + QCOMPARE(i.regularExpression(), match.regularExpression()); + QCOMPARE(i.matchOptions(), match.matchOptions()); + QCOMPARE(i.matchType(), match.matchType()); + + QVERIFY(peeked.isValid() == match.isValid()); + QVERIFY(peeked.hasMatch() == match.hasMatch()); + QVERIFY(peeked.hasPartialMatch() == match.hasPartialMatch()); + QVERIFY(peeked.lastCapturedIndex() == match.lastCapturedIndex()); + for (int i = 0; i <= peeked.lastCapturedIndex(); ++i) { + QVERIFY(peeked.captured(i) == match.captured(i)); + QVERIFY(peeked.capturedStart(i) == match.capturedStart(i)); + QVERIFY(peeked.capturedEnd(i) == match.capturedEnd(i)); + } + } + } else { + QVERIFY(!i.hasNext()); + QTest::ignoreMessage(QtWarningMsg, "QRegularExpressionMatchIterator::peekNext() called on an iterator already at end"); + QRegularExpressionMatch peeked = i.peekNext(); + QTest::ignoreMessage(QtWarningMsg, "QRegularExpressionMatchIterator::next() called on an iterator already at end"); + QRegularExpressionMatch match = i.next(); + consistencyCheck(peeked); + consistencyCheck(match); + QVERIFY(!match.isValid()); + QVERIFY(!peeked.isValid()); + } + +} + +void tst_QRegularExpression::provideRegularExpressions() +{ + QTest::addColumn<QString>("pattern"); + QTest::addColumn<QRegularExpression::PatternOptions>("patternOptions"); + + QTest::newRow("emptynull01") << QString() + << QRegularExpression::PatternOptions(0); + QTest::newRow("emptynull02") << QString() + << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption + | QRegularExpression::DotMatchesEverythingOption + | QRegularExpression::MultilineOption); + QTest::newRow("emptynull03") << "" + << QRegularExpression::PatternOptions(0); + QTest::newRow("emptynull04") << "" + << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption + | QRegularExpression::DotMatchesEverythingOption + | QRegularExpression::MultilineOption); + + QTest::newRow("regexp01") << "a pattern" + << QRegularExpression::PatternOptions(0); + QTest::newRow("regexp02") << "^a (.*) more complicated(?<P>pattern)$" + << QRegularExpression::PatternOptions(0); + QTest::newRow("regexp03") << "(?:a) pAttErN" + << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption); + QTest::newRow("regexp04") << "a\nmultiline\npattern" + << QRegularExpression::PatternOptions(QRegularExpression::MultilineOption); + QTest::newRow("regexp05") << "an extended # IGNOREME\npattern" + << QRegularExpression::PatternOptions(QRegularExpression::ExtendedPatternSyntaxOption); + QTest::newRow("regexp06") << "a [sS]ingleline .* match" + << QRegularExpression::PatternOptions(QRegularExpression::DotMatchesEverythingOption); + QTest::newRow("regexp07") << "multiple.*options" + << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption + | QRegularExpression::DotMatchesEverythingOption + | QRegularExpression::MultilineOption + | QRegularExpression::DontCaptureOption + | QRegularExpression::InvertedGreedinessOption); + + QTest::newRow("unicode01") << QString::fromUtf8("^s[ome] latin-1 \xc3\x80\xc3\x88\xc3\x8c\xc3\x92\xc3\x99 chars$") + << QRegularExpression::PatternOptions(0); + QTest::newRow("unicode02") << QString::fromUtf8("^s[ome] latin-1 \xc3\x80\xc3\x88\xc3\x8c\xc3\x92\xc3\x99 chars$") + << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption + | QRegularExpression::DotMatchesEverythingOption + | QRegularExpression::InvertedGreedinessOption); + QTest::newRow("unicode03") << QString::fromUtf8("Unicode \xf0\x9d\x85\x9d \xf0\x9d\x85\x9e\xf0\x9d\x85\x9f") + << QRegularExpression::PatternOptions(0); + QTest::newRow("unicode04") << QString::fromUtf8("Unicode \xf0\x9d\x85\x9d \xf0\x9d\x85\x9e\xf0\x9d\x85\x9f") + << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption + | QRegularExpression::DotMatchesEverythingOption + | QRegularExpression::InvertedGreedinessOption); +} + +void tst_QRegularExpression::gettersSetters_data() +{ + provideRegularExpressions(); +} + +void tst_QRegularExpression::gettersSetters() +{ + QFETCH(QString, pattern); + QFETCH(QRegularExpression::PatternOptions, patternOptions); + { + QRegularExpression re; + re.setPattern(pattern); + QCOMPARE(re.pattern(), pattern); + QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); + } + { + QRegularExpression re; + re.setPatternOptions(patternOptions); + QCOMPARE(re.pattern(), QString()); + QCOMPARE(re.patternOptions(), patternOptions); + } + { + QRegularExpression re(pattern); + QCOMPARE(re.pattern(), pattern); + QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); + } + { + QRegularExpression re(pattern, patternOptions); + QCOMPARE(re.pattern(), pattern); + QCOMPARE(re.patternOptions(), patternOptions); + } +} + +void tst_QRegularExpression::escape_data() +{ + QTest::addColumn<QString>("string"); + QTest::addColumn<QString>("escaped"); + QTest::newRow("escape01") << "a normal pattern" + << "a\\ normal\\ pattern"; + + QTest::newRow("escape02") << "abcdefghijklmnopqrstuvzABCDEFGHIJKLMNOPQRSTUVZ1234567890_" + << "abcdefghijklmnopqrstuvzABCDEFGHIJKLMNOPQRSTUVZ1234567890_"; + + QTest::newRow("escape03") << "^\\ba\\b.*(?<NAME>reg|exp)$" + << "\\^\\\\ba\\\\b\\.\\*\\(\\?\\<NAME\\>reg\\|exp\\)\\$"; + + QString nulString("abcXabcXXabc"); + nulString[3] = nulString[7] = nulString[8] = QChar(0, 0); + QTest::newRow("NUL") << nulString + << "abc\\0abc\\0\\0abc"; + + QTest::newRow("unicode01") << QString::fromUtf8("^s[ome] latin-1 \xc3\x80\xc3\x88\xc3\x8c\xc3\x92\xc3\x99 chars$") + << QString::fromUtf8("\\^s\\[ome\\]\\ latin\\-1\\ \\\xc3\x80\\\xc3\x88\\\xc3\x8c\\\xc3\x92\\\xc3\x99\\ chars\\$"); + QTest::newRow("unicode02") << QString::fromUtf8("Unicode \xf0\x9d\x85\x9d \xf0\x9d\x85\x9e\xf0\x9d\x85\x9f") + << QString::fromUtf8("Unicode\\ \\\xf0\x9d\x85\x9d\\ \\\xf0\x9d\x85\x9e\\\xf0\x9d\x85\x9f"); + + QString unicodeAndNulString = QString::fromUtf8("^\xc3\x80\xc3\x88\xc3\x8cN\xc3\x92NN\xc3\x99 chars$"); + unicodeAndNulString[4] = unicodeAndNulString[6] = unicodeAndNulString[7] = QChar(0, 0); + QTest::newRow("unicode03") << unicodeAndNulString + << QString::fromUtf8("\\^\\\xc3\x80\\\xc3\x88\\\xc3\x8c\\0\\\xc3\x92\\0\\0\\\xc3\x99\\ chars\\$"); +} + +void tst_QRegularExpression::escape() +{ + QFETCH(QString, string); + QFETCH(QString, escaped); + QCOMPARE(QRegularExpression::escape(string), escaped); + QRegularExpression re(escaped); + QCOMPARE(re.isValid(), true); +} + +void tst_QRegularExpression::validity_data() +{ + QTest::addColumn<QString>("pattern"); + QTest::addColumn<bool>("validity"); + + QTest::newRow("valid01") << "a pattern" << true; + QTest::newRow("valid02") << "(a|pattern)" << true; + QTest::newRow("valid03") << "a [pP]attern" << true; + QTest::newRow("valid04") << "^(?<article>a).*(?<noun>pattern)$" << true; + QTest::newRow("valid05") << "a \\P{Ll}attern" << true; + + QTest::newRow("invalid01") << "a pattern\\" << false; + QTest::newRow("invalid02") << "(a|pattern" << false; + QTest::newRow("invalid03") << "a \\P{BLAH}attern" << false; + + QString pattern; + // 0xD800 (high surrogate) not followed by a low surrogate + pattern = "abcdef"; + pattern[3] = QChar(0x00, 0xD8); + QTest::newRow("invalidUnicode01") << pattern << false; +} + +void tst_QRegularExpression::validity() +{ + QFETCH(QString, pattern); + QFETCH(bool, validity); + QRegularExpression re(pattern); + QCOMPARE(re.isValid(), validity); + if (!validity) + QTest::ignoreMessage(QtWarningMsg, "QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); + QRegularExpressionMatch match = re.match("a pattern"); + QCOMPARE(match.isValid(), validity); + consistencyCheck(match); + + if (!validity) + QTest::ignoreMessage(QtWarningMsg, "QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); + QRegularExpressionMatchIterator iterator = re.globalMatch("a pattern"); + QCOMPARE(iterator.isValid(), validity); +} + +void tst_QRegularExpression::patternOptions_data() +{ + QTest::addColumn<QRegularExpression>("regexp"); + QTest::addColumn<QString>("subject"); + QTest::addColumn<Match>("match"); + + // none of these would successfully match if the respective + // pattern option is not set + + Match m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << QString::fromUtf8("AbC\xc3\xa0"); + QTest::newRow("/i") << QRegularExpression(QString::fromUtf8("abc\xc3\x80"), QRegularExpression::CaseInsensitiveOption) + << QString::fromUtf8("AbC\xc3\xa0") + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "abc123\n678def"; + QTest::newRow("/s") << QRegularExpression("\\Aabc.*def\\z", QRegularExpression::DotMatchesEverythingOption) + << "abc123\n678def" + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "jumped over"; + QTest::newRow("/m") << QRegularExpression("^\\w+ \\w+$", QRegularExpression::MultilineOption) + << "the quick fox\njumped over\nthe lazy\ndog" + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "abc 123456"; + QTest::newRow("/x") << QRegularExpression("\\w+ # a word\n" + "\\ # a space\n" + "\\w+ # another word", + QRegularExpression::ExtendedPatternSyntaxOption) + << "abc 123456 def" + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "the quick fox" << "the" << "quick fox"; + QTest::newRow("/U") << QRegularExpression("(.+) (.+?)", QRegularExpression::InvertedGreedinessOption) + << "the quick fox" + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "the quick fox" << "quick"; + m.namedCaptured["named"] = "quick"; + QTest::newRow("no cap") << QRegularExpression("(\\w+) (?<named>\\w+) (\\w+)", QRegularExpression::DontCaptureOption) + << "the quick fox" + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << QString::fromUtf8("abc\xc3\x80\xc3\xa0 12\xdb\xb1\xdb\xb2\xf0\x9d\x9f\x98") + << QString::fromUtf8("abc\xc3\x80\xc3\xa0") + << QString::fromUtf8("12\xdb\xb1\xdb\xb2\xf0\x9d\x9f\x98"); + QTest::newRow("unicode properties") << QRegularExpression("(\\w+) (\\d+)", QRegularExpression::UseUnicodePropertiesOption) + << QString::fromUtf8("abc\xc3\x80\xc3\xa0 12\xdb\xb1\xdb\xb2\xf0\x9d\x9f\x98") + << m; +} + +void tst_QRegularExpression::patternOptions() +{ + QFETCH(QRegularExpression, regexp); + QFETCH(QString, subject); + QFETCH(Match, match); + + QRegularExpressionMatch m = regexp.match(subject); + consistencyCheck(m); + QVERIFY(m == match); +} + +void tst_QRegularExpression::normalMatch_data() +{ + QTest::addColumn<QRegularExpression>("regexp"); + QTest::addColumn<QString>("subject"); + QTest::addColumn<int>("offset"); + QTest::addColumn<QRegularExpression::MatchOptions>("matchOptions"); + QTest::addColumn<Match>("match"); + + Match m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "string" << "string"; + QTest::newRow("match01") << QRegularExpression("(\\bstring\\b)") + << "a string" + << 0 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "a string" << "a" << "string"; + QTest::newRow("match02") << QRegularExpression("(\\w+) (\\w+)") + << "a string" + << 0 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "a string" << "a" << "string"; + m.namedCaptured["article"] = "a"; + m.namedCaptured["noun"] = "string"; + QTest::newRow("match03") << QRegularExpression("(?<article>\\w+) (?<noun>\\w+)") + << "a string" + << 0 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << " string" << QString() << "string"; + QTest::newRow("match04") << QRegularExpression("(\\w+)? (\\w+)") + << " string" + << 0 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << " string" << QString("") << "string"; + QTest::newRow("match05") << QRegularExpression("(\\w*) (\\w+)") + << " string" + << 0 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "c123def" << "c12" << "3" << "def"; + QTest::newRow("match06") << QRegularExpression("(\\w*)(\\d+)(\\w*)") + << "abc123def" + << 2 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << QString(""); + QTest::newRow("match07") << QRegularExpression("\\w*") + << "abc123def" + << 9 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << QString("a string") << QString("a string") << QString(""); + QTest::newRow("match08") << QRegularExpression("(.*)(.*)") + << "a string" + << 0 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << QString("a string") << QString("") << QString("a string"); + QTest::newRow("match09") << QRegularExpression("(.*?)(.*)") + << "a string" + << 0 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + // *** + + m.clear(); + m.isValid = true; + QTest::newRow("nomatch01") << QRegularExpression("\\d+") + << "a string" + << 0 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; + QTest::newRow("nomatch02") << QRegularExpression("(\\w+) (\\w+)") + << "a string" + << 1 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; + QTest::newRow("nomatch03") << QRegularExpression("\\w+") + << "abc123def" + << 9 + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + // *** + + m.clear(); + m.isValid = true; + QTest::newRow("anchoredmatch01") << QRegularExpression("\\d+") + << "abc123def" + << 0 + << QRegularExpression::MatchOptions(QRegularExpression::AnchoredMatchOption) + << m; +} + + +void tst_QRegularExpression::normalMatch() +{ + QFETCH(QRegularExpression, regexp); + QFETCH(QString, subject); + QFETCH(int, offset); + QFETCH(QRegularExpression::MatchOptions, matchOptions); + QFETCH(Match, match); + + QRegularExpressionMatch m = regexp.match(subject, offset, QRegularExpression::NormalMatch, matchOptions); + consistencyCheck(m); + QVERIFY(m == match); +} + + +void tst_QRegularExpression::partialMatch_data() +{ + QTest::addColumn<QRegularExpression>("regexp"); + QTest::addColumn<QString>("subject"); + QTest::addColumn<int>("offset"); + QTest::addColumn<QRegularExpression::MatchType>("matchType"); + QTest::addColumn<QRegularExpression::MatchOptions>("matchOptions"); + QTest::addColumn<Match>("match"); + + Match m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "str"; + QTest::newRow("softmatch01") << QRegularExpression("string") + << "a str" + << 0 + << QRegularExpression::PartialPreferCompleteMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << " str"; + QTest::newRow("softmatch02") << QRegularExpression("\\bstring\\b") + << "a str" + << 0 + << QRegularExpression::PartialPreferCompleteMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << " str"; + QTest::newRow("softmatch03") << QRegularExpression("(\\bstring\\b)") + << "a str" + << 0 + << QRegularExpression::PartialPreferCompleteMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "8 Dec 19"; + QTest::newRow("softmatch04") << QRegularExpression("^(\\d{1,2}) (\\w{3}) (\\d{4})$") + << "8 Dec 19" + << 0 + << QRegularExpression::PartialPreferCompleteMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "8 Dec 1985" << "8" << "Dec" << "1985"; + QTest::newRow("softmatch05") << QRegularExpression("^(\\d{1,2}) (\\w{3}) (\\d{4})$") + << "8 Dec 1985" + << 0 + << QRegularExpression::PartialPreferCompleteMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "def"; + QTest::newRow("softmatch06") << QRegularExpression("abc\\w+X|def") + << "abcdef" + << 0 + << QRegularExpression::PartialPreferCompleteMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "abcdef"; + QTest::newRow("softmatch07") << QRegularExpression("abc\\w+X|defY") + << "abcdef" + << 0 + << QRegularExpression::PartialPreferCompleteMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "def"; + QTest::newRow("softmatch08") << QRegularExpression("abc\\w+X|defY") + << "abcdef" + << 1 + << QRegularExpression::PartialPreferCompleteMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + // *** + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "str"; + QTest::newRow("hardmatch01") << QRegularExpression("string") + << "a str" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << " str"; + QTest::newRow("hardmatch02") << QRegularExpression("\\bstring\\b") + << "a str" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << " str"; + QTest::newRow("hardmatch03") << QRegularExpression("(\\bstring\\b)") + << "a str" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "8 Dec 19"; + QTest::newRow("hardmatch04") << QRegularExpression("^(\\d{1,2}) (\\w{3}) (\\d{4})$") + << "8 Dec 19" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "8 Dec 1985"; + QTest::newRow("hardmatch05") << QRegularExpression("^(\\d{1,2}) (\\w{3}) (\\d{4})$") + << "8 Dec 1985" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "abcdef"; + QTest::newRow("hardmatch06") << QRegularExpression("abc\\w+X|def") + << "abcdef" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "abcdef"; + QTest::newRow("hardmatch07") << QRegularExpression("abc\\w+X|defY") + << "abcdef" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "def"; + QTest::newRow("hardmatch08") << QRegularExpression("abc\\w+X|defY") + << "abcdef" + << 1 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "ab"; + QTest::newRow("hardmatch09") << QRegularExpression("abc|ab") + << "ab" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "abc"; + QTest::newRow("hardmatch10") << QRegularExpression("abc(def)?") + << "abc" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasPartialMatch = true; + m.captured << "abc"; + QTest::newRow("hardmatch11") << QRegularExpression("(abc)*") + << "abc" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + + // *** + + m.clear(); + m.isValid = true; + QTest::newRow("nomatch01") << QRegularExpression("abc\\w+X|defY") + << "123456" + << 0 + << QRegularExpression::PartialPreferCompleteMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; + QTest::newRow("nomatch02") << QRegularExpression("abc\\w+X|defY") + << "123456" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; + QTest::newRow("nomatch03") << QRegularExpression("abc\\w+X|defY") + << "ab123" + << 0 + << QRegularExpression::PartialPreferCompleteMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; + QTest::newRow("nomatch04") << QRegularExpression("abc\\w+X|defY") + << "ab123" + << 0 + << QRegularExpression::PartialPreferFirstMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + +} + +void tst_QRegularExpression::partialMatch() +{ + QFETCH(QRegularExpression, regexp); + QFETCH(QString, subject); + QFETCH(int, offset); + QFETCH(QRegularExpression::MatchType, matchType); + QFETCH(QRegularExpression::MatchOptions, matchOptions); + QFETCH(Match, match); + + QRegularExpressionMatch m = regexp.match(subject, offset, matchType, matchOptions); + consistencyCheck(m); + QVERIFY(m == match); +} + +void tst_QRegularExpression::globalMatch_data() +{ + QTest::addColumn<QRegularExpression>("regexp"); + QTest::addColumn<QString>("subject"); + QTest::addColumn<int>("offset"); + QTest::addColumn<QRegularExpression::MatchType>("matchType"); + QTest::addColumn<QRegularExpression::MatchOptions>("matchOptions"); + QTest::addColumn<QList<Match> >("matchList"); + + QList<Match> matchList; + Match m; + + matchList.clear(); + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured = QStringList() << "the"; + matchList << m; + m.captured = QStringList() << "quick"; + matchList << m; + m.captured = QStringList() << "fox"; + matchList << m; + QTest::newRow("globalmatch01") << QRegularExpression("\\w+") + << "the quick fox" + << 0 + << QRegularExpression::NormalMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << matchList; + + matchList.clear(); + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured = QStringList() << "the" << "t" << "he"; + matchList << m; + m.captured = QStringList() << "quick" << "q" << "uick"; + matchList << m; + m.captured = QStringList() << "fox" << "f" << "ox"; + matchList << m; + QTest::newRow("globalmatch02") << QRegularExpression("(\\w+?)(\\w+)") + << "the quick fox" + << 0 + << QRegularExpression::NormalMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << matchList; + + matchList.clear(); + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "c"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "c"; + matchList << m; + m.captured = QStringList() << "aabb"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + + QTest::newRow("globalmatch_emptycaptures01") << QRegularExpression("a*b*|c") + << "ccaabbd" + << 0 + << QRegularExpression::NormalMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << matchList; + + matchList.clear(); + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured = QStringList() << "the"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "quick"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "fox"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + + QTest::newRow("globalmatch_emptycaptures02") << QRegularExpression(".*") + << "the\nquick\nfox" + << 0 + << QRegularExpression::NormalMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << matchList; + + matchList.clear(); + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured = QStringList() << "the"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "quick"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "fox"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + + QTest::newRow("globalmatch_emptycaptures03") << QRegularExpression(".*") + << "the\nquick\nfox\n" + << 0 + << QRegularExpression::NormalMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << matchList; + + matchList.clear(); + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured = QStringList() << "the"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "quick"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "fox"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + + QTest::newRow("globalmatch_emptycaptures04") << QRegularExpression("(*CRLF).*") + << "the\r\nquick\r\nfox" + << 0 + << QRegularExpression::NormalMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << matchList; + + matchList.clear(); + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured = QStringList() << "the"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "quick"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "fox"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + + QTest::newRow("globalmatch_emptycaptures05") << QRegularExpression("(*CRLF).*") + << "the\r\nquick\r\nfox\r\n" + << 0 + << QRegularExpression::NormalMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << matchList; + + matchList.clear(); + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured = QStringList() << "the"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "quick"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "fox"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + m.captured = QStringList() << "jumped"; + matchList << m; + m.captured = QStringList() << ""; + matchList << m; + + QTest::newRow("globalmatch_emptycaptures06") << QRegularExpression("(*ANYCRLF).*") + << "the\r\nquick\nfox\rjumped" + << 0 + << QRegularExpression::NormalMatch + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << matchList; +} + +void tst_QRegularExpression::globalMatch() +{ + QFETCH(QRegularExpression, regexp); + QFETCH(QString, subject); + QFETCH(int, offset); + QFETCH(QRegularExpression::MatchType, matchType); + QFETCH(QRegularExpression::MatchOptions, matchOptions); + QFETCH(QList<Match>, matchList); + + QRegularExpressionMatchIterator iterator = regexp.globalMatch(subject, offset, matchType, matchOptions); + consistencyCheck(iterator); + QVERIFY(iterator == matchList); +} + +void tst_QRegularExpression::serialize_data() +{ + provideRegularExpressions(); +} + +void tst_QRegularExpression::serialize() +{ + QFETCH(QString, pattern); + QFETCH(QRegularExpression::PatternOptions, patternOptions); + QRegularExpression outRe(pattern, patternOptions); + QByteArray buffer; + { + QDataStream out(&buffer, QIODevice::WriteOnly); + out << outRe; + } + QRegularExpression inRe; + { + QDataStream in(&buffer, QIODevice::ReadOnly); + in >> inRe; + } + QCOMPARE(inRe, outRe); +} + +static void verifyEquality(const QRegularExpression &re1, const QRegularExpression &re2) +{ + QVERIFY(re1 == re2); + QVERIFY(re2 == re1); + QVERIFY(!(re1 != re2)); + QVERIFY(!(re2 != re1)); + + QRegularExpression re3(re1); + + QVERIFY(re1 == re3); + QVERIFY(re3 == re1); + QVERIFY(!(re1 != re3)); + QVERIFY(!(re3 != re1)); + + QVERIFY(re2 == re3); + QVERIFY(re3 == re2); + QVERIFY(!(re2 != re3)); + QVERIFY(!(re3 != re2)); + + re3 = re2; + QVERIFY(re1 == re3); + QVERIFY(re3 == re1); + QVERIFY(!(re1 != re3)); + QVERIFY(!(re3 != re1)); + + QVERIFY(re2 == re3); + QVERIFY(re3 == re2); + QVERIFY(!(re2 != re3)); + QVERIFY(!(re3 != re2)); +} + +void tst_QRegularExpression::operatoreq_data() +{ + provideRegularExpressions(); +} + +void tst_QRegularExpression::operatoreq() +{ + QFETCH(QString, pattern); + QFETCH(QRegularExpression::PatternOptions, patternOptions); + { + QRegularExpression re1(pattern); + QRegularExpression re2(pattern); + verifyEquality(re1, re2); + } + { + QRegularExpression re1(QString(), patternOptions); + QRegularExpression re2(QString(), patternOptions); + verifyEquality(re1, re2); + } + { + QRegularExpression re1(pattern, patternOptions); + QRegularExpression re2(pattern, patternOptions); + verifyEquality(re1, re2); + } +} + +void tst_QRegularExpression::captureCount_data() +{ + QTest::addColumn<QString>("pattern"); + QTest::addColumn<int>("captureCount"); + QTest::newRow("captureCount01") << "a pattern" << 0; + QTest::newRow("captureCount02") << "a.*pattern" << 0; + QTest::newRow("captureCount03") << "(a) pattern" << 1; + QTest::newRow("captureCount04") << "(a).*(pattern)" << 2; + QTest::newRow("captureCount05") << "^(?<article>\\w+) (?<noun>\\w+)$" << 2; + QTest::newRow("captureCount06") << "^(\\w+) (?<word>\\w+) (.)$" << 3; + QTest::newRow("captureCount07") << "(?:non capturing) (capturing) (?<n>named) (?:non (capturing))" << 3; + QTest::newRow("captureCount08") << "(?|(a)(b)|(c)(d))" << 2; + QTest::newRow("captureCount09") << "(?|(a)(b)|(c)(d)(?:e))" << 2; + QTest::newRow("captureCount10") << "(?|(a)(b)|(c)(d)(e)) (f)(g)" << 5; + QTest::newRow("captureCount11") << "(?|(a)(b)|(c)(d)(e)) (f)(?:g)" << 4; + QTest::newRow("captureCount_invalid01") << "(.*" << -1; + QTest::newRow("captureCount_invalid02") << "\\" << -1; + QTest::newRow("captureCount_invalid03") << "(?<noun)" << -1; +} + +void tst_QRegularExpression::captureCount() +{ + QFETCH(QString, pattern); + QRegularExpression re(pattern); + QTEST(re.captureCount(), "captureCount"); + if (!re.isValid()) + QCOMPARE(re.captureCount(), -1); +} diff --git a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.h b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.h new file mode 100644 index 0000000000..1a703a8f92 --- /dev/null +++ b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>. +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite 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 <qobject.h> +#include <qregularexpression.h> + +Q_DECLARE_METATYPE(QRegularExpression::PatternOptions) +Q_DECLARE_METATYPE(QRegularExpression::MatchType) +Q_DECLARE_METATYPE(QRegularExpression::MatchOptions) + +class tst_QRegularExpression : public QObject +{ + Q_OBJECT + +private slots: + void gettersSetters_data(); + void gettersSetters(); + void escape_data(); + void escape(); + void validity_data(); + void validity(); + void patternOptions_data(); + void patternOptions(); + void normalMatch_data(); + void normalMatch(); + void partialMatch_data(); + void partialMatch(); + void globalMatch_data(); + void globalMatch(); + void serialize_data(); + void serialize(); + void operatoreq_data(); + void operatoreq(); + void captureCount_data(); + void captureCount(); + +private: + void provideRegularExpressions(); +}; diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro index 89bb3bc416..d8961559e5 100644 --- a/tests/auto/corelib/tools/tools.pro +++ b/tests/auto/corelib/tools/tools.pro @@ -25,6 +25,7 @@ SUBDIRS=\ qqueue \ qrect \ qregexp \ + qregularexpression \ qringbuffer \ qscopedpointer \ qscopedvaluerollback \ diff --git a/tests/auto/gui/kernel/qshortcut/tst_qshortcut.cpp b/tests/auto/gui/kernel/qshortcut/tst_qshortcut.cpp index 97c749e5ba..61efb6e3f3 100644 --- a/tests/auto/gui/kernel/qshortcut/tst_qshortcut.cpp +++ b/tests/auto/gui/kernel/qshortcut/tst_qshortcut.cpp @@ -151,14 +151,9 @@ protected: Result ambigResult; }; -QT_BEGIN_NAMESPACE -template<> struct QMetaTypeId<tst_QShortcut::Widget> : public QMetaTypeId<int> -{ static inline int qt_metatype_id() { return QMetaType::Int; } }; -template<> struct QMetaTypeId<tst_QShortcut::Result> : public QMetaTypeId<int> -{ static inline int qt_metatype_id() { return QMetaType::Int; } }; -template<> struct QMetaTypeId<tst_QShortcut::Action> : public QMetaTypeId<int> -{ static inline int qt_metatype_id() { return QMetaType::Int; } }; -QT_END_NAMESPACE +Q_DECLARE_METATYPE(tst_QShortcut::Widget); +Q_DECLARE_METATYPE(tst_QShortcut::Result); +Q_DECLARE_METATYPE(tst_QShortcut::Action); class TestEdit : public QTextEdit { @@ -221,9 +216,7 @@ void tst_QShortcut::initTestCase() mainW->setFixedSize( 100, 100 ); mainW->setCentralWidget( edit ); mainW->show(); -#ifdef Q_WS_X11 - qt_x11_wait_for_window_manager(mainW); -#endif + QTest::qWaitForWindowShown(mainW); mainW->activateWindow(); QTest::qWait(100); connect( mainW->statusBar(), SIGNAL(messageChanged(const QString&)), @@ -251,8 +244,8 @@ Qt::KeyboardModifiers tst_QShortcut::toButtons( int key ) void tst_QShortcut::defElements() { - QTest::addColumn<int>("action"); - QTest::addColumn<int>("testWidget"); + QTest::addColumn<tst_QShortcut::Action>("action"); + QTest::addColumn<tst_QShortcut::Widget>("testWidget"); QTest::addColumn<QString>("txt"); QTest::addColumn<int>("k1"); QTest::addColumn<int>("c1"); @@ -262,7 +255,7 @@ void tst_QShortcut::defElements() QTest::addColumn<int>("c3"); QTest::addColumn<int>("k4"); QTest::addColumn<int>("c4"); - QTest::addColumn<int>("result"); + QTest::addColumn<tst_QShortcut::Result>("result"); } void tst_QShortcut::number() @@ -287,7 +280,7 @@ void tst_QShortcut::number_data() defElements(); // Clear all - QTest::newRow("N00 - clear") << ClearAll <<0<<QString("")<<0<<0<<0<<0<<0<<0<<0<<0<<0; + QTest::newRow("N00 - clear") << ClearAll << NoWidget <<QString("")<<0<<0<<0<<0<<0<<0<<0<<0<<NoResult; //=========================================== // [Shift + key] on non-shift shortcuts testing @@ -307,7 +300,7 @@ void tst_QShortcut::number_data() //commented out because the behaviour changed, those tests should be updated //QTest::newRow("N001:Shift++ [+]") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N001:+ [+]") << TestAccel << NoWidget << QString("") << int(Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("N001 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N001 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Shift + Qt::Key_M on Shift + Qt::Key_M @@ -321,7 +314,7 @@ void tst_QShortcut::number_data() QTest::newRow("N002 - slot2") << SetupAccel << TriggerSlot2 << QString("") << int(Qt::SHIFT + Qt::Key_Plus) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N002:Shift++ [Shift++]") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N002:+ [Shift++]") << TestAccel << NoWidget << QString("") << int(Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N002 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N002 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Shift + Qt::Key_F1 on Qt::Key_F1 @@ -331,7 +324,7 @@ void tst_QShortcut::number_data() //commented out because the behaviour changed, those tests should be updated //QTest::newRow("N003:Shift+F1 - [F1]") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N003:F1 - [F1]") << TestAccel << NoWidget << QString("") << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N003 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N003 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Shift + Qt::Key_F1 on Shift + Qt::Key_F1 Qt::Key_F1 on Shift + Qt::Key_F1 @@ -340,7 +333,7 @@ void tst_QShortcut::number_data() QTest::newRow("N004 - slot1") << SetupAccel << TriggerSlot1 << QString("") << int(Qt::SHIFT + Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N004:Shift+F1 - [Shift+F1]")<< TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N004:F1 - [Shift+F1]") << TestAccel << NoWidget << QString("") << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N004 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N004 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Qt::Key_Tab on Qt::Key_Tab @@ -355,7 +348,7 @@ void tst_QShortcut::number_data() // (Shift+)BackTab != Tab, but Shift+BackTab == Shift+Tab QTest::newRow("N005a:Backtab - [Tab]") << TestAccel << NoWidget << QString("") << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N005a:Shift+Backtab - [Tab]") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("N005a - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N005a - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Qt::Key_Tab on Shift + Qt::Key_Tab @@ -368,7 +361,7 @@ void tst_QShortcut::number_data() QTest::newRow("N005b:Shift+Tab - [Shift+Tab]") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N005b:BackTab - [Shift+Tab]") << TestAccel << NoWidget << QString("") << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N005b:Shift+BackTab - [Shift+Tab]")<< TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N005b - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N005b - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Qt::Key_Tab on Qt::Key_Backtab @@ -384,7 +377,7 @@ void tst_QShortcut::number_data() QTest::newRow("N006a:BackTab - [BackTab]") << TestAccel << NoWidget << QString("") << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //commented out because the behaviour changed, those tests should be updated //QTest::newRow("N006a:Shift+BackTab - [BackTab]") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N006a - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N006a - clear") << ClearAll << NoWidget<< QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Qt::Key_Tab on Shift + Qt::Key_Backtab @@ -397,7 +390,7 @@ void tst_QShortcut::number_data() QTest::newRow("N006b:Shift+Tab - [Shift+BackTab]")<< TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_Tab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N006b:BackTab - [Shift+BackTab]") << TestAccel << NoWidget << QString("") << int(Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N006b:Shift+BackTab - [Shift+BackTab]")<< TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_Backtab) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; //XFAIL - QTest::newRow("N006b - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N006b - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all //=========================================== // [Shift + key] and [key] on shortcuts with @@ -412,7 +405,7 @@ void tst_QShortcut::number_data() QTest::newRow("N007 - slot2") << SetupAccel << TriggerSlot2 << QString("") << int(Qt::SHIFT + Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N007:F1") << TestAccel << NoWidget << QString("") << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N007:Shift + F1") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("N007 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N007 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Qt::Key_M @@ -434,7 +427,7 @@ void tst_QShortcut::number_data() */ QTest::newRow("N05 - slot2") << SetupAccel << TriggerSlot2 << QString("") << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("N:Qt::Key_M on slot") << TestAccel << NoWidget << QString("") << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << Ambiguous; - QTest::newRow("N05 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N05 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Specialkeys Qt::Key_aring @@ -450,7 +443,7 @@ void tst_QShortcut::number_data() QTest::newRow("N:Qt::Key_aring - Text Form") << TestAccel << NoWidget << QString("") << 0 << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N:Qt::Key_Aring - Text Form") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT+0) << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N:Qt::UNICODE_ACCEL + Qt::Key_K") << TestAccel << NoWidget << QString("") << int(Qt::UNICODE_ACCEL + Qt::Key_K) << int('k') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N09 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N09 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Multiple Sequences Qt::Key_M @@ -464,7 +457,7 @@ void tst_QShortcut::number_data() QTest::newRow("N:Qt::Key_M (2)") << TestAccel << NoWidget << QString("") << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("N:Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString("") << int(Qt::Key_I) << int('i') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("N:Shift+Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_I) << int('I') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("N13 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("N13 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all } // ------------------------------------------------------------------ @@ -474,7 +467,7 @@ void tst_QShortcut::text_data() { defElements(); // Clear all - QTest::newRow("T00 - clear") << ClearAll <<0<<QString("")<<0<<0<<0<<0<<0<<0<<0<<0<<0; + QTest::newRow("T00 - clear") << ClearAll << NoWidget <<QString("")<<0<<0<<0<<0<<0<<0<<0<<0<< NoResult; //=========================================== // [Shift + key] on non-shift shortcuts testing @@ -494,7 +487,7 @@ void tst_QShortcut::text_data() //commented out because the behaviour changed, those tests should be updated //QTest::newRow("T001:Shift++ [+]") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T001:+ [+]") << TestAccel << NoWidget << QString("") << int(Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("T001 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("T001 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Shift + Qt::Key_M on Shift + Qt::Key_M @@ -510,7 +503,7 @@ void tst_QShortcut::text_data() QTest::newRow("T002 - slot2") << SetupAccel << TriggerSlot2 << QString("Shift++") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T002:Shift++ [Shift++]") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T002:+ [Shift++]") << TestAccel << NoWidget << QString("") << int(Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("T002 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("T002 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Shift + Ctrl + Qt::Key_Plus on Ctrl + Qt::Key_Plus @@ -522,7 +515,7 @@ void tst_QShortcut::text_data() //QTest::newRow("T002b:Shift+Ctrl++ [Ctrl++]")<< TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::CTRL + Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T002b:Ctrl++ [Ctrl++]") << TestAccel << NoWidget << QString("") << int(Qt::CTRL + Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T002b:+ [Ctrl++]") << TestAccel << NoWidget << QString("") << int(Qt::Key_Plus) << int('+') << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("T002b - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("T002b - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Shift + Qt::Key_F1 on Qt::Key_F1 @@ -532,7 +525,7 @@ void tst_QShortcut::text_data() //commented out because the behaviour changed, those tests should be updated //QTest::newRow("T003:Shift+F1 - [F1]") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T003:F1 - [F1]") << TestAccel << NoWidget << QString("") << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T003 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("T003 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Shift + Qt::Key_F1 on Shift + Qt::Key_F1 @@ -541,7 +534,7 @@ void tst_QShortcut::text_data() QTest::newRow("T004 - slot1") << SetupAccel << TriggerSlot1 << QString("Shift+F1") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T004:Shift+F1 - [Shift+F1]")<< TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T004:F1 - [Shift+F1]") << TestAccel << NoWidget << QString("") << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; - QTest::newRow("T004 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("T004 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all //=========================================== // [Shift + key] and [key] on shortcuts with @@ -556,7 +549,7 @@ void tst_QShortcut::text_data() QTest::newRow("T007 - slot2") << SetupAccel << TriggerSlot2 << QString("Shift+F1") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T007:F1") << TestAccel << NoWidget << QString("") << int(Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T007:Shift + F1") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_F1) << 0 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; - QTest::newRow("T007 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("T007 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Sequences Qt::Key_M @@ -579,7 +572,7 @@ void tst_QShortcut::text_data() */ QTest::newRow("T05 - slot2") << SetupAccel << TriggerSlot2 << QString("M") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; QTest::newRow("T:Qt::Key_M on TriggerSlot2") << TestAccel << NoWidget << QString("") << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << Ambiguous; - QTest::newRow("T06 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("T06 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Single Specialkeys Qt::Key_aring @@ -595,7 +588,7 @@ void tst_QShortcut::text_data() QTest::newRow("T:Qt::Key_aring - Text Form") << TestAccel << NoWidget << QString("") << 0 << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T:Qt::Key_Aring - Text Form") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT+0) << 0xC5 << 0 << 0 << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T:Qt::UNICODE_ACCEL + Qt::Key_K") << TestAccel << NoWidget << QString("") << int(Qt::UNICODE_ACCEL + Qt::Key_K) << int('k') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T09 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("T09 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all /* Testing Multiple Sequences Qt::Key_M @@ -608,7 +601,7 @@ void tst_QShortcut::text_data() QTest::newRow("T:Qt::Key_M (2)") << TestAccel << NoWidget << QString("") << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << 0 << 0 << Slot1Triggered; QTest::newRow("T:Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString("") << int(Qt::Key_I) << int('i') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot2Triggered; QTest::newRow("T:Shift+Qt::Key_I, Qt::Key_M") << TestAccel << NoWidget << QString("") << int(Qt::SHIFT + Qt::Key_I) << int('I') << int(Qt::Key_M) << int('m') << 0 << 0 << 0 << 0 << Slot1Triggered; - QTest::newRow("T13 - clear") << ClearAll << 0 << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0; // Clear all + QTest::newRow("T13 - clear") << ClearAll << NoWidget << QString("") << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << NoResult; // Clear all } // ------------------------------------------------------------------ @@ -1239,8 +1232,8 @@ void tst_QShortcut::sendKeyEvents(QWidget *w, int k1, QChar c1, int k2, QChar c2 void tst_QShortcut::testElement() { currentResult = NoResult; - QFETCH(int, action); - QFETCH(int, testWidget); + QFETCH(tst_QShortcut::Action, action); + QFETCH(tst_QShortcut::Widget, testWidget); QFETCH(QString, txt); QFETCH(int, k1); QFETCH(int, c1); @@ -1250,15 +1243,15 @@ void tst_QShortcut::testElement() QFETCH(int, c3); QFETCH(int, k4); QFETCH(int, c4); - QFETCH(int, result); + QFETCH(tst_QShortcut::Result, result); if (action == ClearAll) { clearAllShortcuts(); } else if (action == SetupAccel) { setupShortcut(testWidget, txt, k1, k2, k3, k4); } else { - sendKeyEvents(k1, c1, k2, c2, k3, c3, k4, c4); - QCOMPARE(int(currentResult), result); + sendKeyEvents(k1, c1, k2, c2, k3, c3, k4, c4); + QCOMPARE(currentResult, result); } } diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index d8f9bf32ad..8685546a5f 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -3385,6 +3385,7 @@ void tst_QNetworkReply::ioGetFromHttpWithCache_data() << QNetworkCacheMetaData::RawHeader("Cache-control", "max-age=0"); // isn't used in cache loading content.first.setRawHeaders(rawHeaders); content.first.setLastModified(past); + content.first.setExpirationDate(past); QTest::newRow("expired,200,prefer-network") << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true; @@ -6334,29 +6335,52 @@ void tst_QNetworkReply::qtbug13431replyThrottling() void tst_QNetworkReply::httpWithNoCredentialUsage() { - QNetworkRequest request(QUrl("http://httptest:httptest@" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi")); - // Do not use credentials - request.setAttribute(QNetworkRequest::AuthenticationReuseAttribute, QNetworkRequest::Manual); QNetworkAccessManager manager; - QNetworkReplyPtr reply = manager.get(request); - qRegisterMetaType<QNetworkReply*>("QNetworkReply*"); - qRegisterMetaType<QAuthenticator*>("QAuthenticator*"); QSignalSpy authSpy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*))); - qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError"); - QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError))); - connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection); - QTestEventLoop::instance().enterLoop(10); - QVERIFY(!QTestEventLoop::instance().timeout()); + // Get with credentials, to preload authentication cache + { + QNetworkRequest request(QUrl("http://httptest:httptest@" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi")); + QNetworkReplyPtr reply = manager.get(request); + QVERIFY(waitForFinish(reply) == Success); + // credentials in URL, so don't expect authentication signal + QCOMPARE(authSpy.count(), 0); + QCOMPARE(finishedSpy.count(), 1); + finishedSpy.clear(); + } - // We check if authenticationRequired was emitted, however we do not anything in it so it should be 401 - QCOMPARE(authSpy.count(), 1); - QCOMPARE(finishedSpy.count(), 1); - QCOMPARE(errorSpy.count(), 1); + // Get with cached credentials (normal usage) + { + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi")); + QNetworkReplyPtr reply = manager.get(request); + QVERIFY(waitForFinish(reply) == Success); + // credentials in cache, so don't expect authentication signal + QCOMPARE(authSpy.count(), 0); + QCOMPARE(finishedSpy.count(), 1); + finishedSpy.clear(); + } - QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError); + // Do not use cached credentials (webkit cross origin usage) + { + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/protected/cgi-bin/md5sum.cgi")); + request.setAttribute(QNetworkRequest::AuthenticationReuseAttribute, QNetworkRequest::Manual); + QNetworkReplyPtr reply = manager.get(request); + + QSignalSpy errorSpy(reply, SIGNAL(error(QNetworkReply::NetworkError))); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // We check if authenticationRequired was emitted, however we do not anything in it so it should be 401 + QCOMPARE(authSpy.count(), 1); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(errorSpy.count(), 1); + + QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError); + } } void tst_QNetworkReply::qtbug15311doubleContentLength() diff --git a/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp b/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp index 5da9ff0476..93299c26c6 100644 --- a/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp +++ b/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp @@ -109,6 +109,7 @@ private slots: void subjectAndIssuerAttributes(); void verify(); void extensions(); + void threadSafeConstMethods(); // helper for verbose test failure messages QString toString(const QList<QSslError>&); @@ -1059,6 +1060,77 @@ void tst_QSslCertificate::extensions() } +class TestThread : public QThread +{ +public: + void run() + { + effectiveDate = cert.effectiveDate(); + expiryDate = cert.expiryDate(); + extensions = cert.extensions(); + isBlacklisted = cert.isBlacklisted(); + issuerInfo = cert.issuerInfo(QSslCertificate::CommonName); + issuerInfoAttributes = cert.issuerInfoAttributes(); + publicKey = cert.publicKey(); + serialNumber = cert.serialNumber(); + subjectInfo = cert.subjectInfo(QSslCertificate::CommonName); + subjectInfoAttributes = cert.subjectInfoAttributes(); + toDer = cert.toDer(); + toPem = cert.toPem(); + toText = cert.toText(); + version = cert.version(); + } + QSslCertificate cert; + QDateTime effectiveDate; + QDateTime expiryDate; + QList<QSslCertificateExtension> extensions; + bool isBlacklisted; + QStringList issuerInfo; + QList<QByteArray> issuerInfoAttributes; + QSslKey publicKey; + QByteArray serialNumber; + QStringList subjectInfo; + QList<QByteArray> subjectInfoAttributes; + QByteArray toDer; + QByteArray toPem; + QByteArray toText; + QByteArray version; +}; + +void tst_QSslCertificate::threadSafeConstMethods() +{ + if (!QSslSocket::supportsSsl()) + return; + + QByteArray encoded = readFile(testDataDir + "/certificates/cert.pem"); + QSslCertificate certificate(encoded); + QVERIFY(!certificate.isNull()); + + TestThread t1; + t1.cert = certificate; //shallow copy + TestThread t2; + t2.cert = certificate; //shallow copy + t1.start(); + t2.start(); + QVERIFY(t1.wait(5000)); + QVERIFY(t2.wait(5000)); + QVERIFY(t1.cert == t2.cert); + QVERIFY(t1.effectiveDate == t2.effectiveDate); + QVERIFY(t1.expiryDate == t2.expiryDate); + //QVERIFY(t1.extensions == t2.extensions); // no equality operator, so not tested + QVERIFY(t1.isBlacklisted == t2.isBlacklisted); + QVERIFY(t1.issuerInfo == t2.issuerInfo); + QVERIFY(t1.issuerInfoAttributes == t2.issuerInfoAttributes); + QVERIFY(t1.publicKey == t2.publicKey); + QVERIFY(t1.serialNumber == t2.serialNumber); + QVERIFY(t1.subjectInfo == t2.subjectInfo); + QVERIFY(t1.subjectInfoAttributes == t2.subjectInfoAttributes); + QVERIFY(t1.toDer == t2.toDer); + QVERIFY(t1.toPem == t2.toPem); + QVERIFY(t1.toText == t2.toText); + QVERIFY(t1.version == t2.version); + +} #endif // QT_NO_SSL diff --git a/tests/auto/other/headersclean/headersclean.pri b/tests/auto/other/headersclean/headersclean.pri new file mode 100644 index 0000000000..163ef6d409 --- /dev/null +++ b/tests/auto/other/headersclean/headersclean.pri @@ -0,0 +1,38 @@ +# The headersclean test attempts to ensure all public Qt headers +# abide by various rules. +# +# To reuse this test outside of the qtbase module, +# include this .pri and create a qt_headersclean_headers.h which includes +# the headers you'd like to test. + +*-g++*: QMAKE_CXXFLAGS += -W -Wall -Wextra -Werror + +# The flags here come from http://wiki.qt-project.org/Coding_Conventions#Conventions_for_public_header_files +# -Wold-style-cast cannot be used, /usr/include/bits/byteswap.h defines the macro bswap_16 using C style casts :( +# -Wfloat-equal cannot be used, qrect.h and qvector2d.h do exact comparisons in isNull and operator==. Would need #pragmas. +*-g++*: QMAKE_CXXFLAGS += -Woverloaded-virtual -Wshadow -Wundef + +# Other nice flags +*-g++*: QMAKE_CXXFLAGS += -Wnon-virtual-dtor -ansi -Wcast-align -Wchar-subscripts -Wpointer-arith -Wformat-security + +# Enable pedantic mode, but accept variadic macros and 'long long' usage. +*-g++*: QMAKE_CXXFLAGS += -Wno-long-long -Wno-variadic-macros -pedantic-errors + +# There are outstanding alignment issues on ARM in some container classes. +contains(QT_ARCH,arm):*-g++*:QMAKE_CXXFLAGS -= -Wcast-align + +QMAKE_CXXFLAGS += -DQT_NO_CAST_TO_ASCII \ + -DQT_NO_CAST_FROM_ASCII \ + -DQT_STRICT_ITERATORS \ + -DQT_NO_URL_CAST_FROM_STRING \ + -DQT_NO_CAST_FROM_BYTEARRAY \ + -DQT_NO_KEYWORDS \ + -DQT_USE_FAST_CONCATENATION \ + -DQT_USE_FAST_OPERATOR_PLUS + +TARGET = tst_headersclean +SOURCES += $$PWD/tst_headersclean.cpp +HEADERS += $$PWD/tst_headersclean.h + +# qt_headersclean_headers.h should be alongside the .pro file +INCLUDEPATH += $$_PRO_FILE_PWD_ diff --git a/tests/auto/other/headersclean/headersclean.pro b/tests/auto/other/headersclean/headersclean.pro index 5444bebfb9..9bfadca08c 100644 --- a/tests/auto/other/headersclean/headersclean.pro +++ b/tests/auto/other/headersclean/headersclean.pro @@ -1,28 +1,2 @@ -CONFIG += testcase -TARGET = tst_headersclean -SOURCES += tst_headersclean.cpp - -# No need to link to all modules, only those actually used QT = core testlib gui widgets - -*-g++*: QMAKE_CXXFLAGS += -W -Wall -Wextra -Werror - -# The flags here come from http://wiki.qt-project.org/Coding_Conventions#Conventions_for_public_header_files -# -Wold-style-cast cannot be used, /usr/include/bits/byteswap.h defines the macro bswap_16 using C style casts :( -# -Wfloat-equal cannot be used, qrect.h and qvector2d.h do exact comparisons in isNull and operator==. Would need #pragmas. -*-g++*: QMAKE_CXXFLAGS += -Woverloaded-virtual -Wshadow -Wundef - -# Other nice flags -*-g++*: QMAKE_CXXFLAGS += -Wnon-virtual-dtor -ansi -Wcast-align -Wchar-subscripts -Wpointer-arith -Wformat-security - -# Enable pedantic mode, but accept variadic macros and 'long long' usage. -*-g++*: QMAKE_CXXFLAGS += -Wno-long-long -Wno-variadic-macros -pedantic-errors - -QMAKE_CXXFLAGS += -DQT_NO_CAST_TO_ASCII \ - -DQT_NO_CAST_FROM_ASCII \ - -DQT_STRICT_ITERATORS \ - -DQT_NO_URL_CAST_FROM_STRING \ - -DQT_NO_CAST_FROM_BYTEARRAY \ - -DQT_NO_KEYWORDS \ - -DQT_USE_FAST_CONCATENATION \ - -DQT_USE_FAST_OPERATOR_PLUS +include(headersclean.pri) diff --git a/tests/auto/other/headersclean/qt_headersclean_headers.h b/tests/auto/other/headersclean/qt_headersclean_headers.h new file mode 100644 index 0000000000..c9b4e3f399 --- /dev/null +++ b/tests/auto/other/headersclean/qt_headersclean_headers.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite 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 QT_HEADERSCLEAN_HEADERS +#define QT_HEADERSCLEAN_HEADERS + +/* + This file should include all the headers to be tested by the headersclean + test. It may be copied and customized for each module. +*/ + +#include <QtCore/QtCore> +#include <QtConcurrent/QtConcurrent> +#include <QtTest/QtTest> + +#include <QtNetwork/QtNetwork> +#include <QtXml/QtXml> +#include <QtSql/QtSql> +#include <QtGui/QtGui> +#include <QtWidgets/QtWidgets> +#include <QtPrintSupport/QtPrintSupport> +#include <QtPlatformSupport/QtPlatformSupport> + +#ifndef QT_NO_OPENGL +#include <QtOpenGL/QtOpenGL> +#endif + +#if !defined(QT_NO_DBUS) && defined(Q_OS_UNIX) +#include <QtDBus/QtDBus> +#endif + +#endif diff --git a/tests/auto/other/headersclean/tst_headersclean.cpp b/tests/auto/other/headersclean/tst_headersclean.cpp index 81d0aa3a4a..d442ad39d3 100644 --- a/tests/auto/other/headersclean/tst_headersclean.cpp +++ b/tests/auto/other/headersclean/tst_headersclean.cpp @@ -39,38 +39,12 @@ ** ****************************************************************************/ -#define signals int -#define slots int -#define emit public:; -#define foreach public:; -#define forever public:; +#include "tst_headersclean.h" +#include <QTest> -#include <QtCore/QtCore> -#include <QtConcurrent/QtConcurrent> -#include <QtTest/QtTest> - -#include <QtNetwork/QtNetwork> -#include <QtXml/QtXml> -#include <QtSql/QtSql> -#include <QtGui/QtGui> -#include <QtWidgets/QtWidgets> -#include <QtPrintSupport/QtPrintSupport> -#include <QtPlatformSupport/QtPlatformSupport> - -#ifndef QT_NO_OPENGL -#include <QtOpenGL/QtOpenGL> -#endif - -#if !defined(QT_NO_DBUS) && defined(Q_OS_UNIX) -#include <QtDBus/QtDBus> -#endif - -class tst_HeadersClean: public QObject +void tst_HeadersClean::initTestCase() { - Q_OBJECT -public: - tst_HeadersClean() {} -}; + QWARN("This is a compile test only"); +} QTEST_MAIN(tst_HeadersClean) -#include "tst_headersclean.moc" diff --git a/tests/auto/other/headersclean/tst_headersclean.h b/tests/auto/other/headersclean/tst_headersclean.h new file mode 100644 index 0000000000..7479e061dc --- /dev/null +++ b/tests/auto/other/headersclean/tst_headersclean.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite 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 TST_HEADERSCLEAN_H +#define TST_HEADERSCLEAN_H + +#define signals int +#define slots int +#define emit public:; +#define foreach public:; +#define forever public:; + +#include <qt_headersclean_headers.h> +#include <QObject> + +class tst_HeadersClean: public QObject +{ + Q_OBJECT +private Q_SLOTS: + void initTestCase(); +}; + +#endif diff --git a/tests/auto/other/other.pro b/tests/auto/other/other.pro index 9ec5ea8e34..6c708baa6b 100644 --- a/tests/auto/other/other.pro +++ b/tests/auto/other/other.pro @@ -30,8 +30,7 @@ testcocoon: SUBDIRS -= headersclean cross_compile: SUBDIRS -= \ atwrapper \ - compiler \ - headersclean \ + compiler wince*|!contains(QT_CONFIG, accessibility):SUBDIRS -= qaccessibility diff --git a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp index b2a4b1cca2..4c370bebc7 100644 --- a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp +++ b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp @@ -777,6 +777,7 @@ void tst_QAccessibility::actionTest() { QPushButton *button = new QPushButton; button->show(); + QTest::qWaitForWindowShown(button); button->clearFocus(); QCOMPARE(button->hasFocus(), false); QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(button); diff --git a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp index b22e876c3c..584fcb045a 100644 --- a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp +++ b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp @@ -222,6 +222,8 @@ private slots: void sqlite_real_data() { generic_data("QSQLITE"); } void sqlite_real(); + void aggregateFunctionTypes_data() { generic_data(); } + void aggregateFunctionTypes(); private: // returns all database connections void generic_data(const QString &engine=QString()); @@ -3339,5 +3341,121 @@ void tst_QSqlQuery::sqlite_real() QCOMPARE(q.value(0).toDouble(), 5.6); } +void tst_QSqlQuery::aggregateFunctionTypes() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + { + const QString tableName(qTableName("numericFunctionsWithIntValues", __FILE__)); + tst_Databases::safeDropTable( db, tableName ); + + QSqlQuery q(db); + QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id INTEGER)")); + + // First test without any entries + QVERIFY_SQL(q, exec("SELECT SUM(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.record().field(0).type(), QVariant::Invalid); + + QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id) VALUES (1)")); + QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id) VALUES (2)")); + + QVERIFY_SQL(q, exec("SELECT SUM(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toInt(), 3); + QCOMPARE(q.record().field(0).type(), QVariant::Int); + + QVERIFY_SQL(q, exec("SELECT AVG(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toDouble(), 1.5); + QCOMPARE(q.record().field(0).type(), QVariant::Double); + + QVERIFY_SQL(q, exec("SELECT COUNT(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toInt(), 2); + QCOMPARE(q.record().field(0).type(), QVariant::Int); + + QVERIFY_SQL(q, exec("SELECT MIN(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toInt(), 1); + QCOMPARE(q.record().field(0).type(), QVariant::Int); + + QVERIFY_SQL(q, exec("SELECT MAX(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toInt(), 2); + QCOMPARE(q.record().field(0).type(), QVariant::Int); + } + { + const QString tableName(qTableName("numericFunctionsWithDoubleValues", __FILE__)); + tst_Databases::safeDropTable( db, tableName ); + + QSqlQuery q(db); + QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id DOUBLE)")); + + // First test without any entries + QVERIFY_SQL(q, exec("SELECT SUM(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.record().field(0).type(), QVariant::Invalid); + + QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id) VALUES (1.5)")); + QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id) VALUES (2.5)")); + + QVERIFY_SQL(q, exec("SELECT SUM(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toDouble(), 4.0); + QCOMPARE(q.record().field(0).type(), QVariant::Double); + + QVERIFY_SQL(q, exec("SELECT AVG(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toDouble(), 2.0); + QCOMPARE(q.record().field(0).type(), QVariant::Double); + + QVERIFY_SQL(q, exec("SELECT COUNT(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toInt(), 2); + QCOMPARE(q.record().field(0).type(), QVariant::Int); + + QVERIFY_SQL(q, exec("SELECT MIN(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toDouble(), 1.5); + QCOMPARE(q.record().field(0).type(), QVariant::Double); + + QVERIFY_SQL(q, exec("SELECT MAX(id) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toDouble(), 2.5); + QCOMPARE(q.record().field(0).type(), QVariant::Double); + + QVERIFY_SQL(q, exec("SELECT ROUND(id, 1) FROM " + tableName + " WHERE id=1.5")); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toDouble(), 1.5); + QCOMPARE(q.record().field(0).type(), QVariant::Double); + + QVERIFY_SQL(q, exec("SELECT ROUND(id, 0) FROM " + tableName + " WHERE id=2.5")); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toDouble(), 3.0); + QCOMPARE(q.record().field(0).type(), QVariant::Double); + } + { + const QString tableName(qTableName("stringFunctions", __FILE__)); + tst_Databases::safeDropTable( db, tableName ); + + QSqlQuery q(db); + QVERIFY_SQL(q, exec("CREATE TABLE " + tableName + " (id INTEGER, txt VARCHAR(50))")); + + QVERIFY_SQL(q, exec("SELECT MAX(txt) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.record().field(0).type(), QVariant::Invalid); + + QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id, txt) VALUES (1, 'lower')")); + QVERIFY_SQL(q, exec("INSERT INTO " + tableName + " (id, txt) VALUES (2, 'upper')")); + + QVERIFY_SQL(q, exec("SELECT MAX(txt) FROM " + tableName)); + QVERIFY(q.next()); + QCOMPARE(q.value(0).toString(), QLatin1String("upper")); + QCOMPARE(q.record().field(0).type(), QVariant::String); + } +} + QTEST_MAIN( tst_QSqlQuery ) #include "tst_qsqlquery.moc" diff --git a/tests/auto/sql/models/qsqlrelationaltablemodel/tst_qsqlrelationaltablemodel.cpp b/tests/auto/sql/models/qsqlrelationaltablemodel/tst_qsqlrelationaltablemodel.cpp index ddafeea427..ce0d8db1fd 100644 --- a/tests/auto/sql/models/qsqlrelationaltablemodel/tst_qsqlrelationaltablemodel.cpp +++ b/tests/auto/sql/models/qsqlrelationaltablemodel/tst_qsqlrelationaltablemodel.cpp @@ -542,6 +542,13 @@ void tst_QSqlRelationalTableModel::setRecord() model.setSort(0, Qt::AscendingOrder); QVERIFY_SQL(model, submit()); + if (model.editStrategy() != QSqlTableModel::OnManualSubmit) { + QCOMPARE(model.data(model.index(1, 0)).toInt(), 7); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("tester")); + QCOMPARE(model.data(model.index(1, 2)).toString(), QString("herr")); + QVERIFY_SQL(model, select()); + } + QCOMPARE(model.data(model.index(3, 0)).toInt(), 7); QCOMPARE(model.data(model.index(3, 1)).toString(), QString("tester")); QCOMPARE(model.data(model.index(3, 2)).toString(), QString("herr")); @@ -599,6 +606,8 @@ void tst_QSqlRelationalTableModel::insertWithStrategies() QVERIFY_SQL(model, submitAll()); model.setEditStrategy(QSqlTableModel::OnManualSubmit); + // The changes were submitted, but there was no automatic select to resort + QVERIFY_SQL(model, select()); QCOMPARE(model.data(model.index(0,0)).toInt(), 1); QCOMPARE(model.data(model.index(0,1)).toString(), QString("harry")); @@ -1401,6 +1410,8 @@ void tst_QSqlRelationalTableModel::whiteSpaceInIdentifiers() QVERIFY_SQL(model, insertRecord(-1, rec)); model.submitAll(); + if (model.editStrategy() != QSqlTableModel::OnManualSubmit) + QVERIFY_SQL(model, select()); QCOMPARE(model.data(model.index(0, 0)).toInt(), 3); QCOMPARE(model.data(model.index(0, 1)).toString(), QString("Washington")); diff --git a/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp b/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp index 270de8292b..448111cd9f 100644 --- a/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp +++ b/tests/auto/sql/models/qsqltablemodel/tst_qsqltablemodel.cpp @@ -411,8 +411,11 @@ void tst_QSqlTableModel::setRecord() } else if ((QSqlTableModel::EditStrategy)submitpolicy == QSqlTableModel::OnRowChange && i == model.rowCount() -1) model.submit(); else { - // dataChanged() is not emitted when submitAll() is called - QCOMPARE(spy.count(), 1); + // dataChanged() emitted by selectRow() as well as setRecord() + if ((QSqlTableModel::EditStrategy)submitpolicy == QSqlTableModel::OnFieldChange) + QCOMPARE(spy.count(), 2); + else + QCOMPARE(spy.count(), 1); QCOMPARE(spy.at(0).count(), 2); QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(0)), model.index(i, 0)); QCOMPARE(qvariant_cast<QModelIndex>(spy.at(0).at(1)), model.index(i, rec.count() - 1)); @@ -471,8 +474,7 @@ void tst_QSqlTableModel::insertRow() rec.setValue(0, 42); rec.setValue(1, QString("francis")); - // FieldChange updates immediately and resorts - // Row/Manual submit does not resort + // Setting record does not cause resort QVERIFY(model.setRecord(2, rec)); QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); @@ -482,8 +484,23 @@ void tst_QSqlTableModel::insertRow() QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); QCOMPARE(model.data(model.index(1, 2)).toInt(), 2); - // See comment above setRecord - if (submitpolicy == QSqlTableModel::OnFieldChange) { + QCOMPARE(model.data(model.index(2, 0)).toInt(), 42); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("francis")); + QCOMPARE(model.data(model.index(2, 2)).toInt(), 2); + QCOMPARE(model.data(model.index(3, 0)).toInt(), 3); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(3, 2)).toInt(), 3); + + QVERIFY(model.submitAll()); + + if (submitpolicy == QSqlTableModel::OnManualSubmit) { + // After the submit we should have the resorted view + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); + QCOMPARE(model.data(model.index(1, 0)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(1, 2)).toInt(), 2); QCOMPARE(model.data(model.index(2, 0)).toInt(), 3); QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); QCOMPARE(model.data(model.index(2, 2)).toInt(), 3); @@ -491,6 +508,13 @@ void tst_QSqlTableModel::insertRow() QCOMPARE(model.data(model.index(3, 1)).toString(), QString("francis")); QCOMPARE(model.data(model.index(3, 2)).toInt(), 2); } else { + // Submit does not select, therefore not resorted + QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); + QCOMPARE(model.data(model.index(1, 0)).toInt(), 2); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(1, 2)).toInt(), 2); QCOMPARE(model.data(model.index(2, 0)).toInt(), 42); QCOMPARE(model.data(model.index(2, 1)).toString(), QString("francis")); QCOMPARE(model.data(model.index(2, 2)).toInt(), 2); @@ -499,9 +523,8 @@ void tst_QSqlTableModel::insertRow() QCOMPARE(model.data(model.index(3, 2)).toInt(), 3); } - QVERIFY(model.submitAll()); - - // After the submit we should have the resorted view + QVERIFY(model.select()); + // After the select we should have the resorted view in all strategies QCOMPARE(model.data(model.index(0, 0)).toInt(), 1); QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); QCOMPARE(model.data(model.index(0, 2)).toInt(), 1); @@ -514,7 +537,6 @@ void tst_QSqlTableModel::insertRow() QCOMPARE(model.data(model.index(3, 0)).toInt(), 42); QCOMPARE(model.data(model.index(3, 1)).toString(), QString("francis")); QCOMPARE(model.data(model.index(3, 2)).toInt(), 2); - } void tst_QSqlTableModel::insertRecord() @@ -647,8 +669,7 @@ void tst_QSqlTableModel::removeRow() QVERIFY_SQL(model, select()); QCOMPARE(model.rowCount(), 3); - // headerDataChanged must be emitted by the model when the edit strategy is OnManualSubmit, - // when OnFieldChange or OnRowChange it's not needed because the model will re-select. + // headerDataChanged must be emitted by the model since the row won't vanish until select qRegisterMetaType<Qt::Orientation>("Qt::Orientation"); QSignalSpy headerDataChangedSpy(&model, SIGNAL(headerDataChanged(Qt::Orientation, int, int))); @@ -673,7 +694,10 @@ void tst_QSqlTableModel::removeRow() headerDataChangedSpy.clear(); QVERIFY(model.removeRow(1)); - QCOMPARE(headerDataChangedSpy.count(), 0); + QCOMPARE(headerDataChangedSpy.count(), 1); + QCOMPARE(model.rowCount(), 3); + + QVERIFY_SQL(model, select()); QCOMPARE(model.rowCount(), 2); QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); @@ -706,6 +730,11 @@ void tst_QSqlTableModel::removeRows() QCOMPARE(beforeDeleteSpy.count(), 2); QVERIFY(beforeDeleteSpy.at(0).at(0).toInt() == 0); QVERIFY(beforeDeleteSpy.at(1).at(0).toInt() == 1); + // deleted rows shown as empty until select + QCOMPARE(model.rowCount(), 3); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("")); + QVERIFY(model.select()); + // deleted rows are gone QCOMPARE(model.rowCount(), 1); QCOMPARE(model.data(model.index(0, 1)).toString(), QString("vohi")); model.clear(); @@ -730,10 +759,10 @@ void tst_QSqlTableModel::removeRows() QSignalSpy headerDataChangedSpy(&model, SIGNAL(headerDataChanged(Qt::Orientation, int, int))); QVERIFY(model.removeRows(0, 2, QModelIndex())); QCOMPARE(headerDataChangedSpy.count(), 2); - QCOMPARE(headerDataChangedSpy.at(0).at(1).toInt(), 0); - QCOMPARE(headerDataChangedSpy.at(0).at(2).toInt(), 0); - QCOMPARE(headerDataChangedSpy.at(1).at(1).toInt(), 1); - QCOMPARE(headerDataChangedSpy.at(1).at(2).toInt(), 1); + QCOMPARE(headerDataChangedSpy.at(0).at(1).toInt(), 1); + QCOMPARE(headerDataChangedSpy.at(0).at(2).toInt(), 1); + QCOMPARE(headerDataChangedSpy.at(1).at(1).toInt(), 0); + QCOMPARE(headerDataChangedSpy.at(1).at(2).toInt(), 0); QCOMPARE(model.rowCount(), 3); QVERIFY(beforeDeleteSpy.count() == 0); QVERIFY(model.submitAll()); @@ -778,6 +807,14 @@ void tst_QSqlTableModel::removeInsertedRow() model.submitAll(); + if (model.editStrategy() != QSqlTableModel::OnManualSubmit) { + QCOMPARE(model.rowCount(), 4); + QCOMPARE(model.data(model.index(1, 0)).toInt(), 55); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("null columns")); + QCOMPARE(model.data(model.index(1, 2)).isNull(), true); + QVERIFY(model.select()); + } + QCOMPARE(model.rowCount(), 4); QCOMPARE(model.data(model.index(3, 0)).toInt(), 55); QCOMPARE(model.data(model.index(3, 1)).toString(), QString("null columns")); @@ -785,8 +822,17 @@ void tst_QSqlTableModel::removeInsertedRow() QVERIFY(model.removeRow(3)); model.submitAll(); - QCOMPARE(model.rowCount(), 3); + if (model.editStrategy() != QSqlTableModel::OnManualSubmit) { + QCOMPARE(model.rowCount(), 4); + QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); + QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); + QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); + QCOMPARE(model.data(model.index(3, 1)).toString(), QString("")); + QVERIFY(model.select()); + } + + QCOMPARE(model.rowCount(), 3); QCOMPARE(model.data(model.index(0, 1)).toString(), QString("harry")); QCOMPARE(model.data(model.index(1, 1)).toString(), QString("trond")); QCOMPARE(model.data(model.index(2, 1)).toString(), QString("vohi")); @@ -1129,6 +1175,11 @@ void tst_QSqlTableModel::insertRecordBeforeSelect() buffer.setValue("title", 0); QVERIFY_SQL(model, insertRecord(1, buffer)); + if (model.editStrategy() != QSqlTableModel::OnManualSubmit) { + QCOMPARE(model.rowCount(), 2); + QVERIFY_SQL(model, select()); + } + int rowCount = model.rowCount(); model.clear(); QCOMPARE(model.rowCount(), 0); diff --git a/tests/auto/tools/qmake/testdata/export_across_file_boundaries/oink.pri b/tests/auto/tools/qmake/testdata/export_across_file_boundaries/oink.pri index cbd2d0f7d0..ea58ef076e 100644 --- a/tests/auto/tools/qmake/testdata/export_across_file_boundaries/oink.pri +++ b/tests/auto/tools/qmake/testdata/export_across_file_boundaries/oink.pri @@ -1 +1,2 @@ +load(default_pre) setVar(BAR) diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp index af76b1c3c1..ee7ec7ba57 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp @@ -4338,9 +4338,16 @@ void tst_QGraphicsView::task255529_transformationAnchorMouseAndViewportMargins() QPointF newMouseScenePos = view.mapToScene(mouseViewPos); qreal slack = 1; - QEXPECT_FAIL("", "QTBUG-22455", Abort); - QVERIFY(qAbs(newMouseScenePos.x() - mouseScenePos.x()) < slack); - QVERIFY(qAbs(newMouseScenePos.y() - mouseScenePos.y()) < slack); + + const qreal dx = qAbs(newMouseScenePos.x() - mouseScenePos.x()); + const qreal dy = qAbs(newMouseScenePos.y() - mouseScenePos.y()); + const QByteArray message = QString::fromLatin1("QTBUG-22455, distance: dx=%1, dy=%2 slack=%3 (%4)."). + arg(dx).arg(dy).arg(slack).arg(qApp->style()->metaObject()->className()).toLocal8Bit(); + // This is highly unstable (observed to pass on Windows and some Linux configurations). +#ifdef Q_OS_MAC + QEXPECT_FAIL("", message.constData(), Abort); +#endif + QVERIFY2(dx < slack && dy < slack, message.constData()); #endif } diff --git a/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp b/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp index 0a2d0cd2f8..c6b2b49d98 100644 --- a/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp @@ -1158,9 +1158,6 @@ void tst_QGraphicsWidget::initStyleOption() QCOMPARE(hasFocus, focus); bool isUnderMouse = option.state & QStyle::State_MouseOver; #ifndef Q_OS_WINCE -# if !defined(Q_OS_MAC) && !defined(Q_OS_WIN) - QEXPECT_FAIL("all", "QTBUG-22457", Abort); -# endif QCOMPARE(isUnderMouse, underMouse); #endif // if (layoutDirection != Qt::LeftToRight) diff --git a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp index f139eac275..9bb08edcce 100644 --- a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp +++ b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp @@ -1830,10 +1830,11 @@ void tst_QListView::taskQTBUG_2233_scrollHiddenItems() QStringListModel model(&view); QStringList list; for (int i = 0; i < rowCount; ++i) - list << QString::fromAscii("Item %1").arg(i); + list << QString::number(i); model.setStringList(list); view.setModel(&model); + view.setUniformItemSizes(true); view.setViewMode(QListView::ListMode); for (int i = 0; i < rowCount / 2; ++i) view.setRowHidden(2 * i, true); diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index caf0f1f80d..dab9dd7690 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -5693,7 +5693,6 @@ private: void tst_QWidget::childEvents() { EventRecorder::EventList expected; - bool accessibilityEnabled = false; // Move away the cursor; otherwise it might result in an enter event if it's // inside the widget when the widget is shown. @@ -5735,8 +5734,6 @@ void tst_QWidget::childEvents() << qMakePair(&widget, QEvent::Resize) << qMakePair(&widget, QEvent::Show); - if (accessibilityEnabled) - expected << qMakePair(&widget, QEvent::AccessibilityPrepare); expected << qMakePair(&widget, QEvent::ShowToParent); QCOMPARE(spy.eventList(), expected); spy.clear(); @@ -5820,8 +5817,6 @@ void tst_QWidget::childEvents() << qMakePair(&widget, QEvent::Resize) << qMakePair(&widget, QEvent::Show); - if (accessibilityEnabled) - expected << qMakePair(&widget, QEvent::AccessibilityPrepare); expected << qMakePair(&widget, QEvent::ShowToParent); QCOMPARE(spy.eventList(), expected); spy.clear(); @@ -5908,8 +5903,6 @@ void tst_QWidget::childEvents() << qMakePair(&widget, QEvent::Resize) << qMakePair(&widget, QEvent::Show); - if (accessibilityEnabled) - expected << qMakePair(&widget, QEvent::AccessibilityPrepare); expected << qMakePair(&widget, QEvent::ShowToParent); QCOMPARE(spy.eventList(), expected); spy.clear(); diff --git a/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp b/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp index 1201b0f1da..544923a1c3 100644 --- a/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp +++ b/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp @@ -555,11 +555,11 @@ void tst_QStyleSheetStyle::layoutSpacing() void tst_QStyleSheetStyle::qproperty() { QPushButton pb; - pb.setStyleSheet("QPushButton { qproperty-text: hello; qproperty-checkable: true; qproperty-checked: 1}"); + pb.setStyleSheet("QPushButton { qproperty-text: hello; qproperty-checkable: 1; qproperty-checked: false}"); pb.ensurePolished(); QCOMPARE(pb.text(), QString("hello")); QCOMPARE(pb.isCheckable(), true); - QCOMPARE(pb.isChecked(), true); + QCOMPARE(pb.isChecked(), false); } namespace ns { @@ -781,10 +781,6 @@ void tst_QStyleSheetStyle::focusColors() + " did not contain background color #e8ff66, using style " + QString::fromLatin1(qApp->style()->metaObject()->className())) .toLocal8Bit().constData()); -#ifdef Q_OS_MAC - if (widget == widgets.first()) - QEXPECT_FAIL("", "Failure only for first widget, the QPushButton, see QTBUG-23686", Continue); -#endif QVERIFY2(testForColors(image, QColor(0xff, 0x00, 0x84)), (QString::fromLatin1(widget->metaObject()->className()) + " did not contain text color #ff0084, using style " @@ -882,8 +878,7 @@ void tst_QStyleSheetStyle::hoverColors() (QString::fromLatin1(widget->metaObject()->className()) + " did not contain background color #e8ff66").toLocal8Bit().constData()); #ifdef Q_OS_MAC - if (qobject_cast<QPushButton *>(widget) - || qobject_cast<QComboBox *>(widget)) + if (qobject_cast<QComboBox *>(widget)) QEXPECT_FAIL("", "Failure only for QPushButton and QComboBox, see QTBUG-23686", Continue); #endif QVERIFY2(testForColors(image, QColor(0xff, 0x00, 0x84)), diff --git a/tests/auto/widgets/widgets/qcalendarwidget/qcalendarwidget.pro b/tests/auto/widgets/widgets/qcalendarwidget/qcalendarwidget.pro index 127d78596a..f16110cbd1 100644 --- a/tests/auto/widgets/widgets/qcalendarwidget/qcalendarwidget.pro +++ b/tests/auto/widgets/widgets/qcalendarwidget/qcalendarwidget.pro @@ -2,6 +2,3 @@ CONFIG += testcase TARGET = tst_qcalendarwidget QT += widgets testlib SOURCES += tst_qcalendarwidget.cpp - -# QTBUG-23615 - unstable test -linux-*:system(". /etc/lsb-release && [ $DISTRIB_CODENAME = oneiric ]"):CONFIG += insignificant_test diff --git a/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp b/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp index 4f95f80525..4857900dc8 100644 --- a/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp +++ b/tests/auto/widgets/widgets/qcalendarwidget/tst_qcalendarwidget.cpp @@ -175,6 +175,8 @@ void tst_QCalendarWidget::buttonClickCheck() QSize size = object.sizeHint(); object.setGeometry(0,0,size.width(), size.height()); object.show(); + QTest::qWaitForWindowShown(&object); + object.activateWindow(); QDate selectedDate(2005, 1, 1); //click on the month buttons diff --git a/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp b/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp index 0f07546559..c6a039cb37 100644 --- a/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp +++ b/tests/auto/widgets/widgets/qmainwindow/tst_qmainwindow.cpp @@ -1435,6 +1435,11 @@ void MoveSeparator::apply(QMainWindow *mw) const } QVERIFY(!path.isEmpty()); + // For QTBUG-15689: + // Following commands is used to simulate user move action. A better way should + // use QMainWindowLayout::startSeparatorMove/separatorMove/endSeparatorMove, + // but then we will need real start/end position coordinates. + l->layoutState.dockAreaLayout.fallbackToSizeHints = false; l->layoutState.dockAreaLayout.separatorMove(path, QPoint(0, 0), QPoint(delta, delta)); } #endif diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp index 4fffd74491..473c5e05ec 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp @@ -562,9 +562,6 @@ void tst_QMenu::tearOff() QTest::mouseClick(menu, Qt::LeftButton, 0, QPoint(3, 3), 10); QTest::qWait(100); -#ifndef Q_OS_MAC - QEXPECT_FAIL("", "QTBUG-22565", Abort); -#endif QVERIFY(menu->isTearOffMenuVisible()); QPointer<QMenu> torn = 0; foreach (QWidget *w, QApplication::allWidgets()) { @@ -783,9 +780,6 @@ void tst_QMenu::task258920_mouseBorder() #ifdef Q_OS_WINCE_WM QSKIP("Mouse move related signals for Windows Mobile unavailable"); #endif - // ### fixme: Check platforms - QSKIP("QTBUG-20753 QCursor::setPos() / QTest::mouseMove() doesn't work on qpa"); - Menu258920 menu; // For styles which inherit from QWindowsStyle, styleHint(QStyle::SH_Menu_MouseTracking) is true. menu.setMouseTracking(true); diff --git a/tests/benchmarks/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/benchmarks/corelib/kernel/qmetatype/tst_qmetatype.cpp index a3cdd38e56..c80a946366 100644 --- a/tests/benchmarks/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/benchmarks/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -77,6 +77,9 @@ private slots: void constructCoreTypeCopy_data(); void constructCoreTypeCopy(); + void constructCustomType_data(); + void constructCustomType(); + void constructInPlace_data(); void constructInPlace(); void constructInPlaceCopy_data(); @@ -309,6 +312,24 @@ void tst_QMetaType::constructCoreTypeCopy() } } +void tst_QMetaType::constructCustomType_data() +{ + QTest::addColumn<int>("typeId"); + + QTest::newRow("BigClass") << qMetaTypeId<BigClass>(); +} + +void tst_QMetaType::constructCustomType() +{ + QFETCH(int, typeId); + QBENCHMARK { + for (int i = 0; i < 100000; ++i) { + void *data = QMetaType::create(typeId, (void *)0); + QMetaType::destroy(typeId, data); + } + } +} + void tst_QMetaType::constructInPlace_data() { constructCoreType_data(); diff --git a/tests/manual/qcursor/allcursors/allcursors.pro b/tests/manual/qcursor/allcursors/allcursors.pro index 8e7da30752..b9f6a0c98d 100644 --- a/tests/manual/qcursor/allcursors/allcursors.pro +++ b/tests/manual/qcursor/allcursors/allcursors.pro @@ -6,7 +6,7 @@ TARGET = tst_allcursors TEMPLATE = app - +QT = core gui widgets SOURCES += main.cpp\ mainwindow.cpp diff --git a/tests/manual/qcursor/allcursors/main.cpp b/tests/manual/qcursor/allcursors/main.cpp index 8872481e88..fab9a7d3d7 100644 --- a/tests/manual/qcursor/allcursors/main.cpp +++ b/tests/manual/qcursor/allcursors/main.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#include <QtGui/QApplication> +#include <QtWidgets/QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) diff --git a/tests/manual/qcursor/allcursors/mainwindow.h b/tests/manual/qcursor/allcursors/mainwindow.h index 938f744862..5908d26e17 100644 --- a/tests/manual/qcursor/allcursors/mainwindow.h +++ b/tests/manual/qcursor/allcursors/mainwindow.h @@ -42,7 +42,7 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H -#include <QtGui/QMainWindow> +#include <QtWidgets/QMainWindow> class QTimer; diff --git a/tests/manual/qcursor/grab_override/grab_override.pro b/tests/manual/qcursor/grab_override/grab_override.pro index c0f69be5ad..d84e2ee2b8 100644 --- a/tests/manual/qcursor/grab_override/grab_override.pro +++ b/tests/manual/qcursor/grab_override/grab_override.pro @@ -6,7 +6,7 @@ TARGET = t_cursors TEMPLATE = app - +QT = core gui widgets SOURCES += main.cpp\ mainwindow.cpp diff --git a/tests/manual/qcursor/grab_override/main.cpp b/tests/manual/qcursor/grab_override/main.cpp index 8872481e88..fab9a7d3d7 100644 --- a/tests/manual/qcursor/grab_override/main.cpp +++ b/tests/manual/qcursor/grab_override/main.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#include <QtGui/QApplication> +#include <QtWidgets/QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) diff --git a/tests/manual/qcursor/grab_override/mainwindow.h b/tests/manual/qcursor/grab_override/mainwindow.h index c231e9583d..3eb7e26fd7 100644 --- a/tests/manual/qcursor/grab_override/mainwindow.h +++ b/tests/manual/qcursor/grab_override/mainwindow.h @@ -42,7 +42,7 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H -#include <QtGui/QMainWindow> +#include <QtWidgets/QMainWindow> class QTimer; |