diff options
Diffstat (limited to 'src')
171 files changed, 6374 insertions, 1375 deletions
diff --git a/src/3rdparty/phonon/ds9/videorenderer_default.cpp b/src/3rdparty/phonon/ds9/videorenderer_default.cpp index 9d82255..0045a49 100644 --- a/src/3rdparty/phonon/ds9/videorenderer_default.cpp +++ b/src/3rdparty/phonon/ds9/videorenderer_default.cpp @@ -32,7 +32,7 @@ namespace Phonon { namespace DS9 { - VideoRendererDefault::~VideoRendererVMR9() + VideoRendererDefault::~VideoRendererDefault() { } diff --git a/src/3rdparty/phonon/ds9/videowidget.cpp b/src/3rdparty/phonon/ds9/videowidget.cpp index 59abb04..1eddaee 100644 --- a/src/3rdparty/phonon/ds9/videowidget.cpp +++ b/src/3rdparty/phonon/ds9/videowidget.cpp @@ -369,7 +369,7 @@ namespace Phonon } } #else - renderer = new VideoRendererVMR9(m_widget); + renderer = new VideoRendererDefault(m_widget); if (renderer->getFilter() == 0) { //instanciating the renderer might fail m_noNativeRendererSupported = true; diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index 2b4ab47..f5a9d16 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -161,7 +161,9 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_THREAD Q_GLOBAL_STATIC(QThreadStorage<QUnifiedTimer *>, unifiedTimer) +#endif QUnifiedTimer::QUnifiedTimer() : QObject(), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL), @@ -173,12 +175,17 @@ QUnifiedTimer::QUnifiedTimer() : QUnifiedTimer *QUnifiedTimer::instance() { QUnifiedTimer *inst; +#ifndef QT_NO_THREAD if (!unifiedTimer()->hasLocalData()) { inst = new QUnifiedTimer; unifiedTimer()->setLocalData(inst); } else { inst = unifiedTimer()->localData(); } +#else + static QUnifiedTimer unifiedTimer; + inst = &unifiedTimer; +#endif return inst; } @@ -242,7 +249,7 @@ void QUnifiedTimer::timerEvent(QTimerEvent *event) animationTimer.stop(); isPauseTimerActive = false; // invalidate the start reference time - time = QTime(); + time.invalidate(); } else { restartAnimationTimer(); if (!time.isValid()) { diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index ad35d89..8bc3224 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -58,6 +58,10 @@ #include <QtCore/qtimer.h> #include <private/qobject_p.h> +#ifdef Q_OS_WIN +#include <qt_windows.h> +#endif + #ifndef QT_NO_ANIMATION QT_BEGIN_NAMESPACE @@ -109,6 +113,61 @@ private: Q_DECLARE_PUBLIC(QAbstractAnimation) }; +class ElapsedTimer +{ +public: + ElapsedTimer() { + invalidate(); + } + + void invalidate() { + m_started = -1; + } + + bool isValid() const { + return m_started >= 0; + } + + void start() { + m_started = getTickCount_sys(); + } + + qint64 elapsed() const { + qint64 current = getTickCount_sys(); + qint64 delta = current - m_started; + if (delta < 0) + delta += getPeriod_sys(); //we wrapped around + return delta; + } + +private: + enum { + MSECS_PER_HOUR = 3600000, + MSECS_PER_MIN = 60000 + }; + + qint64 m_started; + + quint64 getPeriod_sys() const { +#ifdef Q_OS_WIN + return Q_UINT64_C(0x100000000); +#else + // fallback + return 86400 * 1000; +#endif + } + + qint64 getTickCount_sys() const { +#ifdef Q_OS_WIN + return ::GetTickCount(); +#else + // fallback + const QTime t = QTime::currentTime(); + return MSECS_PER_HOUR * t.hour() + MSECS_PER_MIN * t.minute() + 1000 * t.second() + t.msec(); +#endif + } +}; + class QUnifiedTimer : public QObject { @@ -162,7 +221,8 @@ private: // timer used to delay the check if we should start/stop the animation timer QBasicTimer startStopAnimationTimer; - QTime time; + ElapsedTimer time; + int lastTick; int timingInterval; int currentAnimationIdx; diff --git a/src/corelib/animation/qpropertyanimation.cpp b/src/corelib/animation/qpropertyanimation.cpp index cfd7cc2..37b79ba 100644 --- a/src/corelib/animation/qpropertyanimation.cpp +++ b/src/corelib/animation/qpropertyanimation.cpp @@ -118,7 +118,9 @@ void QPropertyAnimationPrivate::updateMetaProperty() propertyType = QVariant::Invalid; if (!targetValue->dynamicPropertyNames().contains(propertyName)) qWarning("QPropertyAnimation: you're trying to animate a non-existing property %s of your QObject", propertyName.constData()); - } + } else if (!targetValue->metaObject()->property(propertyIndex).isWritable()) { + qWarning("QPropertyAnimation: you're trying to animate the non-writable property %s of your QObject", propertyName.constData()); + } } void QPropertyAnimationPrivate::updateProperty(const QVariant &newValue) @@ -265,7 +267,9 @@ void QPropertyAnimation::updateState(QAbstractAnimation::State newState, QPropertyAnimation *animToStop = 0; { +#ifndef QT_NO_THREAD QMutexLocker locker(QMutexPool::globalInstanceGet(&staticMetaObject)); +#endif typedef QPair<QObject *, QByteArray> QPropertyAnimationPair; typedef QHash<QPropertyAnimationPair, QPropertyAnimation*> QPropertyAnimationHash; static QPropertyAnimationHash hash; diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index 115edbe..173802d 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -431,7 +431,9 @@ void QVariantAnimation::registerInterpolator(QVariantAnimation::Interpolator fun { // will override any existing interpolators QInterpolatorVector *interpolators = registeredInterpolators(); +#ifndef QT_NO_THREAD QMutexLocker locker(QMutexPool::globalInstanceGet(interpolators)); +#endif if (int(interpolationType) >= interpolators->count()) interpolators->resize(int(interpolationType) + 1); interpolators->replace(interpolationType, func); @@ -446,7 +448,9 @@ 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); diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp index 698ca9e..86845c7 100644 --- a/src/corelib/codecs/qtextcodec.cpp +++ b/src/corelib/codecs/qtextcodec.cpp @@ -1163,6 +1163,19 @@ QTextDecoder* QTextCodec::makeDecoder() const return new QTextDecoder(this); } +/*! + Creates a QTextDecoder with a specified \a flags to decode chunks + of \c{char *} data to create chunks of Unicode data. + + The caller is responsible for deleting the returned object. + + \since 4.7 +*/ +QTextDecoder* QTextCodec::makeDecoder(QTextCodec::ConversionFlags flags) const +{ + return new QTextDecoder(this, flags); +} + /*! Creates a QTextEncoder which stores enough state to encode chunks @@ -1176,6 +1189,19 @@ QTextEncoder* QTextCodec::makeEncoder() const } /*! + Creates a QTextEncoder with a specified \a flags to encode chunks + of Unicode data as \c{char *} data. + + The caller is responsible for deleting the returned object. + + \since 4.7 +*/ +QTextEncoder* QTextCodec::makeEncoder(QTextCodec::ConversionFlags flags) const +{ + return new QTextEncoder(this, flags); +} + +/*! \fn QByteArray QTextCodec::fromUnicode(const QChar *input, int number, ConverterState *state) const @@ -1316,6 +1342,17 @@ QString QTextCodec::toUnicode(const char *chars) const */ /*! + Constructs a text encoder for the given \a codec and conversion \a flags. + + \since 4.7 +*/ +QTextEncoder::QTextEncoder(const QTextCodec *codec, QTextCodec::ConversionFlags flags) + : c(codec), state() +{ + state.flags = flags; +} + +/*! Destroys the encoder. */ QTextEncoder::~QTextEncoder() @@ -1392,6 +1429,18 @@ QByteArray QTextEncoder::fromUnicode(const QString& uc, int& lenInOut) */ /*! + Constructs a text decoder for the given \a codec and conversion \a flags. + + \since 4.7 +*/ + +QTextDecoder::QTextDecoder(const QTextCodec *codec, QTextCodec::ConversionFlags flags) + : c(codec), state() +{ + state.flags = flags; +} + +/*! Destroys the decoder. */ QTextDecoder::~QTextDecoder() diff --git a/src/corelib/codecs/qtextcodec.h b/src/corelib/codecs/qtextcodec.h index a099dd9..5012b42 100644 --- a/src/corelib/codecs/qtextcodec.h +++ b/src/corelib/codecs/qtextcodec.h @@ -85,9 +85,6 @@ public: static QTextCodec *codecForUtfText(const QByteArray &ba); static QTextCodec *codecForUtfText(const QByteArray &ba, QTextCodec *defaultCodec); - QTextDecoder* makeDecoder() const; - QTextEncoder* makeEncoder() const; - bool canEncode(QChar) const; bool canEncode(const QString&) const; @@ -120,6 +117,12 @@ public: QByteArray fromUnicode(const QChar *in, int length, ConverterState *state = 0) const { return convertFromUnicode(in, length, state); } + // ### Qt 5: merge these functions. + QTextDecoder* makeDecoder() const; + QTextDecoder* makeDecoder(ConversionFlags flags) const; + QTextEncoder* makeEncoder() const; + QTextEncoder* makeEncoder(ConversionFlags flags) const; + virtual QByteArray name() const = 0; virtual QList<QByteArray> aliases() const; virtual int mibEnum() const = 0; @@ -157,6 +160,7 @@ class Q_CORE_EXPORT QTextEncoder { Q_DISABLE_COPY(QTextEncoder) public: explicit QTextEncoder(const QTextCodec *codec) : c(codec), state() {} + QTextEncoder(const QTextCodec *codec, QTextCodec::ConversionFlags flags); ~QTextEncoder(); QByteArray fromUnicode(const QString& str); QByteArray fromUnicode(const QChar *uc, int len); @@ -178,6 +182,7 @@ class Q_CORE_EXPORT QTextDecoder { Q_DISABLE_COPY(QTextDecoder) public: explicit QTextDecoder(const QTextCodec *codec) : c(codec), state() {} + QTextDecoder(const QTextCodec *codec, QTextCodec::ConversionFlags flags); ~QTextDecoder(); QString toUnicode(const char* chars, int len); QString toUnicode(const QByteArray &ba); diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index ffbaa28..b237659 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -275,7 +275,7 @@ namespace QT_NAMESPACE {} # endif #endif -#ifdef AUTODETECT_COCOA +#ifdef QT_AUTODETECT_COCOA # ifdef Q_OS_MAC64 # define QT_MAC_USE_COCOA 1 # define QT_BUILD_KEY QT_BUILD_KEY_COCOA @@ -672,8 +672,9 @@ namespace QT_NAMESPACE {} # define Q_ALIGNOF(type) __alignof__(type) # define Q_TYPEOF(expr) __typeof__(expr) # define Q_DECL_ALIGN(n) __attribute__((__aligned__(n))) -// using CC 5.9: Warning: attribute visibility is unsupported and will be skipped.. -//# define Q_DECL_EXPORT __attribute__((__visibility__("default"))) +# endif +# if __SUNPRO_CC >= 0x550 +# define Q_DECL_EXPORT __global # endif # if __SUNPRO_CC < 0x5a0 # define Q_NO_TEMPLATE_FRIENDS @@ -1540,6 +1541,7 @@ inline QT3_SUPPORT bool qt_winUnicode() { return true; } inline QT3_SUPPORT int qWinVersion() { return QSysInfo::WindowsVersion; } #endif +// ### Qt 5: remove Win9x support macros QT_WA and QT_WA_INLINE. #define QT_WA(unicode, ansi) unicode #define QT_WA_INLINE(unicode, ansi) (unicode) diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index 02a1586..2ed0e46 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -6,6 +6,7 @@ HEADERS += \ io/qbuffer.h \ io/qdatastream.h \ io/qdatastream_p.h \ + io/qdataurl_p.h \ io/qdebug.h \ io/qdir.h \ io/qdiriterator.h \ @@ -34,6 +35,7 @@ SOURCES += \ io/qabstractfileengine.cpp \ io/qbuffer.cpp \ io/qdatastream.cpp \ + io/qdataurl.cpp \ io/qdebug.cpp \ io/qdir.cpp \ io/qdiriterator.cpp \ diff --git a/src/corelib/io/qdataurl.cpp b/src/corelib/io/qdataurl.cpp new file mode 100644 index 0000000..9bb896e --- /dev/null +++ b/src/corelib/io/qdataurl.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qurl.h" +#include "private/qdataurl_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + + Decode a data: URL into its mimetype and payload. Returns a null string if + the URL could not be decoded. +*/ +Q_CORE_EXPORT QPair<QString, QByteArray> qDecodeDataUrl(const QUrl &uri) +{ + QString mimeType; + QByteArray payload; + + if (uri.scheme() == QLatin1String("data") && uri.host().isEmpty()) { + mimeType = QLatin1String("text/plain;charset=US-ASCII"); + + // the following would have been the correct thing, but + // reality often differs from the specification. People have + // data: URIs with ? and # + //QByteArray data = QByteArray::fromPercentEncoding(uri.encodedPath()); + QByteArray data = QByteArray::fromPercentEncoding(uri.toEncoded()); + + // remove the data: scheme + data.remove(0, 5); + + // parse it: + int pos = data.indexOf(','); + if (pos != -1) { + payload = data.mid(pos + 1); + data.truncate(pos); + data = data.trimmed(); + + // find out if the payload is encoded in Base64 + if (data.endsWith(";base64")) { + payload = QByteArray::fromBase64(payload); + data.chop(7); + } + + if (data.toLower().startsWith("charset")) { + int i = 7; // strlen("charset") + while (data.at(i) == ' ') + ++i; + if (data.at(i) == '=') + data.prepend("text/plain;"); + } + + if (!data.isEmpty()) + mimeType = QLatin1String(data.trimmed()); + + } + } + + return QPair<QString,QByteArray>(mimeType,payload); +} + +QT_END_NAMESPACE diff --git a/src/corelib/io/qdataurl_p.h b/src/corelib/io/qdataurl_p.h new file mode 100644 index 0000000..be5b10d --- /dev/null +++ b/src/corelib/io/qdataurl_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDATAURL_P_H +#define QDATAURL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qDecodeDataUrl. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qurl.h" +#include "QtCore/qbytearray.h" +#include "QtCore/qstring.h" +#include "QtCore/qpair.h" + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT QPair<QString, QByteArray> qDecodeDataUrl(const QUrl &url); + +QT_END_NAMESPACE + +#endif // QDATAURL_P_H diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index dc7f17e..250e5e5 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -52,13 +52,13 @@ #include "qregexp.h" #include "qvector.h" #include "qalgorithms.h" +#include "qvarlengtharray.h" + #ifdef QT_BUILD_CORE_LIB -# include "qresource.h" +# include "qresource.h" +# include "private/qcoreglobaldata_p.h" #endif -#include "qvarlengtharray.h" - -#include "private/qcoreglobaldata_p.h" #include <stdlib.h> QT_BEGIN_NAMESPACE @@ -83,12 +83,10 @@ static QString driveSpec(const QString &path) //************* QDirPrivate class QDirPrivate { - QDir *q_ptr; - Q_DECLARE_PUBLIC(QDir) - friend struct QScopedPointerDeleter<QDirPrivate>; -protected: - QDirPrivate(QDir*, const QDir *copy=0); + +public: + QDirPrivate(const QDir *copy = 0); ~QDirPrivate(); QString initFileEngine(const QString &file); @@ -96,7 +94,6 @@ protected: void updateFileLists() const; void sortFileList(QDir::SortFlags, QFileInfoList &, QStringList *, QFileInfoList *) const; -private: #ifdef QT3_SUPPORT QChar filterSepChar; bool matchAllDirs; @@ -109,28 +106,30 @@ private: sep = QChar(QLatin1Char(' ')); return sep; } - static inline QStringList splitFilters(const QString &nameFilter, QChar sep=0) { - if(sep == 0) + static inline QStringList splitFilters(const QString &nameFilter, QChar sep = 0) { + if (sep == 0) sep = getFilterSepChar(nameFilter); QStringList ret = nameFilter.split(sep); - for(int i = 0; i < ret.count(); i++) + for (int i = 0; i < ret.count(); ++i) ret[i] = ret[i].trimmed(); return ret; } struct Data { inline Data() - : ref(1), fileEngine(0) - { clear(); } + : ref(1), fileEngine(0), listsDirty(1) + {} inline Data(const Data ©) : ref(1), path(copy.path), nameFilters(copy.nameFilters), sort(copy.sort), - filters(copy.filters), fileEngine(0) - { clear(); } + filters(copy.filters), fileEngine(0), listsDirty(1) + {} inline ~Data() { delete fileEngine; } inline void clear() { listsDirty = 1; + files.clear(); + fileInfos.clear(); } mutable QAtomicInt ref; @@ -147,7 +146,6 @@ private: } *data; inline void setPath(const QString &p) { - detach(false); QString path = p; if ((path.endsWith(QLatin1Char('/')) || path.endsWith(QLatin1Char('\\'))) && path.length() > 1) { @@ -156,12 +154,9 @@ private: #endif path.truncate(path.length() - 1); } - if(!data->fileEngine || !QDir::isRelativePath(path)) - path = initFileEngine(path); - data->fileEngine->setFileName(path); + // set the path to be the qt friendly version so then we can operate on it using just / - data->path = data->fileEngine->fileName(QAbstractFileEngine::DefaultName); - data->clear(); + data->path = initFileEngine(path); } inline void reset() { detach(); @@ -170,18 +165,16 @@ private: void detach(bool createFileEngine = true); }; -QDirPrivate::QDirPrivate(QDir *qq, const QDir *copy) : q_ptr(qq) +QDirPrivate::QDirPrivate(const QDir *copy) #ifdef QT3_SUPPORT - , filterSepChar(0) - , matchAllDirs(false) + : filterSepChar(0), matchAllDirs(false) #endif { - if(copy) { + if (copy) { copy->d_func()->data->ref.ref(); data = copy->d_func()->data; } else { data = new QDirPrivate::Data; - data->clear(); } } @@ -190,18 +183,19 @@ QDirPrivate::~QDirPrivate() if (!data->ref.deref()) delete data; data = 0; - q_ptr = 0; } /* For sorting */ -struct QDirSortItem { +struct QDirSortItem +{ mutable QString filename_cache; mutable QString suffix_cache; QFileInfo item; }; -class QDirSortItemComparator { +class QDirSortItemComparator +{ int qt_cmp_si_sort_flags; public: QDirSortItemComparator(int flags) : qt_cmp_si_sort_flags(flags) {} @@ -240,7 +234,7 @@ bool QDirSortItemComparator::operator()(const QDirSortItem &n1, const QDirSortIt f2->suffix_cache = ic ? f2->item.suffix().toLower() : f2->item.suffix(); - r = qt_cmp_si_sort_flags & QDir::LocaleAware + r = qt_cmp_si_sort_flags & QDir::LocaleAware ? f1->suffix_cache.localeAwareCompare(f2->suffix_cache) : f1->suffix_cache.compare(f2->suffix_cache); } @@ -260,7 +254,7 @@ bool QDirSortItemComparator::operator()(const QDirSortItem &n1, const QDirSortIt f2->filename_cache = ic ? f2->item.fileName().toLower() : f2->item.fileName(); - r = qt_cmp_si_sort_flags & QDir::LocaleAware + r = qt_cmp_si_sort_flags & QDir::LocaleAware ? f1->filename_cache.localeAwareCompare(f2->filename_cache) : f1->filename_cache.compare(f2->filename_cache); } @@ -274,16 +268,13 @@ bool QDirSortItemComparator::operator()(const QDirSortItem &n1, const QDirSortIt inline void QDirPrivate::sortFileList(QDir::SortFlags sort, QFileInfoList &l, QStringList *names, QFileInfoList *infos) const { - if(names) - names->clear(); - if(infos) - infos->clear(); + // names and infos are always empty lists or 0 here int n = l.size(); - if(n > 0) { + if (n > 0) { if (n == 1 || (sort & QDir::SortByMask) == QDir::Unsorted) { - if(infos) + if (infos) *infos = l; - if(names) { + if (names) { for (int i = 0; i < n; ++i) names->append(l.at(i).fileName()); } @@ -291,13 +282,13 @@ inline void QDirPrivate::sortFileList(QDir::SortFlags sort, QFileInfoList &l, QScopedArrayPointer<QDirSortItem> si(new QDirSortItem[n]); for (int i = 0; i < n; ++i) si[i].item = l.at(i); - qSort(si.data(), si.data()+n, QDirSortItemComparator(sort)); + qSort(si.data(), si.data() + n, QDirSortItemComparator(sort)); // put them back in the list(s) - if(infos) { + if (infos) { for (int i = 0; i < n; ++i) infos->append(si[i].item); } - if(names) { + if (names) { for (int i = 0; i < n; ++i) names->append(si[i].item.fileName()); } @@ -307,7 +298,7 @@ inline void QDirPrivate::sortFileList(QDir::SortFlags sort, QFileInfoList &l, inline void QDirPrivate::updateFileLists() const { - if(data->listsDirty) { + if (data->listsDirty) { QFileInfoList l; QDirIterator it(data->path, data->nameFilters, data->filters); while (it.hasNext()) { @@ -319,12 +310,11 @@ inline void QDirPrivate::updateFileLists() const } } -QString QDirPrivate::initFileEngine(const QString &path) +inline QString QDirPrivate::initFileEngine(const QString &path) { detach(false); - delete data->fileEngine; - data->fileEngine = 0; data->clear(); + delete data->fileEngine; data->fileEngine = QAbstractFileEngine::create(path); return data->fileEngine->fileName(QAbstractFileEngine::DefaultName); } @@ -520,8 +510,7 @@ void QDirPrivate::detach(bool createFileEngine) \sa currentPath() */ - -QDir::QDir(const QString &path) : d_ptr(new QDirPrivate(this)) +QDir::QDir(const QString &path) : d_ptr(new QDirPrivate) { Q_D(QDir); d->setPath(path.isEmpty() ? QString::fromLatin1(".") : path); @@ -548,18 +537,17 @@ QDir::QDir(const QString &path) : d_ptr(new QDirPrivate(this)) \sa exists(), setPath(), setNameFilter(), setFilter(), setSorting() */ - QDir::QDir(const QString &path, const QString &nameFilter, - SortFlags sort, Filters filters) : d_ptr(new QDirPrivate(this)) + SortFlags sort, Filters filters) : d_ptr(new QDirPrivate) { Q_D(QDir); d->setPath(path.isEmpty() ? QString::fromLatin1(".") : path); d->data->nameFilters = QDir::nameFiltersFromString(nameFilter); bool empty = d->data->nameFilters.isEmpty(); - if(!empty) { + if (!empty) { empty = true; - for(int i = 0; i < d->data->nameFilters.size(); ++i) { - if(!d->data->nameFilters.at(i).isEmpty()) { + for (int i = 0; i < d->data->nameFilters.size(); ++i) { + if (!d->data->nameFilters.at(i).isEmpty()) { empty = false; break; } @@ -577,8 +565,7 @@ QDir::QDir(const QString &path, const QString &nameFilter, \sa operator=() */ - -QDir::QDir(const QDir &dir) : d_ptr(new QDirPrivate(this, &dir)) +QDir::QDir(const QDir &dir) : d_ptr(new QDirPrivate(&dir)) { } @@ -586,7 +573,6 @@ QDir::QDir(const QDir &dir) : d_ptr(new QDirPrivate(this, &dir)) Destroys the QDir object frees up its resources. This has no effect on the underlying directory in the file system. */ - QDir::~QDir() { } @@ -607,7 +593,6 @@ QDir::~QDir() \sa path(), absolutePath(), exists(), cleanPath(), dirName(), absoluteFilePath(), isRelative(), makeAbsolute() */ - void QDir::setPath(const QString &path) { Q_D(QDir); @@ -624,7 +609,6 @@ void QDir::setPath(const QString &path) \sa setPath(), absolutePath(), exists(), cleanPath(), dirName(), absoluteFilePath(), toNativeSeparators(), makeAbsolute() */ - QString QDir::path() const { Q_D(const QDir); @@ -639,7 +623,6 @@ QString QDir::path() const \sa setPath(), canonicalPath(), exists(), cleanPath(), dirName(), absoluteFilePath() */ - QString QDir::absolutePath() const { Q_D(const QDir); @@ -649,7 +632,6 @@ QString QDir::absolutePath() const return cleanPath(ret); } - /*! Returns the canonical path, i.e. a path without symbolic links or redundant "." or ".." elements. @@ -666,12 +648,11 @@ QString QDir::absolutePath() const \sa path(), absolutePath(), exists(), cleanPath(), dirName(), absoluteFilePath() */ - QString QDir::canonicalPath() const { Q_D(const QDir); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); return cleanPath(d->data->fileEngine->fileName(QAbstractFileEngine::CanonicalName)); } @@ -687,7 +668,6 @@ QString QDir::canonicalPath() const \sa path(), filePath(), absolutePath(), absoluteFilePath() */ - QString QDir::dirName() const { Q_D(const QDir); @@ -706,7 +686,6 @@ QString QDir::dirName() const \sa dirName() absoluteFilePath(), isRelative(), canonicalPath() */ - QString QDir::filePath(const QString &fileName) const { Q_D(const QDir); @@ -714,7 +693,7 @@ QString QDir::filePath(const QString &fileName) const return QString(fileName); QString ret = d->data->path; - if(!fileName.isEmpty()) { + if (!fileName.isEmpty()) { if (!ret.isEmpty() && ret[(int)ret.length()-1] != QLatin1Char('/') && fileName[0] != QLatin1Char('/')) ret += QLatin1Char('/'); ret += fileName; @@ -730,13 +709,12 @@ QString QDir::filePath(const QString &fileName) const \sa relativeFilePath() filePath() canonicalPath() */ - QString QDir::absoluteFilePath(const QString &fileName) const { Q_D(const QDir); if (isAbsolutePath(fileName)) return fileName; - if(!d->data->fileEngine) + if (!d->data->fileEngine) return fileName; QString ret; @@ -744,7 +722,7 @@ QString QDir::absoluteFilePath(const QString &fileName) const if (isRelativePath(d->data->path)) //get pwd ret = QFSFileEngine::currentPath(fileName); #endif - if(!d->data->path.isEmpty() && d->data->path != QLatin1String(".")) { + if (!d->data->path.isEmpty() && d->data->path != QLatin1String(".")) { if (!ret.isEmpty() && !ret.endsWith(QLatin1Char('/'))) ret += QLatin1Char('/'); ret += d->data->path; @@ -764,7 +742,6 @@ QString QDir::absoluteFilePath(const QString &fileName) const \sa absoluteFilePath() filePath() canonicalPath() */ - QString QDir::relativeFilePath(const QString &fileName) const { QString dir = absolutePath(); @@ -851,7 +828,7 @@ QString QDir::toNativeSeparators(const QString &pathName) { QString n(pathName); #if defined(Q_FS_FAT) || defined(Q_OS_OS2EMX) || defined(Q_OS_SYMBIAN) - for (int i=0; i<(int)n.length(); i++) { + for (int i = 0; i < (int)n.length(); ++i) { if (n[i] == QLatin1Char('/')) n[i] = QLatin1Char('\\'); } @@ -875,7 +852,7 @@ QString QDir::fromNativeSeparators(const QString &pathName) { QString n(pathName); #if defined(Q_FS_FAT) || defined(Q_OS_OS2EMX) || defined(Q_OS_SYMBIAN) - for (int i=0; i<(int)n.length(); i++) { + for (int i = 0; i < (int)n.length(); ++i) { if (n[i] == QLatin1Char('\\')) n[i] = QLatin1Char('/'); } @@ -894,7 +871,6 @@ QString QDir::fromNativeSeparators(const QString &pathName) \sa cdUp(), isReadable(), exists(), path() */ - bool QDir::cd(const QString &dirName) { Q_D(QDir); @@ -930,14 +906,13 @@ bool QDir::cd(const QString &dirName) } } } - { - QFileInfo fi(newPath); - if (!(fi.exists() && fi.isDir())) - return false; - } - d->setPath(newPath); - refresh(); + QDir dir(*this); + dir.setPath(newPath); + if (!dir.exists()) + return false; + + *this = dir; return true; } @@ -951,7 +926,6 @@ bool QDir::cd(const QString &dirName) \sa cd(), isReadable(), exists(), path() */ - bool QDir::cdUp() { return cd(QString::fromLatin1("..")); @@ -960,7 +934,6 @@ bool QDir::cdUp() /*! Returns the string list set by setNameFilters() */ - QStringList QDir::nameFilters() const { Q_D(const QDir); @@ -983,10 +956,10 @@ QStringList QDir::nameFilters() const \sa nameFilters(), setFilter() */ - void QDir::setNameFilters(const QStringList &nameFilters) { Q_D(QDir); + d->detach(); d->data->nameFilters = nameFilters; } @@ -1001,7 +974,6 @@ void QDir::setNameFilters(const QStringList &nameFilters) \sa {The Qt Resource System}, QResource::addSearchPath() */ - void QDir::addResourceSearchPath(const QString &path) { #ifdef QT_BUILD_CORE_LIB @@ -1039,7 +1011,7 @@ void QDir::setSearchPaths(const QString &prefix, const QStringList &searchPaths) return; } - for (int i = 0; i < prefix.count(); i++) { + for (int i = 0; i < prefix.count(); ++i) { if (!prefix.at(i).isLetterOrNumber()) { qWarning("QDir::setSearchPaths: Prefix can only contain letters or numbers"); return; @@ -1089,7 +1061,6 @@ QStringList QDir::searchPaths(const QString &prefix) /*! Returns the value set by setFilter() */ - QDir::Filters QDir::filter() const { Q_D(const QDir); @@ -1171,7 +1142,6 @@ QDir::Filters QDir::filter() const \sa filter(), setNameFilters() */ - void QDir::setFilter(Filters filters) { Q_D(QDir); @@ -1185,7 +1155,6 @@ void QDir::setFilter(Filters filters) \sa setSorting() SortFlag */ - QDir::SortFlags QDir::sorting() const { Q_D(const QDir); @@ -1231,7 +1200,6 @@ QDir::SortFlags QDir::sorting() const \sa sorting() SortFlag */ - void QDir::setSorting(SortFlags sort) { Q_D(QDir); @@ -1240,7 +1208,6 @@ void QDir::setSorting(SortFlags sort) d->data->sort = sort; } - /*! Returns the total number of directories and files in the directory. @@ -1248,7 +1215,6 @@ void QDir::setSorting(SortFlags sort) \sa operator[](), entryList() */ - uint QDir::count() const { Q_D(const QDir); @@ -1260,13 +1226,10 @@ uint QDir::count() const /*! Returns the file name at position \a pos in the list of file names. Equivalent to entryList().at(index). - - Returns an empty string if \a pos is out of range or if the - entryList() function failed. + \a pos must be a valid index position in the list (i.e., 0 <= pos < count()). \sa count(), entryList() */ - QString QDir::operator[](int pos) const { Q_D(const QDir); @@ -1294,7 +1257,6 @@ QString QDir::operator[](int pos) const \sa entryInfoList(), setNameFilters(), setSorting(), setFilter() */ - QStringList QDir::entryList(Filters filters, SortFlags sort) const { Q_D(const QDir); @@ -1342,7 +1304,6 @@ QFileInfoList QDir::entryInfoList(Filters filters, SortFlags sort) const \sa entryInfoList(), setNameFilters(), setSorting(), setFilter() */ - QStringList QDir::entryList(const QStringList &nameFilters, Filters filters, SortFlags sort) const { @@ -1356,10 +1317,12 @@ QStringList QDir::entryList(const QStringList &nameFilters, Filters filters, #endif if (sort == NoSort) sort = d->data->sort; - if (filters == NoFilter && sort == NoSort && nameFilters == d->data->nameFilters) { + + if (filters == d->data->filters && sort == d->data->sort && nameFilters == d->data->nameFilters) { d->updateFileLists(); return d->data->files; } + QFileInfoList l; QDirIterator it(d->data->path, nameFilters, filters); while (it.hasNext()) { @@ -1387,7 +1350,6 @@ QStringList QDir::entryList(const QStringList &nameFilters, Filters filters, \sa entryList(), setNameFilters(), setSorting(), setFilter(), isReadable(), exists() */ - QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filters, SortFlags sort) const { @@ -1401,10 +1363,12 @@ QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filter #endif if (sort == NoSort) sort = d->data->sort; - if (filters == NoFilter && sort == NoSort && nameFilters == d->data->nameFilters) { + + if (filters == d->data->filters && sort == d->data->sort && nameFilters == d->data->nameFilters) { d->updateFileLists(); return d->data->fileInfos; } + QFileInfoList l; QDirIterator it(d->data->path, nameFilters, filters); while (it.hasNext()) { @@ -1423,7 +1387,6 @@ QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filter \sa rmdir() */ - bool QDir::mkdir(const QString &dirName) const { Q_D(const QDir); @@ -1432,7 +1395,7 @@ bool QDir::mkdir(const QString &dirName) const qWarning("QDir::mkdir: Empty or null file name(s)"); return false; } - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; QString fn = filePath(dirName); @@ -1448,7 +1411,6 @@ bool QDir::mkdir(const QString &dirName) const \sa mkdir() */ - bool QDir::rmdir(const QString &dirName) const { Q_D(const QDir); @@ -1457,7 +1419,7 @@ bool QDir::rmdir(const QString &dirName) const qWarning("QDir::rmdir: Empty or null file name(s)"); return false; } - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; QString fn = filePath(dirName); @@ -1474,7 +1436,6 @@ bool QDir::rmdir(const QString &dirName) const \sa rmpath() */ - bool QDir::mkpath(const QString &dirPath) const { Q_D(const QDir); @@ -1483,7 +1444,7 @@ bool QDir::mkpath(const QString &dirPath) const qWarning("QDir::mkpath: Empty or null file name(s)"); return false; } - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; QString fn = filePath(dirPath); @@ -1509,7 +1470,7 @@ bool QDir::rmpath(const QString &dirPath) const qWarning("QDir::rmpath: Empty or null file name(s)"); return false; } - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; QString fn = filePath(dirPath); @@ -1525,17 +1486,16 @@ bool QDir::rmpath(const QString &dirPath) const \sa QFileInfo::isReadable() */ - - bool QDir::isReadable() const { Q_D(const QDir); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; - const QAbstractFileEngine::FileFlags info = d->data->fileEngine->fileFlags(QAbstractFileEngine::DirectoryType - |QAbstractFileEngine::PermsMask); - if(!(info & QAbstractFileEngine::DirectoryType)) + const QAbstractFileEngine::FileFlags info = + d->data->fileEngine->fileFlags(QAbstractFileEngine::DirectoryType + | QAbstractFileEngine::PermsMask); + if (!(info & QAbstractFileEngine::DirectoryType)) return false; return info & QAbstractFileEngine::ReadUserPerm; } @@ -1551,19 +1511,17 @@ bool QDir::isReadable() const \sa QFileInfo::exists(), QFile::exists() */ - bool QDir::exists() const { Q_D(const QDir); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; const QAbstractFileEngine::FileFlags info = - d->data->fileEngine->fileFlags( - QAbstractFileEngine::DirectoryType - | QAbstractFileEngine::ExistsFlag - | QAbstractFileEngine::Refresh); - if(!(info & QAbstractFileEngine::DirectoryType)) + d->data->fileEngine->fileFlags(QAbstractFileEngine::DirectoryType + | QAbstractFileEngine::ExistsFlag + | QAbstractFileEngine::Refresh); + if (!(info & QAbstractFileEngine::DirectoryType)) return false; return info & QAbstractFileEngine::ExistsFlag; } @@ -1580,12 +1538,11 @@ bool QDir::exists() const \sa root(), rootPath() */ - bool QDir::isRoot() const { Q_D(const QDir); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return true; return d->data->fileEngine->fileFlags(QAbstractFileEngine::FlagsMask) & QAbstractFileEngine::RootFlag; } @@ -1615,12 +1572,11 @@ bool QDir::isRoot() const \sa makeAbsolute() isAbsolute() isAbsolutePath() cleanPath() */ - bool QDir::isRelative() const { Q_D(const QDir); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; return d->data->fileEngine->isRelativePath(); } @@ -1633,20 +1589,19 @@ bool QDir::isRelative() const \sa isAbsolute() isAbsolutePath() isRelative() cleanPath() */ - bool QDir::makeAbsolute() // ### What do the return values signify? { Q_D(QDir); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; QString absolutePath = d->data->fileEngine->fileName(QAbstractFileEngine::AbsoluteName); - if(QDir::isRelativePath(absolutePath)) + if (QDir::isRelativePath(absolutePath)) return false; d->detach(); d->data->path = absolutePath; d->data->fileEngine->setFileName(absolutePath); - if(!(d->data->fileEngine->fileFlags(QAbstractFileEngine::TypesMask) & QAbstractFileEngine::DirectoryType)) + if (!(d->data->fileEngine->fileFlags(QAbstractFileEngine::TypesMask) & QAbstractFileEngine::DirectoryType)) return false; return true; } @@ -1660,22 +1615,21 @@ bool QDir::makeAbsolute() // ### What do the return values signify? \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 10 */ - bool QDir::operator==(const QDir &dir) const { const QDirPrivate *d = d_func(); const QDirPrivate *other = dir.d_func(); - if(d->data == other->data) + if (d->data == other->data) return true; Q_ASSERT(d->data->fileEngine && other->data->fileEngine); - if(d->data->fileEngine->caseSensitive() != other->data->fileEngine->caseSensitive()) + if (d->data->fileEngine->caseSensitive() != other->data->fileEngine->caseSensitive()) return false; - if(d->data->filters == other->data->filters + if (d->data->filters == other->data->filters && d->data->sort == other->data->sort && d->data->nameFilters == other->data->nameFilters) { QString dir1 = absolutePath(), dir2 = dir.absolutePath(); - if(!other->data->fileEngine->caseSensitive()) + if (!other->data->fileEngine->caseSensitive()) return (dir1.toLower() == dir2.toLower()); return (dir1 == dir2); @@ -1688,7 +1642,6 @@ bool QDir::operator==(const QDir &dir) const Makes a copy of the \a dir object and assigns it to this QDir object. */ - QDir &QDir::operator=(const QDir &dir) { if (this == &dir) @@ -1707,7 +1660,6 @@ QDir &QDir::operator=(const QDir &dir) Use setPath() instead. */ - QDir &QDir::operator=(const QString &path) { Q_D(QDir); @@ -1728,22 +1680,19 @@ QDir &QDir::operator=(const QString &path) \snippet doc/src/snippets/code/src_corelib_io_qdir.cpp 11 */ - /*! Removes the file, \a fileName. Returns true if the file is removed successfully; otherwise returns false. */ - bool QDir::remove(const QString &fileName) { if (fileName.isEmpty()) { qWarning("QDir::remove: Empty or null file name"); return false; } - QString p = filePath(fileName); - return QFile::remove(p); + return QFile::remove(filePath(fileName)); } /*! @@ -1757,7 +1706,6 @@ bool QDir::remove(const QString &fileName) fail. For example, on at least one file system rename() fails if \a newName points to an open file. */ - bool QDir::rename(const QString &oldName, const QString &newName) { Q_D(QDir); @@ -1766,11 +1714,11 @@ bool QDir::rename(const QString &oldName, const QString &newName) qWarning("QDir::rename: Empty or null file name(s)"); return false; } - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; QFile file(filePath(oldName)); - if(!file.exists()) + if (!file.exists()) return false; return file.rename(filePath(newName)); } @@ -1785,15 +1733,13 @@ bool QDir::rename(const QString &oldName, const QString &newName) \sa QFileInfo::exists(), QFile::exists() */ - bool QDir::exists(const QString &name) const { if (name.isEmpty()) { qWarning("QDir::exists: Empty or null file name"); return false; } - QString tmp = filePath(name); - return QFile::exists(tmp); + return QFile::exists(filePath(name)); } /*! @@ -1805,7 +1751,6 @@ bool QDir::exists(const QString &name) const \sa root(), rootPath() */ - QFileInfoList QDir::drives() { #ifdef QT_NO_FSFILEENGINE @@ -1825,7 +1770,6 @@ QFileInfoList QDir::drives() user using their operating system's separator use toNativeSeparators(). */ - QChar QDir::separator() { #if defined (Q_FS_FAT) || defined(Q_WS_WIN) || defined(Q_OS_SYMBIAN) @@ -1844,9 +1788,8 @@ QChar QDir::separator() Returns true if the directory was successfully changed; otherwise returns false. - \sa current() currentPath() home() root() temp() + \sa current(), currentPath(), home(), root(), temp() */ - bool QDir::setCurrent(const QString &path) { #ifdef QT_NO_FSFILEENGINE @@ -1865,13 +1808,13 @@ bool QDir::setCurrent(const QString &path) The directory is constructed using the absolute path of the current directory, ensuring that its path() will be the same as its absolutePath(). - \sa currentPath(), home(), root(), temp() + \sa currentPath(), setCurrent(), home(), root(), temp() */ /*! Returns the absolute path of the application's current directory. - \sa current(), homePath(), rootPath(), tempPath() + \sa current(), setCurrent(), homePath(), rootPath(), tempPath() */ QString QDir::currentPath() { @@ -1888,7 +1831,7 @@ QString QDir::currentPath() Use currentPath() instead. - \sa currentPath() + \sa currentPath(), setCurrent() */ /*! @@ -1945,14 +1888,14 @@ QString QDir::homePath() } /*! - \fn QString QDir::homeDirPath() + \fn QString QDir::homeDirPath() - Returns the absolute path of the user's home directory. + Returns the absolute path of the user's home directory. - Use homePath() instead. + Use homePath() instead. - \sa homePath() - */ + \sa homePath() +*/ /*! \fn QDir QDir::temp() @@ -2018,13 +1961,13 @@ QString QDir::rootPath() } /*! - \fn QString QDir::rootDirPath() + \fn QString QDir::rootDirPath() - Returns the absolute path of the root directory. + Returns the absolute path of the root directory. - Use rootPath() instead. + Use rootPath() instead. - \sa rootPath() + \sa rootPath() */ #ifndef QT_NO_REGEXP @@ -2037,11 +1980,9 @@ QString QDir::rootPath() \sa {QRegExp wildcard matching}, QRegExp::exactMatch() entryList() entryInfoList() */ - - bool QDir::match(const QStringList &filters, const QString &fileName) { - for(QStringList::ConstIterator sit = filters.begin(); sit != filters.end(); ++sit) { + for (QStringList::ConstIterator sit = filters.constBegin(); sit != filters.constEnd(); ++sit) { QRegExp rx(*sit, Qt::CaseInsensitive, QRegExp::Wildcard); if (rx.exactMatch(fileName)) return true; @@ -2057,12 +1998,11 @@ bool QDir::match(const QStringList &filters, const QString &fileName) \sa {QRegExp wildcard matching}, QRegExp::exactMatch() entryList() entryInfoList() */ - bool QDir::match(const QString &filter, const QString &fileName) { return match(nameFiltersFromString(filter), fileName); } -#endif +#endif // QT_NO_REGEXP /*! Removes all multiple directory separators "/" and resolves any @@ -2075,15 +2015,14 @@ bool QDir::match(const QString &filter, const QString &fileName) \sa absolutePath() canonicalPath() */ - QString QDir::cleanPath(const QString &path) { if (path.isEmpty()) return path; QString name = path; QChar dir_separator = separator(); - if(dir_separator != QLatin1Char('/')) - name.replace(dir_separator, QLatin1Char('/')); + if (dir_separator != QLatin1Char('/')) + name.replace(dir_separator, QLatin1Char('/')); int used = 0, levels = 0; const int len = name.length(); @@ -2091,27 +2030,27 @@ QString QDir::cleanPath(const QString &path) QChar *out = outVector.data(); const QChar *p = name.unicode(); - for(int i = 0, last = -1, iwrite = 0; i < len; i++) { - if(p[i] == QLatin1Char('/')) { - while(i < len-1 && p[i+1] == QLatin1Char('/')) { + for (int i = 0, last = -1, iwrite = 0; i < len; ++i) { + if (p[i] == QLatin1Char('/')) { + while (i < len-1 && p[i+1] == QLatin1Char('/')) { #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) //allow unc paths - if(!i) + if (!i) break; #endif i++; } bool eaten = false; - if(i < len - 1 && p[i+1] == QLatin1Char('.')) { + if (i < len - 1 && p[i+1] == QLatin1Char('.')) { int dotcount = 1; - if(i < len - 2 && p[i+2] == QLatin1Char('.')) + if (i < len - 2 && p[i+2] == QLatin1Char('.')) dotcount++; - if(i == len - dotcount - 1) { - if(dotcount == 1) { + if (i == len - dotcount - 1) { + if (dotcount == 1) { break; - } else if(levels) { - if(last == -1) { - for(int i2 = iwrite-1; i2 >= 0; i2--) { - if(out[i2] == QLatin1Char('/')) { + } else if (levels) { + if (last == -1) { + for (int i2 = iwrite-1; i2 >= 0; i2--) { + if (out[i2] == QLatin1Char('/')) { last = i2; break; } @@ -2120,11 +2059,11 @@ QString QDir::cleanPath(const QString &path) used -= iwrite - last - 1; break; } - } else if(p[i+dotcount+1] == QLatin1Char('/')) { - if(dotcount == 2 && levels) { - if(last == -1 || iwrite - last == 1) { - for(int i2 = (last == -1) ? (iwrite-1) : (last-1); i2 >= 0; i2--) { - if(out[i2] == QLatin1Char('/')) { + } else if (p[i+dotcount+1] == QLatin1Char('/')) { + if (dotcount == 2 && levels) { + if (last == -1 || iwrite - last == 1) { + for (int i2 = (last == -1) ? (iwrite-1) : (last-1); i2 >= 0; i2--) { + if (out[i2] == QLatin1Char('/')) { eaten = true; last = i2; break; @@ -2133,7 +2072,7 @@ QString QDir::cleanPath(const QString &path) } else { eaten = true; } - if(eaten) { + if (eaten) { levels--; used -= iwrite - last; iwrite = last; @@ -2145,38 +2084,38 @@ QString QDir::cleanPath(const QString &path) iwrite = qMax(0, last); last = -1; ++i; - } else if(dotcount == 1) { + } else if (dotcount == 1) { eaten = true; } - if(eaten) + if (eaten) i += dotcount; } else { levels++; } - } else if(last != -1 && iwrite - last == 1) { + } else if (last != -1 && iwrite - last == 1) { #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) eaten = (iwrite > 2); #else eaten = true; #endif last = -1; - } else if(last != -1 && i == len-1) { + } else if (last != -1 && i == len-1) { eaten = true; } else { levels++; } - if(!eaten) + if (!eaten) last = i - (i - iwrite); else continue; - } else if(!i && p[i] == QLatin1Char('.')) { + } else if (!i && p[i] == QLatin1Char('.')) { int dotcount = 1; - if(len >= 1 && p[1] == QLatin1Char('.')) + if (len >= 1 && p[1] == QLatin1Char('.')) dotcount++; - if(len >= dotcount && p[dotcount] == QLatin1Char('/')) { - if(dotcount == 1) { + if (len >= dotcount && p[dotcount] == QLatin1Char('/')) { + if (dotcount == 1) { i++; - while(i+1 < len-1 && p[i+1] == QLatin1Char('/')) + while (i+1 < len-1 && p[i+1] == QLatin1Char('/')) i++; continue; } @@ -2185,16 +2124,15 @@ QString QDir::cleanPath(const QString &path) out[iwrite++] = p[i]; used++; } - QString ret; - if(used == len) - ret = name; - else - ret = QString(out, used); + QString ret = (used == len ? name : QString(out, used)); // Strip away last slash except for root directories - if (ret.endsWith(QLatin1Char('/')) - && !(ret.size() == 1 || (ret.size() == 3 && ret.at(1) == QLatin1Char(':')))) - ret = ret.left(ret.length() - 1); + if (ret.length() > 1 && ret.endsWith(QLatin1Char('/'))) { +#ifdef Q_OS_WIN + if (!(ret.length() == 3 && ret.at(1) == QLatin1Char(':'))) +#endif + ret.chop(1); + } return ret; } @@ -2205,7 +2143,6 @@ QString QDir::cleanPath(const QString &path) \sa isRelative() isAbsolutePath() makeAbsolute() */ - bool QDir::isRelativePath(const QString &path) { return QFileInfo(path).isRelative(); @@ -2214,7 +2151,6 @@ bool QDir::isRelativePath(const QString &path) /*! Refreshes the directory information. */ - void QDir::refresh() const { Q_D(const QDir); @@ -2229,7 +2165,6 @@ void QDir::refresh() const there is more than one filter, each pair of filters is separated by a space or by a semicolon.) */ - QStringList QDir::nameFiltersFromString(const QString &nameFilter) { return QDirPrivate::splitFilters(nameFilter); @@ -2421,7 +2356,8 @@ void QDir::setNameFilter(const QString &nameFilter) Use QDir::SortFlags instead. */ -#endif +#endif // QT3_SUPPORT + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, QDir::Filters filters) { @@ -2484,9 +2420,6 @@ QDebug operator<<(QDebug debug, const QDir &dir) << ')'; return debug.space(); } - - - -#endif +#endif // QT_NO_DEBUG_STREAM QT_END_NAMESPACE diff --git a/src/corelib/io/qdir.h b/src/corelib/io/qdir.h index 6be4922..186dd2f 100644 --- a/src/corelib/io/qdir.h +++ b/src/corelib/io/qdir.h @@ -83,7 +83,7 @@ public: Modified = 0x080, Hidden = 0x100, System = 0x200, - + AccessMask = 0x3F0, AllDirs = 0x400, @@ -215,6 +215,7 @@ public: static bool match(const QStringList &filters, const QString &fileName); static bool match(const QString &filter, const QString &fileName); #endif + static QString cleanPath(const QString &path); void refresh() const; @@ -246,7 +247,7 @@ public: inline QT3_SUPPORT static QString homeDirPath() { return homePath(); } inline QT3_SUPPORT static QString rootDirPath() { return rootPath(); } inline QT3_SUPPORT static QString cleanDirPath(const QString &name) { return cleanPath(name); } -#endif +#endif // QT3_SUPPORT }; Q_DECLARE_OPERATORS_FOR_FLAGS(QDir::Filters) diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp index e90529e..625098e 100644 --- a/src/corelib/io/qfileinfo.cpp +++ b/src/corelib/io/qfileinfo.cpp @@ -41,12 +41,8 @@ #include "qplatformdefs.h" #include "qfileinfo.h" -#include "qdatetime.h" -#include "qabstractfileengine.h" #include "qfsfileengine_p.h" #include "qglobal.h" -#include "qatomic.h" -#include "qhash.h" #include "qdir.h" #include "qfileinfo_p.h" @@ -54,7 +50,7 @@ QT_BEGIN_NAMESPACE QFileInfoPrivate::QFileInfoPrivate(const QFileInfo *copy) { - if(copy) { + if (copy) { copy->d_func()->data->ref.ref(); data = copy->d_func()->data; } else { @@ -81,7 +77,8 @@ void QFileInfoPrivate::initFileEngine(const QString &file) bool QFileInfoPrivate::hasAccess(Access access) const { - if (!(getFileFlags(QAbstractFileEngine::FileInfoAll) & QAbstractFileEngine::LocalDiskFlag)) { + if (!(getFileFlags(QAbstractFileEngine::PermsMask + | QAbstractFileEngine::LocalDiskFlag) & QAbstractFileEngine::LocalDiskFlag)) { switch (access) { case ReadAccess: return getFileFlags(QAbstractFileEngine::ReadUserPerm); @@ -129,86 +126,104 @@ void QFileInfoPrivate::detach() QString QFileInfoPrivate::getFileName(QAbstractFileEngine::FileName name) const { - if(data->cache_enabled && !data->fileNames[(int)name].isNull()) + if (data->cache_enabled && !data->fileNames[(int)name].isNull()) return data->fileNames[(int)name]; QString ret = data->fileEngine->fileName(name); - if(data->cache_enabled) + if (ret.isNull()) + ret = QLatin1String(""); + if (data->cache_enabled) data->fileNames[(int)name] = ret; return ret; } +QString QFileInfoPrivate::getFileOwner(QAbstractFileEngine::FileOwner own) const +{ + if (data->cache_enabled && !data->fileOwners[(int)own].isNull()) + return data->fileOwners[(int)own]; + QString ret = data->fileEngine->owner(own); + if (ret.isNull()) + ret = QLatin1String(""); + if (data->cache_enabled) + data->fileOwners[(int)own] = ret; + return ret; +} + uint QFileInfoPrivate::getFileFlags(QAbstractFileEngine::FileFlags request) const { - // We split the testing into tests for for LinkType, BundleType and the rest. + // We split the testing into tests for for LinkType, BundleType, PermsMask + // and the rest. + // Tests for file permissions on Windows can be slow, expecially on network + // paths and NTFS drives. // In order to determine if a file is a symlink or not, we have to lstat(). // If we're not interested in that information, we might as well avoid one // extra syscall. Bundle detecton on Mac can be slow, expecially on network // paths, so we separate out that as well. - QAbstractFileEngine::FileFlags flags; - if (!data->getCachedFlag(CachedFileFlags)) { - QAbstractFileEngine::FileFlags req = QAbstractFileEngine::FileInfoAll; - req &= (~QAbstractFileEngine::LinkType); - req &= (~QAbstractFileEngine::BundleType); + QAbstractFileEngine::FileFlags req = 0; + uint cachedFlags = 0; - flags = data->fileEngine->fileFlags(req); - data->setCachedFlag(CachedFileFlags); - data->fileFlags |= uint(flags); - } else { - flags = QAbstractFileEngine::FileFlags(data->fileFlags & request); - } + if (request & (QAbstractFileEngine::FlagsMask | QAbstractFileEngine::TypesMask)) { + if (!data->getCachedFlag(CachedFileFlags)) { + req |= QAbstractFileEngine::FlagsMask; + req |= QAbstractFileEngine::TypesMask; + req &= (~QAbstractFileEngine::LinkType); + req &= (~QAbstractFileEngine::BundleType); - if (request & QAbstractFileEngine::LinkType) { - if (!data->getCachedFlag(CachedLinkTypeFlag)) { - QAbstractFileEngine::FileFlags linkflag; - linkflag = data->fileEngine->fileFlags(QAbstractFileEngine::LinkType); + cachedFlags |= CachedFileFlags; + } - data->setCachedFlag(CachedLinkTypeFlag); - data->fileFlags |= uint(linkflag); - flags |= linkflag; + if (request & QAbstractFileEngine::LinkType) { + if (!data->getCachedFlag(CachedLinkTypeFlag)) { + req |= QAbstractFileEngine::LinkType; + cachedFlags |= CachedLinkTypeFlag; + } } - } - if (request & QAbstractFileEngine::BundleType) { - if (!data->getCachedFlag(CachedBundleTypeFlag)) { - QAbstractFileEngine::FileFlags bundleflag; - bundleflag = data->fileEngine->fileFlags(QAbstractFileEngine::BundleType); + if (request & QAbstractFileEngine::BundleType) { + if (!data->getCachedFlag(CachedBundleTypeFlag)) { + req |= QAbstractFileEngine::BundleType; + cachedFlags |= CachedBundleTypeFlag; + } + } + } - data->setCachedFlag(CachedBundleTypeFlag); - data->fileFlags |= uint(bundleflag); - flags |= bundleflag; + if (request & QAbstractFileEngine::PermsMask) { + if (!data->getCachedFlag(CachedPerms)) { + req |= QAbstractFileEngine::PermsMask; + cachedFlags |= CachedPerms; } } - // no else branch - // if we had it cached, it was caught in the previous else branch + if (req) { + if (data->cache_enabled) + req &= (~QAbstractFileEngine::Refresh); + else + req |= QAbstractFileEngine::Refresh; - return flags & request; + QAbstractFileEngine::FileFlags flags = data->fileEngine->fileFlags(req); + data->fileFlags |= uint(flags); + data->setCachedFlag(cachedFlags); + } + + return data->fileFlags & request; } QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) const { if (!data->cache_enabled) data->clearFlags(); - if(request == QAbstractFileEngine::CreationTime) { - if(data->getCachedFlag(CachedCTime)) - return data->fileTimes[request]; - data->setCachedFlag(CachedCTime); - return (data->fileTimes[request] = data->fileEngine->fileTime(request)); + uint cf; + if (request == QAbstractFileEngine::CreationTime) + cf = CachedCTime; + else if (request == QAbstractFileEngine::ModificationTime) + cf = CachedMTime; + else + cf = CachedATime; + if (!data->getCachedFlag(cf)) { + data->fileTimes[request] = data->fileEngine->fileTime(request); + data->setCachedFlag(cf); } - if(request == QAbstractFileEngine::ModificationTime) { - if(data->getCachedFlag(CachedMTime)) - return data->fileTimes[request]; - data->setCachedFlag(CachedMTime); - return (data->fileTimes[request] = data->fileEngine->fileTime(request)); - } - if(request == QAbstractFileEngine::AccessTime) { - if(data->getCachedFlag(CachedATime)) - return data->fileTimes[request]; - data->setCachedFlag(CachedATime); - return (data->fileTimes[request] = data->fileEngine->fileTime(request)); - } - return data->fileTimes[0]; //cannot really happen + return data->fileTimes[request]; } //************* QFileInfo @@ -305,7 +320,6 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request) \sa setFile() */ - QFileInfo::QFileInfo() : d_ptr(new QFileInfoPrivate()) { } @@ -316,7 +330,6 @@ QFileInfo::QFileInfo() : d_ptr(new QFileInfoPrivate()) \sa setFile(), isRelative(), QDir::setCurrent(), QDir::isRelativePath() */ - QFileInfo::QFileInfo(const QString &file) : d_ptr(new QFileInfoPrivate()) { d_ptr->initFileEngine(file); @@ -331,7 +344,6 @@ QFileInfo::QFileInfo(const QString &file) : d_ptr(new QFileInfoPrivate()) \sa isRelative() */ - QFileInfo::QFileInfo(const QFile &file) : d_ptr(new QFileInfoPrivate()) { d_ptr->initFileEngine(file.fileName()); @@ -349,7 +361,6 @@ QFileInfo::QFileInfo(const QFile &file) : d_ptr(new QFileInfoPrivate()) \sa isRelative() */ - QFileInfo::QFileInfo(const QDir &dir, const QString &file) : d_ptr(new QFileInfoPrivate()) { d_ptr->initFileEngine(dir.filePath(file)); @@ -358,7 +369,6 @@ QFileInfo::QFileInfo(const QDir &dir, const QString &file) : d_ptr(new QFileInfo /*! Constructs a new QFileInfo that is a copy of the given \a fileinfo. */ - QFileInfo::QFileInfo(const QFileInfo &fileinfo) : d_ptr(new QFileInfoPrivate(&fileinfo)) { @@ -368,7 +378,6 @@ QFileInfo::QFileInfo(const QFileInfo &fileinfo) : d_ptr(new QFileInfoPrivate(&fi Destroys the QFileInfo and frees its resources. */ - QFileInfo::~QFileInfo() { } @@ -395,19 +404,19 @@ bool QFileInfo::operator==(const QFileInfo &fileinfo) const Q_D(const QFileInfo); // ### Qt 5: understand long and short file names on Windows // ### (GetFullPathName()). - if(fileinfo.d_func()->data == d->data) + if (fileinfo.d_func()->data == d->data) return true; - if(!d->data->fileEngine || !fileinfo.d_func()->data->fileEngine) + if (!d->data->fileEngine || !fileinfo.d_func()->data->fileEngine) return false; - if(d->data->fileEngine->caseSensitive() != fileinfo.d_func()->data->fileEngine->caseSensitive()) + if (d->data->fileEngine->caseSensitive() != fileinfo.d_func()->data->fileEngine->caseSensitive()) return false; - if(fileinfo.size() == size()) { //if the size isn't the same... + if (fileinfo.size() == size()) { //if the size isn't the same... QString file1 = canonicalFilePath(), file2 = fileinfo.canonicalFilePath(); - if(file1.length() == file2.length()) { - if(!fileinfo.d_func()->data->fileEngine->caseSensitive()) { - for(int i = 0; i < file1.length(); i++) { - if(file1.at(i).toLower() != file2.at(i).toLower()) + if (file1.length() == file2.length()) { + if (!fileinfo.d_func()->data->fileEngine->caseSensitive()) { + for (int i = 0; i < file1.length(); i++) { + if (file1.at(i).toLower() != file2.at(i).toLower()) return false; } return true; @@ -441,7 +450,6 @@ bool QFileInfo::operator==(const QFileInfo &fileinfo) /*! Makes a copy of the given \a fileinfo and assigns it to this QFileInfo. */ - QFileInfo &QFileInfo::operator=(const QFileInfo &fileinfo) { Q_D(QFileInfo); @@ -464,7 +472,6 @@ QFileInfo &QFileInfo::operator=(const QFileInfo &fileinfo) \sa isRelative(), QDir::setCurrent(), QDir::isRelativePath() */ - void QFileInfo::setFile(const QString &file) { *this = QFileInfo(file); @@ -481,7 +488,6 @@ void QFileInfo::setFile(const QString &file) \sa isRelative() */ - void QFileInfo::setFile(const QFile &file) { *this = QFileInfo(file.fileName()); @@ -498,7 +504,6 @@ void QFileInfo::setFile(const QFile &file) \sa isRelative() */ - void QFileInfo::setFile(const QDir &dir, const QString &file) { *this = QFileInfo(dir.filePath(file)); @@ -528,25 +533,24 @@ void QFileInfo::setFile(const QDir &dir, const QString &file) QString QFileInfo::absoluteFilePath() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); return d->getFileName(QAbstractFileEngine::AbsoluteName); } /*! - Returns the canonical path including the file name, i.e. an absolute - path without symbolic links or redundant "." or ".." elements. + Returns the canonical path including the file name, i.e. an absolute + path without symbolic links or redundant "." or ".." elements. - If the file does not exist, canonicalFilePath() returns an empty - string. + If the file does not exist, canonicalFilePath() returns an empty + string. - \sa filePath(), absoluteFilePath(), dir() + \sa filePath(), absoluteFilePath(), dir() */ - QString QFileInfo::canonicalFilePath() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); return d->getFileName(QAbstractFileEngine::CanonicalName); } @@ -569,7 +573,6 @@ QString QFileInfo::canonicalFilePath() const \sa absoluteFilePath(), path(), canonicalPath(), fileName(), isRelative() */ - QString QFileInfo::absolutePath() const { Q_D(const QFileInfo); @@ -591,16 +594,14 @@ QString QFileInfo::absolutePath() const \sa path(), absolutePath() */ - QString QFileInfo::canonicalPath() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); return d->getFileName(QAbstractFileEngine::CanonicalPathName); } - /*! Returns the file's path. This doesn't include the file name. @@ -610,11 +611,10 @@ QString QFileInfo::canonicalPath() const \sa filePath(), absolutePath(), canonicalPath(), dir(), fileName(), isRelative() */ - QString QFileInfo::path() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); return d->getFileName(QAbstractFileEngine::PathName); } @@ -635,16 +635,14 @@ QString QFileInfo::path() const \sa isAbsolute() */ - bool QFileInfo::isRelative() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return true; return d->data->fileEngine->isRelativePath(); } - /*! Converts the file's path to an absolute path if it is not already in that form. Returns true to indicate that the path was converted; otherwise returns false @@ -652,11 +650,10 @@ bool QFileInfo::isRelative() const \sa filePath(), isRelative() */ - bool QFileInfo::makeAbsolute() { Q_D(QFileInfo); - if(!d->data->fileEngine || !d->data->fileEngine->isRelativePath()) + if (!d->data->fileEngine || !d->data->fileEngine->isRelativePath()) return false; QString absFileName = d->getFileName(QAbstractFileEngine::AbsoluteName); d->initFileEngine(absFileName); @@ -669,11 +666,10 @@ bool QFileInfo::makeAbsolute() \note If the file is a symlink that points to a non existing file, false is returned. */ - bool QFileInfo::exists() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; return d->getFileFlags(QAbstractFileEngine::ExistsFlag); } @@ -685,7 +681,6 @@ bool QFileInfo::exists() const \note On Windows CE, there might be a delay for the file system driver to detect changes on the file. */ - void QFileInfo::refresh() { Q_D(QFileInfo); @@ -698,11 +693,10 @@ void QFileInfo::refresh() \sa absoluteFilePath(), canonicalFilePath(), isRelative() */ - QString QFileInfo::filePath() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); return d->getFileName(QAbstractFileEngine::DefaultName); } @@ -718,11 +712,10 @@ QString QFileInfo::filePath() const \sa isRelative(), filePath(), baseName(), extension() */ - QString QFileInfo::fileName() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); return d->getFileName(QAbstractFileEngine::BaseName); } @@ -739,11 +732,10 @@ QString QFileInfo::fileName() const \sa isBundle(), filePath(), baseName(), extension() */ - QString QFileInfo::bundleName() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); return d->getFileName(QAbstractFileEngine::BundleName); } @@ -764,11 +756,10 @@ QString QFileInfo::bundleName() const \sa fileName(), suffix(), completeSuffix(), completeBaseName() */ - QString QFileInfo::baseName() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); return d->getFileName(QAbstractFileEngine::BaseName).section(QLatin1Char('.'), 0, 0); } @@ -784,11 +775,10 @@ QString QFileInfo::baseName() const \sa fileName(), suffix(), completeSuffix(), baseName() */ - QString QFileInfo::completeBaseName() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); QString name = d->getFileName(QAbstractFileEngine::BaseName); int index = name.lastIndexOf(QLatin1Char('.')); @@ -806,11 +796,10 @@ QString QFileInfo::completeBaseName() const \sa fileName(), suffix(), baseName(), completeBaseName() */ - QString QFileInfo::completeSuffix() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); QString fileName = d->getFileName(QAbstractFileEngine::BaseName); int firstDot = fileName.indexOf(QLatin1Char('.')); @@ -834,11 +823,10 @@ QString QFileInfo::completeSuffix() const \sa fileName(), completeSuffix(), baseName(), completeBaseName() */ - QString QFileInfo::suffix() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); QString fileName = d->getFileName(QAbstractFileEngine::BaseName); int lastDot = fileName.lastIndexOf(QLatin1Char('.')); @@ -849,24 +837,23 @@ QString QFileInfo::suffix() const /*! - Returns the path of the object's parent directory as a QDir object. + Returns the path of the object's parent directory as a QDir object. - \bold{Note:} The QDir returned always corresponds to the object's - parent directory, even if the QFileInfo represents a directory. + \bold{Note:} The QDir returned always corresponds to the object's + parent directory, even if the QFileInfo represents a directory. - For each of the follwing, dir() returns a QDir for - \c{"~/examples/191697"}. + For each of the follwing, dir() returns a QDir for + \c{"~/examples/191697"}. - \snippet doc/src/snippets/fileinfo/main.cpp 0 + \snippet doc/src/snippets/fileinfo/main.cpp 0 - For each of the follwing, dir() returns a QDir for - \c{"."}. + For each of the follwing, dir() returns a QDir for + \c{"."}. - \snippet doc/src/snippets/fileinfo/main.cpp 1 + \snippet doc/src/snippets/fileinfo/main.cpp 1 - \sa absolutePath(), filePath(), fileName(), isRelative(), absoluteDir() + \sa absolutePath(), filePath(), fileName(), isRelative(), absoluteDir() */ - QDir QFileInfo::dir() const { // ### Qt5: Maybe rename this to parentDirectory(), considering what it actually do? @@ -878,7 +865,6 @@ QDir QFileInfo::dir() const \sa dir(), filePath(), fileName(), isRelative() */ - QDir QFileInfo::absoluteDir() const { return QDir(absolutePath()); @@ -891,7 +877,7 @@ QDir QFileInfo::absoluteDir() const */ QDir QFileInfo::dir(bool absPath) const { - if(absPath) + if (absPath) return absoluteDir(); return dir(); } @@ -902,11 +888,10 @@ QDir QFileInfo::dir(bool absPath) const \sa isWritable(), isExecutable(), permission() */ - bool QFileInfo::isReadable() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; return d->hasAccess(QFileInfoPrivate::ReadAccess); } @@ -916,11 +901,10 @@ bool QFileInfo::isReadable() const \sa isReadable(), isExecutable(), permission() */ - bool QFileInfo::isWritable() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; return d->hasAccess(QFileInfoPrivate::WriteAccess); } @@ -930,11 +914,10 @@ bool QFileInfo::isWritable() const \sa isReadable(), isWritable(), permission() */ - bool QFileInfo::isExecutable() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; return d->hasAccess(QFileInfoPrivate::ExecuteAccess); } @@ -948,7 +931,7 @@ bool QFileInfo::isExecutable() const bool QFileInfo::isHidden() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; return d->getFileFlags(QAbstractFileEngine::HiddenFlag); } @@ -960,11 +943,10 @@ bool QFileInfo::isHidden() const \sa isDir(), isSymLink(), isBundle() */ - bool QFileInfo::isFile() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; return d->getFileFlags(QAbstractFileEngine::FileType); } @@ -975,11 +957,10 @@ bool QFileInfo::isFile() const \sa isFile(), isSymLink(), isBundle() */ - bool QFileInfo::isDir() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; return d->getFileFlags(QAbstractFileEngine::DirectoryType); } @@ -992,11 +973,10 @@ bool QFileInfo::isDir() const \sa isDir(), isSymLink(), isFile() */ - bool QFileInfo::isBundle() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; return d->getFileFlags(QAbstractFileEngine::BundleType); } @@ -1018,21 +998,19 @@ bool QFileInfo::isBundle() const \sa isFile(), isDir(), symLinkTarget() */ - bool QFileInfo::isSymLink() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; return d->getFileFlags(QAbstractFileEngine::LinkType); } /*! - Returns true if the object points to a directory or to a symbolic - link to a directory, and that directory is the root directory; otherwise - returns false. + Returns true if the object points to a directory or to a symbolic + link to a directory, and that directory is the root directory; otherwise + returns false. */ - bool QFileInfo::isRoot() const { Q_D(const QFileInfo); @@ -1064,7 +1042,7 @@ bool QFileInfo::isRoot() const QString QFileInfo::readLink() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); return d->getFileName(QAbstractFileEngine::LinkName); } @@ -1079,13 +1057,12 @@ QString QFileInfo::readLink() const \sa ownerId(), group(), groupId() */ - QString QFileInfo::owner() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); - return d->data->fileEngine->owner(QAbstractFileEngine::OwnerUser); + return d->getFileOwner(QAbstractFileEngine::OwnerUser); } /*! @@ -1096,11 +1073,10 @@ QString QFileInfo::owner() const \sa owner(), group(), groupId() */ - uint QFileInfo::ownerId() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return 0; return d->data->fileEngine->ownerId(QAbstractFileEngine::OwnerUser); } @@ -1115,13 +1091,12 @@ uint QFileInfo::ownerId() const \sa groupId(), owner(), ownerId() */ - QString QFileInfo::group() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QLatin1String(""); - return d->data->fileEngine->owner(QAbstractFileEngine::OwnerGroup); + return d->getFileOwner(QAbstractFileEngine::OwnerGroup); } /*! @@ -1132,11 +1107,10 @@ QString QFileInfo::group() const \sa group(), owner(), ownerId() */ - uint QFileInfo::groupId() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return 0; return d->data->fileEngine->ownerId(QAbstractFileEngine::OwnerGroup); } @@ -1154,11 +1128,10 @@ uint QFileInfo::groupId() const \sa isReadable(), isWritable(), isExecutable() */ - bool QFileInfo::permission(QFile::Permissions permissions) const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return false; return d->getFileFlags(QAbstractFileEngine::FileFlags((int)permissions)) == (uint)permissions; } @@ -1167,11 +1140,10 @@ bool QFileInfo::permission(QFile::Permissions permissions) const Returns the complete OR-ed together combination of QFile::Permissions for the file. */ - QFile::Permissions QFileInfo::permissions() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return 0; return QFile::Permissions(d->getFileFlags(QAbstractFileEngine::PermsMask) & QAbstractFileEngine::PermsMask); } @@ -1183,13 +1155,12 @@ QFile::Permissions QFileInfo::permissions() const \sa exists() */ - qint64 QFileInfo::size() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return 0; - if(!d->data->getCachedFlag(QFileInfoPrivate::CachedSize)) { + if (!d->data->getCachedFlag(QFileInfoPrivate::CachedSize)) { d->data->setCachedFlag(QFileInfoPrivate::CachedSize); d->data->fileSize = d->data->fileEngine->size(); } @@ -1209,11 +1180,10 @@ qint64 QFileInfo::size() const \sa lastModified() lastRead() */ - QDateTime QFileInfo::created() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QDateTime(); return d->getFileTime(QAbstractFileEngine::CreationTime); } @@ -1223,11 +1193,10 @@ QDateTime QFileInfo::created() const \sa created() lastRead() */ - QDateTime QFileInfo::lastModified() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QDateTime(); return d->getFileTime(QAbstractFileEngine::ModificationTime); } @@ -1240,11 +1209,10 @@ QDateTime QFileInfo::lastModified() const \sa created() lastModified() */ - QDateTime QFileInfo::lastRead() const { Q_D(const QFileInfo); - if(!d->data->fileEngine) + if (!d->data->fileEngine) return QDateTime(); return d->getFileTime(QAbstractFileEngine::AccessTime); } @@ -1252,7 +1220,6 @@ QDateTime QFileInfo::lastRead() const /*! \internal Detaches all internal data. */ - void QFileInfo::detach() { Q_D(QFileInfo); @@ -1264,7 +1231,6 @@ void QFileInfo::detach() \sa setCaching(), refresh() */ - bool QFileInfo::caching() const { Q_D(const QFileInfo); @@ -1283,7 +1249,6 @@ bool QFileInfo::caching() const \sa refresh(), caching() */ - void QFileInfo::setCaching(bool enable) { Q_D(QFileInfo); diff --git a/src/corelib/io/qfileinfo_p.h b/src/corelib/io/qfileinfo_p.h index 065f860..d97a0cf 100644 --- a/src/corelib/io/qfileinfo_p.h +++ b/src/corelib/io/qfileinfo_p.h @@ -54,6 +54,9 @@ // #include "qfileinfo.h" +#include "qabstractfileengine.h" +#include "qdatetime.h" +#include "qatomic.h" QT_BEGIN_NAMESPACE @@ -75,35 +78,41 @@ public: uint getFileFlags(QAbstractFileEngine::FileFlags) const; QDateTime &getFileTime(QAbstractFileEngine::FileTime) const; QString getFileName(QAbstractFileEngine::FileName) const; + QString getFileOwner(QAbstractFileEngine::FileOwner own) const; enum { CachedFileFlags=0x01, CachedLinkTypeFlag=0x02, CachedBundleTypeFlag=0x04, CachedMTime=0x10, CachedCTime=0x20, CachedATime=0x40, - CachedSize =0x08 }; + CachedSize =0x08, CachedPerms=0x80 }; struct Data { inline Data() - : ref(1), fileEngine(0), cache_enabled(1), fileSize(0) - { clear(); } + : ref(1), fileEngine(0), + cachedFlags(0), cache_enabled(1), fileFlags(0), fileSize(0) + {} inline Data(const Data ©) : ref(1), fileEngine(QAbstractFileEngine::create(copy.fileName)), - fileName(copy.fileName), cache_enabled(copy.cache_enabled), fileSize(copy.fileSize) - { clear(); } + fileName(copy.fileName), + cachedFlags(0), cache_enabled(copy.cache_enabled), fileFlags(0), fileSize(0) + {} inline ~Data() { delete fileEngine; } inline void clearFlags() { fileFlags = 0; cachedFlags = 0; if (fileEngine) - (void)fileEngine->fileFlags(QFSFileEngine::Refresh); + (void)fileEngine->fileFlags(QAbstractFileEngine::Refresh); } inline void clear() { clearFlags(); for (int i = QAbstractFileEngine::NFileNames - 1 ; i >= 0 ; --i) fileNames[i].clear(); + fileOwners[1].clear(); + fileOwners[0].clear(); } mutable QAtomicInt ref; QAbstractFileEngine *fileEngine; mutable QString fileName; mutable QString fileNames[QAbstractFileEngine::NFileNames]; + mutable QString fileOwners[2]; mutable uint cachedFlags : 31; mutable uint cache_enabled : 1; diff --git a/src/corelib/io/qfilesystemwatcher.cpp b/src/corelib/io/qfilesystemwatcher.cpp index 451fbd4..7223e49 100644 --- a/src/corelib/io/qfilesystemwatcher.cpp +++ b/src/corelib/io/qfilesystemwatcher.cpp @@ -248,7 +248,7 @@ QFileSystemWatcherEngine *QFileSystemWatcherPrivate::createNativeEngine() eng = QDnotifyFileSystemWatcherEngine::create(); return eng; #elif defined(Q_OS_FREEBSD) || defined(Q_OS_MAC) -# if 0 && (defined Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) +# if defined(Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) return QFSEventsFileSystemWatcherEngine::create(); else diff --git a/src/corelib/io/qfilesystemwatcher_dnotify.cpp b/src/corelib/io/qfilesystemwatcher_dnotify.cpp index 1a218c7..82470c8 100644 --- a/src/corelib/io/qfilesystemwatcher_dnotify.cpp +++ b/src/corelib/io/qfilesystemwatcher_dnotify.cpp @@ -39,6 +39,7 @@ ** ****************************************************************************/ +#include "qplatformdefs.h" #include "qfilesystemwatcher.h" #include "qfilesystemwatcher_dnotify_p.h" @@ -255,16 +256,16 @@ QStringList QDnotifyFileSystemWatcherEngine::addPaths(const QStringList &paths, if(fd == 0) { - DIR *d = ::opendir(path.toUtf8().constData()); + QT_DIR *d = QT_OPENDIR(path.toUtf8().constData()); if(!d) continue; // Could not open directory - DIR *parent = 0; + QT_DIR *parent = 0; QDir parentDir(path); if(!parentDir.isRoot()) { parentDir.cdUp(); - parent = ::opendir(parentDir.path().toUtf8().constData()); + parent = QT_OPENDIR(parentDir.path().toUtf8().constData()); if(!parent) { - ::closedir(d); + QT_CLOSEDIR(d); continue; } } @@ -272,8 +273,8 @@ QStringList QDnotifyFileSystemWatcherEngine::addPaths(const QStringList &paths, fd = qt_safe_dup(::dirfd(d)); int parentFd = parent ? qt_safe_dup(::dirfd(parent)) : 0; - ::closedir(d); - if(parent) ::closedir(parent); + QT_CLOSEDIR(d); + if(parent) QT_CLOSEDIR(parent); Q_ASSERT(fd); if(::fcntl(fd, F_SETSIG, SIGIO) || diff --git a/src/corelib/io/qfilesystemwatcher_fsevents.cpp b/src/corelib/io/qfilesystemwatcher_fsevents.cpp index 046377e..54ae24e 100644 --- a/src/corelib/io/qfilesystemwatcher_fsevents.cpp +++ b/src/corelib/io/qfilesystemwatcher_fsevents.cpp @@ -94,7 +94,7 @@ static void addPathToHash(PathHash &pathHash, const QString &key, const QFileInf { PathInfoList &list = pathHash[key]; list.push_back(PathInfo(path, - fileInfo.absoluteFilePath().normalized(QString::NormalizationForm_D).toUtf8())); + fileInfo.canonicalFilePath().normalized(QString::NormalizationForm_D).toUtf8())); pathHash.insert(key, list); } @@ -206,7 +206,7 @@ QStringList QFSEventsFileSystemWatcherEngine::addPaths(const QStringList &paths, } else { directories->append(path); // Full file path for dirs. - QCFString cfpath(createFSStreamPath(fileInfo.absoluteFilePath())); + QCFString cfpath(createFSStreamPath(fileInfo.canonicalFilePath())); addPathToHash(dirPathInfoHash, cfpath, fileInfo, path); CFArrayAppendValue(tmpArray, cfpath); } @@ -216,7 +216,7 @@ QStringList QFSEventsFileSystemWatcherEngine::addPaths(const QStringList &paths, continue; } else { // Just the absolute path (minus it's filename) for files. - QCFString cfpath(createFSStreamPath(fileInfo.absolutePath())); + QCFString cfpath(createFSStreamPath(fileInfo.canonicalPath())); files->append(path); addPathToHash(filePathInfoHash, cfpath, fileInfo, path); CFArrayAppendValue(tmpArray, cfpath); @@ -293,7 +293,7 @@ QStringList QFSEventsFileSystemWatcherEngine::removePaths(const QStringList &pat itemCount = CFArrayGetCount(tmpArray); const QString &path = paths.at(i); QFileInfo fi(path); - QCFString cfpath(createFSStreamPath(fi.absolutePath())); + QCFString cfpath(createFSStreamPath(fi.canonicalPath())); CFIndex index = CFArrayGetFirstIndexOfValue(tmpArray, CFRangeMake(0, itemCount), cfpath); if (index != -1) { @@ -302,7 +302,7 @@ QStringList QFSEventsFileSystemWatcherEngine::removePaths(const QStringList &pat removePathFromHash(filePathInfoHash, cfpath, path); } else { // Could be a directory we are watching instead. - QCFString cfdirpath(createFSStreamPath(fi.absoluteFilePath())); + QCFString cfdirpath(createFSStreamPath(fi.canonicalFilePath())); index = CFArrayGetFirstIndexOfValue(tmpArray, CFRangeMake(0, itemCount), cfdirpath); if (index != -1) { CFArrayRemoveValueAtIndex(tmpArray, index); diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp index c842e49..1672dfc 100644 --- a/src/corelib/io/qfsfileengine.cpp +++ b/src/corelib/io/qfsfileengine.cpp @@ -56,6 +56,9 @@ #endif #include <stdio.h> #include <stdlib.h> +#if defined(Q_OS_MAC) +# include <private/qcore_mac_p.h> +#endif QT_BEGIN_NAMESPACE @@ -143,9 +146,27 @@ QString QFSFileEnginePrivate::canonicalized(const QString &path) if (path.size() == 1 && path.at(0) == QLatin1Char('/')) return path; #endif - // Mac OS X 10.5.x doesn't support the realpath(X,0) extenstion we use here. -#if defined(Q_OS_LINUX) || defined(Q_OS_SYMBIAN) - char *ret = realpath(path.toLocal8Bit().constData(), (char*)0); +#if defined(Q_OS_LINUX) || defined(Q_OS_SYMBIAN) || defined(Q_OS_MAC) + char *ret = 0; +#if defined(Q_OS_MAC) + // Mac OS X 10.5.x doesn't support the realpath(X,0) extension we use here. + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) { + ret = realpath(path.toLocal8Bit().constData(), (char*)0); + } else { + // on 10.5 we can use FSRef to resolve the file path. + FSRef fsref; + if (FSPathMakeRef((const UInt8 *)QDir::cleanPath(path).toUtf8().data(), &fsref, 0) == noErr) { + CFURLRef urlref = CFURLCreateFromFSRef(NULL, &fsref); + CFStringRef canonicalPath = CFURLCopyFileSystemPath(urlref, kCFURLPOSIXPathStyle); + QString ret = QCFString::toQString(canonicalPath); + CFRelease(canonicalPath); + CFRelease(urlref); + return ret; + } + } +#else + ret = realpath(path.toLocal8Bit().constData(), (char*)0); +#endif if (ret) { QString canonicalPath = QDir::cleanPath(QString::fromLocal8Bit(ret)); free(ret); diff --git a/src/corelib/io/qfsfileengine_iterator_unix.cpp b/src/corelib/io/qfsfileengine_iterator_unix.cpp index b68b1a1..bfdb03e 100644 --- a/src/corelib/io/qfsfileengine_iterator_unix.cpp +++ b/src/corelib/io/qfsfileengine_iterator_unix.cpp @@ -58,13 +58,13 @@ public: #endif {} - DIR *dir; - dirent *dirEntry; + QT_DIR *dir; + QT_DIRENT *dirEntry; bool done; #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) // for readdir_r - dirent *mt_file; + QT_DIRENT *mt_file; #endif }; @@ -76,14 +76,14 @@ void QFSFileEngineIterator::advance() return; #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) - if (::readdir_r(platform->dir, platform->mt_file, &platform->dirEntry) != 0) + if (QT_READDIR_R(platform->dir, platform->mt_file, &platform->dirEntry) != 0) platform->done = true; #else // ### add local lock to prevent breaking reentrancy - platform->dirEntry = ::readdir(platform->dir); + platform->dirEntry = QT_READDIR(platform->dir); #endif // _POSIX_THREAD_SAFE_FUNCTIONS if (!platform->dirEntry) { - ::closedir(platform->dir); + QT_CLOSEDIR(platform->dir); platform->dir = 0; platform->done = true; #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) @@ -101,7 +101,7 @@ void QFSFileEngineIterator::newPlatformSpecifics() void QFSFileEngineIterator::deletePlatformSpecifics() { if (platform->dir) { - ::closedir(platform->dir); + QT_CLOSEDIR(platform->dir); #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) delete [] platform->mt_file; platform->mt_file = 0; @@ -115,18 +115,18 @@ bool QFSFileEngineIterator::hasNext() const { if (!platform->done && !platform->dir) { QFSFileEngineIterator *that = const_cast<QFSFileEngineIterator *>(this); - if ((that->platform->dir = ::opendir(QFile::encodeName(path()).data())) == 0) { + if ((that->platform->dir = QT_OPENDIR(QFile::encodeName(path()).data())) == 0) { that->platform->done = true; } else { // ### Race condition; we should use fpathconf and dirfd(). long maxPathName = ::pathconf(QFile::encodeName(path()).data(), _PC_NAME_MAX); if ((int) maxPathName == -1) maxPathName = FILENAME_MAX; - maxPathName += sizeof(dirent) + 1; + maxPathName += sizeof(QT_DIRENT) + 1; #if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_CYGWIN) && !defined(Q_OS_SYMBIAN) if (that->platform->mt_file) delete [] that->platform->mt_file; - that->platform->mt_file = (dirent *)new char[maxPathName]; + that->platform->mt_file = (QT_DIRENT *)new char[maxPathName]; #endif that->advance(); diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp index ca01eb2..eb99f27 100644 --- a/src/corelib/io/qfsfileengine_win.cpp +++ b/src/corelib/io/qfsfileengine_win.cpp @@ -177,7 +177,7 @@ void QFSFileEnginePrivate::resolveLibs() triedResolve = true; #if !defined(Q_OS_WINCE) - HINSTANCE advapiHnd = LoadLibraryW(L"advapi32"); + HINSTANCE advapiHnd = LoadLibrary(L"advapi32"); if (advapiHnd) { ptrGetNamedSecurityInfoW = (PtrGetNamedSecurityInfoW)GetProcAddress(advapiHnd, "GetNamedSecurityInfoW"); ptrLookupAccountSidW = (PtrLookupAccountSidW)GetProcAddress(advapiHnd, "LookupAccountSidW"); @@ -209,7 +209,7 @@ void QFSFileEnginePrivate::resolveLibs() ptrFreeSid(pWorld); } } - HINSTANCE userenvHnd = LoadLibraryW(L"userenv"); + HINSTANCE userenvHnd = LoadLibrary(L"userenv"); if (userenvHnd) ptrGetUserProfileDirectoryW = (PtrGetUserProfileDirectoryW)GetProcAddress(userenvHnd, "GetUserProfileDirectoryW"); #endif @@ -217,7 +217,6 @@ void QFSFileEnginePrivate::resolveLibs() } #endif // QT_NO_LIBRARY -// UNC functions NT typedef DWORD (WINAPI *PtrNetShareEnum)(LPWSTR, DWORD, LPBYTE*, DWORD, LPDWORD, LPDWORD, LPDWORD); static PtrNetShareEnum ptrNetShareEnum = 0; typedef DWORD (WINAPI *PtrNetApiBufferFree)(LPVOID); @@ -241,7 +240,7 @@ bool QFSFileEnginePrivate::resolveUNCLibs() #endif triedResolve = true; #if !defined(Q_OS_WINCE) - HINSTANCE hLib = LoadLibraryW(L"Netapi32"); + HINSTANCE hLib = LoadLibrary(L"netapi32"); if (hLib) { ptrNetShareEnum = (PtrNetShareEnum)GetProcAddress(hLib, "NetShareEnum"); if (ptrNetShareEnum) @@ -1038,11 +1037,11 @@ QString QFSFileEngine::homePath() if (ok) { DWORD dwBufferSize = 0; // First call, to determine size of the strings (with '\0'). - ok = ::ptrGetUserProfileDirectoryW(token, NULL, &dwBufferSize); + ok = ptrGetUserProfileDirectoryW(token, NULL, &dwBufferSize); if (!ok && dwBufferSize != 0) { // We got the required buffer size wchar_t *userDirectory = new wchar_t[dwBufferSize]; // Second call, now we can fill the allocated buffer. - ok = ::ptrGetUserProfileDirectoryW(token, userDirectory, &dwBufferSize); + ok = ptrGetUserProfileDirectoryW(token, userDirectory, &dwBufferSize); if (ok) ret = QString::fromWCharArray(userDirectory); @@ -1414,7 +1413,7 @@ QAbstractFileEngine::FileFlags QFSFileEnginePrivate::getPermissions() const QAbstractFileEngine::FileFlags ret = 0; #if !defined(QT_NO_LIBRARY) - if((qt_ntfs_permission_lookup > 0) && ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_NT)) { + if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) { resolveLibs(); if(ptrGetNamedSecurityInfoW && ptrBuildTrusteeWithSidW && ptrGetEffectiveRightsFromAclW) { enum { ReadMask = 0x00000001, WriteMask = 0x00000002, ExecMask = 0x00000020 }; @@ -1717,8 +1716,7 @@ QString QFSFileEngine::owner(FileOwner own) const QString name; #if !defined(QT_NO_LIBRARY) Q_D(const QFSFileEngine); - - if ((qt_ntfs_permission_lookup > 0) && ((QSysInfo::WindowsVersion&QSysInfo::WV_NT_based) > QSysInfo::WV_NT)) { + if((qt_ntfs_permission_lookup > 0) && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) { QFSFileEnginePrivate::resolveLibs(); if (ptrGetNamedSecurityInfoW && ptrLookupAccountSidW) { PSID pOwner = 0; diff --git a/src/corelib/io/qurl.h b/src/corelib/io/qurl.h index 0aa534a..906979c 100644 --- a/src/corelib/io/qurl.h +++ b/src/corelib/io/qurl.h @@ -46,6 +46,7 @@ #include <QtCore/qobjectdefs.h> #include <QtCore/qpair.h> #include <QtCore/qstring.h> +#include <QtCore/qhash.h> QT_BEGIN_HEADER @@ -75,6 +76,7 @@ public: RemovePath = 0x20, RemoveQuery = 0x40, RemoveFragment = 0x80, + // 0x100: private: normalized StripTrailingSlash = 0x10000 }; @@ -268,6 +270,11 @@ public: inline DataPtr &data_ptr() { return d; } }; +inline uint qHash(const QUrl &uri) +{ + return qHash(uri.toEncoded(QUrl::FormattingOption(0x100))); +} + Q_DECLARE_TYPEINFO(QUrl, Q_MOVABLE_TYPE); Q_DECLARE_SHARED(QUrl) Q_DECLARE_OPERATORS_FOR_FLAGS(QUrl::FormattingOptions) diff --git a/src/corelib/kernel/qabstractitemmodel.cpp b/src/corelib/kernel/qabstractitemmodel.cpp index dbf422e..fdbc5ba 100644 --- a/src/corelib/kernel/qabstractitemmodel.cpp +++ b/src/corelib/kernel/qabstractitemmodel.cpp @@ -1043,12 +1043,32 @@ void QAbstractItemModelPrivate::columnsRemoved(const QModelIndex &parent, /*! + \since 4.7 + + \fn int QModelIndex::rowCount() const + + Returns the number of children of this model index. + + \sa columnCount(), parent(), child(), sibling(), model() +*/ + +/*! + \since 4.7 + + \fn int QModelIndex::columnCount() const + + Returns the number of columns for the children of this model index. + + \sa rowCount(), parent(), child(), sibling(), model() +*/ + +/*! \fn QModelIndex QModelIndex::parent() const Returns the parent of the model index, or QModelIndex() if it has no parent. - \sa child(), sibling(), model() + \sa child(), sibling(), rowCount(), columnCount(), model() */ /*! diff --git a/src/corelib/kernel/qabstractitemmodel.h b/src/corelib/kernel/qabstractitemmodel.h index 63d9e6f..d91c383 100644 --- a/src/corelib/kernel/qabstractitemmodel.h +++ b/src/corelib/kernel/qabstractitemmodel.h @@ -68,6 +68,8 @@ public: inline int column() const { return c; } inline void *internalPointer() const { return p; } inline qint64 internalId() const { return reinterpret_cast<qint64>(p); } + inline int rowCount() const; + inline int columnCount() const; inline QModelIndex parent() const; inline QModelIndex sibling(int row, int column) const; inline QModelIndex child(int row, int column) const; @@ -385,6 +387,12 @@ inline QModelIndex::QModelIndex(int arow, int acolumn, void *adata, const QAbstractItemModel *amodel) : r(arow), c(acolumn), p(adata), m(amodel) {} +inline int QModelIndex::rowCount() const +{ return m ? m->rowCount(*this) : 0; } + +inline int QModelIndex::columnCount() const +{ return m ? m->columnCount(*this) : 0; } + inline QModelIndex QModelIndex::parent() const { return m ? m->parent(*this) : QModelIndex(); } diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index 3f69b4f..d177b26 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -331,7 +331,7 @@ QEvent::~QEvent() equivalent of calling setAccepted(false). Clearing the accept parameter indicates that the event receiver - does not want the event. Unwanted events might be propgated to the + does not want the event. Unwanted events might be propagated to the parent widget. \sa accept() diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index c76e81b..a20d171 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -266,7 +266,6 @@ public: UngrabMouse = 187, GrabKeyboard = 188, UngrabKeyboard = 189, - CocoaRequestModal = 190, // Internal for requesting an application modal Cocoa Window MacGLClearDrawable = 191, // Internal Cocoa, the window has changed, so we must clear StateMachineSignal = 192, diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index cbc87f3..7c8fb3d 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -341,6 +341,9 @@ public: // for controlling when to send posted events QAtomicInt serialNumber; int lastSerialNumber; +#ifndef Q_OS_WINCE + int lastMessageTime; +#endif QAtomicInt wakeUps; // timers @@ -364,7 +367,12 @@ public: }; QEventDispatcherWin32Private::QEventDispatcherWin32Private() - : threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0), getMessageHook(0), serialNumber(0), lastSerialNumber(0), wakeUps(0) + : threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0), getMessageHook(0), + serialNumber(0), lastSerialNumber(0), +#ifndef Q_OS_WINCE + lastMessageTime(0), +#endif + wakeUps(0) { resolveTimerAPI(); } @@ -479,6 +487,9 @@ LRESULT CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) int localSerialNumber = d->serialNumber; if (localSerialNumber != d->lastSerialNumber) { d->lastSerialNumber = localSerialNumber; +#ifndef Q_OS_WINCE + d->lastMessageTime = GetMessageTime(); +#endif QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); } return 0; @@ -495,9 +506,13 @@ LRESULT CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp) if (q) { QEventDispatcherWin32Private *d = q->d_func(); int localSerialNumber = d->serialNumber; - if (HIWORD(GetQueueStatus(QS_INPUT | QS_RAWINPUT | QS_TIMER)) == 0) { - // no more input or timer events in the message queue, we can allow posted events to be - // sent now + if (HIWORD(GetQueueStatus(QS_INPUT | QS_RAWINPUT | QS_TIMER)) == 0 +#ifndef Q_OS_WINCE + || GetMessageTime() - d->lastMessageTime >= 10 +#endif + ) { + // no more input or timer events in the message queue or more than 10ms has elapsed since + // we send posted events, we can allow posted events to be sent now (void) d->wakeUps.fetchAndStoreRelease(0); MSG *msg = (MSG *) lp; if (localSerialNumber != d->lastSerialNumber @@ -744,6 +759,8 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { if (seenWM_QT_SENDPOSTEDEVENTS) { + // when calling processEvents() "manually", we only want to send posted + // events once needWM_QT_SENDPOSTEDEVENTS = true; continue; } diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index cd7418a..be1b2ae 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -482,6 +482,46 @@ int QMetaObject::classInfoCount() const return n; } +/** \internal +* helper class for indexOf{Method,Slot,Signal}, returns the relative index of the method within +* the baseObject +* \a MethodType might be MethodSignal or MethodSlot, or 0 to match everything. +*/ +template<int MethodType> +static inline int indexOfMethodRelative(const QMetaObject **baseObject, + const char *method, + bool normalizeStringData) +{ + const QMetaObject *m; + for (m = *baseObject; m; m = *baseObject = m->d.superdata) { + const QMetaObject *const m = *baseObject; + int i = (MethodType == MethodSignal && priv(m->d.data)->revision >= 4) + ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1); + if (i < 0) + continue; + + const int end = (MethodType == MethodSlot && priv(m->d.data)->revision >= 4) + ? (priv(m->d.data)->signalCount) : 0; + if (!normalizeStringData) { + for (; i >= end; --i) { + if ((MethodType == 0 || (m->d.data[priv(m->d.data)->methodData + 5*i + 4] & MethodTypeMask) == MethodType) + && strcmp(method, m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5*i]) == 0) + return i; + } + } else if (priv(m->d.data)->revision < 5) { + const char *stringdata = (m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5 * i]); + const QByteArray normalizedSignature = QMetaObject::normalizedSignature(stringdata); + for (; i >= end; --i) { + if ((MethodType == 0|| (m->d.data[priv(m->d.data)->methodData + 5*i + 4] & MethodTypeMask) == MethodType) + && normalizedSignature == method) + return i; + } + } + } + return -1; +} + + /*! \since 4.5 @@ -515,17 +555,14 @@ int QMetaObject::indexOfConstructor(const char *constructor) const */ int QMetaObject::indexOfMethod(const char *method) const { - int i = -1; const QMetaObject *m = this; - while (m && i < 0) { - for (i = priv(m->d.data)->methodCount-1; i >= 0; --i) - if (strcmp(method, m->d.stringdata - + m->d.data[priv(m->d.data)->methodData + 5*i]) == 0) { - i += m->methodOffset(); - break; - } - m = m->d.superdata; + int i = indexOfMethodRelative<0>(&m, method, false); + if (i < 0) { + m = this; + i = indexOfMethodRelative<0>(&m, method, true); } + if (i >= 0) + i += m->methodOffset(); return i; } @@ -543,7 +580,11 @@ int QMetaObject::indexOfMethod(const char *method) const int QMetaObject::indexOfSignal(const char *signal) const { const QMetaObject *m = this; - int i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal); + int i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, false); + if (i < 0) { + m = this; + i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, true); + } if (i >= 0) i += m->methodOffset(); return i; @@ -554,21 +595,11 @@ int QMetaObject::indexOfSignal(const char *signal) const \a baseObject will be adjusted to the enclosing QMetaObject, or 0 if the signal is not found */ -int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, const char *signal) +int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, + const char *signal, + bool normalizeStringData) { - int i = -1; - while (*baseObject) { - const QMetaObject *const m = *baseObject; - for (i = priv(m->d.data)->methodCount-1; i >= 0; --i) - if ((m->d.data[priv(m->d.data)->methodData + 5*i + 4] & MethodTypeMask) == MethodSignal - && strcmp(signal, m->d.stringdata - + m->d.data[priv(m->d.data)->methodData + 5*i]) == 0) { - break; - } - if (i >= 0) - break; - *baseObject = m->d.superdata; - } + int i = indexOfMethodRelative<MethodSignal>(baseObject, signal, normalizeStringData); #ifndef QT_NO_DEBUG const QMetaObject *m = *baseObject; if (i >= 0 && m && m->d.superdata) { @@ -581,7 +612,6 @@ int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, co return i; } - /*! Finds \a slot and returns its index; otherwise returns -1. @@ -592,18 +622,19 @@ int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, co */ int QMetaObject::indexOfSlot(const char *slot) const { - int i = -1; - const QMetaObject *m = this; - while (m && i < 0) { - for (i = priv(m->d.data)->methodCount-1; i >= 0; --i) - if ((m->d.data[priv(m->d.data)->methodData + 5*i + 4] & MethodTypeMask) == MethodSlot - && strcmp(slot, m->d.stringdata - + m->d.data[priv(m->d.data)->methodData + 5*i]) == 0) { - i += m->methodOffset(); - break; - } - m = m->d.superdata; - } + int i = QMetaObjectPrivate::indexOfSlot(this, slot, false); + if (i < 0) + i = QMetaObjectPrivate::indexOfSlot(this, slot, true); + return i; +} + +int QMetaObjectPrivate::indexOfSlot(const QMetaObject *m, + const char *slot, + bool normalizeStringData) +{ + int i = indexOfMethodRelative<MethodSlot>(&m, slot, normalizeStringData); + if (i >= 0) + i += m->methodOffset(); return i; } diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index 3bbb050..a176149 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -115,11 +115,17 @@ struct QMetaObjectPrivate int constructorCount, constructorData; //since revision 2 int flags; //since revision 3 int signalCount; //since revision 4 + // revision 5 introduces changes in normalized signatures, no new members static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) { return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); } - static int indexOfSignalRelative(const QMetaObject **baseObject, const char* name); + static int indexOfSignalRelative(const QMetaObject **baseObject, + const char* name, + bool normalizeStringData); + static int indexOfSlot(const QMetaObject *m, + const char *slot, + bool normalizeStringData); static int originalClone(const QMetaObject *obj, int local_method_index); #ifndef QT_NO_QOBJECT @@ -247,6 +253,7 @@ static QByteArray normalizeTypeInternal(const char *t, const char *e, bool fixSc } while (optional[++i].keyword != 0); } + bool star = false; while (t != e) { char c = *t++; if (fixScope && c == ':' && *t == ':' ) { @@ -257,6 +264,7 @@ static QByteArray normalizeTypeInternal(const char *t, const char *e, bool fixSc --i; result.resize(i + 1); } + star = star || c == '*'; result += c; if (c == '<') { //template recursion @@ -277,6 +285,23 @@ static QByteArray normalizeTypeInternal(const char *t, const char *e, bool fixSc } } } + + // cv qualifers can appear after the type as well + if (t != e && (e - t >= 5 && strncmp("const", t, 5) == 0)) { + t += 5; + while (t != e && is_space(*t)) + ++t; + if (adjustConst && t != e && *t == '&') { + // treat const ref as value + ++t; + } else if (!star) { + // move const to the front (but not if const comes after a *) + result.prepend("const "); + } else { + // keep const after a * + result += "const"; + } + } } return result; diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index e304290..870baab 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -361,7 +361,14 @@ void QMetaType::registerStreamOperators(const char *typeName, SaveOperator saveO int idx = type(typeName); if (!idx) return; + registerStreamOperators(idx, saveOp, loadOp); +} +/*! \internal +*/ +void QMetaType::registerStreamOperators(int idx, SaveOperator saveOp, + LoadOperator loadOp) +{ QVector<QCustomTypeInfo> *ct = customTypes(); if (!ct) return; diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index c0dd288..c23caed 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -108,6 +108,8 @@ public: typedef void (*LoadOperator)(QDataStream &, void *); static void registerStreamOperators(const char *typeName, SaveOperator saveOp, LoadOperator loadOp); + static void registerStreamOperators(int type, SaveOperator saveOp, + LoadOperator loadOp); #endif static int registerType(const char *typeName, Destructor destructor, Constructor constructor); @@ -224,6 +226,24 @@ inline int qRegisterMetaType( #endif } +#ifndef QT_NO_DATASTREAM +template <typename T> +inline int qRegisterMetaTypeStreamOperators() +{ + typedef void(*SavePtr)(QDataStream &, const T *); + typedef void(*LoadPtr)(QDataStream &, T *); + SavePtr sptr = qMetaTypeSaveHelper<T>; + LoadPtr lptr = qMetaTypeLoadHelper<T>; + + register int id = qMetaTypeId<T>(); + QMetaType::registerStreamOperators(id, + reinterpret_cast<QMetaType::SaveOperator>(sptr), + reinterpret_cast<QMetaType::LoadOperator>(lptr)); + + return id; +} +#endif + #define Q_DECLARE_METATYPE(TYPE) \ QT_BEGIN_NAMESPACE \ template <> \ diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 5298fff..761b31f 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -871,7 +871,7 @@ QObject::~QObject() // all the signal/slots connections are still in place - if we don't // quit now, we will crash pretty soon. qWarning("Detected an unexpected exception in ~QObject while emitting destroyed()."); -#if defined(Q_AUTOTEST_EXPORT) && !defined(QT_NO_EXCEPTIONS) +#if defined(Q_BUILD_INTERNAL) && !defined(QT_NO_EXCEPTIONS) struct AutotestException : public std::exception { const char *what() const throw() { return "autotest swallow"; } @@ -903,7 +903,8 @@ QObject::~QObject() // disconnect all receivers if (d->connectionLists) { ++d->connectionLists->inUse; - for (int signal = -1; signal < d->connectionLists->count(); ++signal) { + int connectionListsCount = d->connectionLists->count(); + for (int signal = -1; signal < connectionListsCount; ++signal) { QObjectPrivate::ConnectionList &connectionList = (*d->connectionLists)[signal]; @@ -940,16 +941,17 @@ QObject::~QObject() // disconnect all senders QObjectPrivate::Connection *node = d->senders; while (node) { - QMutex *m = signalSlotLock(node->sender); + QObject *sender = node->sender; + QMutex *m = signalSlotLock(sender); node->prev = &node; bool needToUnlock = QOrderedMutexLocker::relock(locker.mutex(), m); //the node has maybe been removed while the mutex was unlocked in relock? - if (!node || signalSlotLock(node->sender) != m) { + if (!node || node->sender != sender) { m->unlock(); continue; } node->receiver = 0; - QObjectConnectionListVector *senderLists = node->sender->d_func()->connectionLists; + QObjectConnectionListVector *senderLists = sender->d_func()->connectionLists; if (senderLists) senderLists->dirty = true; @@ -2510,20 +2512,25 @@ bool QObject::connect(const QObject *sender, const char *signal, const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code - int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal); + int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); if (signal_index < 0) { // check for normalized signatures tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); signal = tmp_signal_name.constData() + 1; smeta = sender->metaObject(); - signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal); + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); + } + if (signal_index < 0) { + // re-use tmp_signal_name and signal from above - if (signal_index < 0) { - err_method_notfound(sender, signal_arg, "connect"); - err_info_about_objects("connect", sender, receiver); - return false; - } + smeta = sender->metaObject(); + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + } + if (signal_index < 0) { + err_method_notfound(sender, signal_arg, "connect"); + err_info_about_objects("connect", sender, receiver); + return false; } signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); int signalOffset, methodOffset; @@ -2543,16 +2550,21 @@ bool QObject::connect(const QObject *sender, const char *signal, int method_index = -1; switch (membcode) { case QSLOT_CODE: - method_index = rmeta->indexOfSlot(method); + method_index = QMetaObjectPrivate::indexOfSlot(rmeta, method, false); break; case QSIGNAL_CODE: - method_index = rmeta->indexOfSignal(method); + method_index = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); + if (method_index >= 0) + method_index += rmeta->methodOffset(); break; } if (method_index < 0) { // check for normalized methods tmp_method_name = QMetaObject::normalizedSignature(method); method = tmp_method_name.constData(); + + // rmeta may have been modified above + rmeta = receiver->metaObject(); switch (membcode) { case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); @@ -2740,7 +2752,9 @@ bool QObject::disconnect(const QObject *sender, const char *signal, do { int signal_index = -1; if (signal) { - signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal); + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); + if (signal_index < 0) + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); if (signal_index < 0) break; signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); @@ -3360,7 +3374,9 @@ int QObjectPrivate::signalIndex(const char *signalName) const { Q_Q(const QObject); const QMetaObject *base = q->metaObject(); - int relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName); + int relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, false); + if (relative_index < 0) + relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, true); if (relative_index < 0) return relative_index; relative_index = QMetaObjectPrivate::originalClone(base, relative_index); diff --git a/src/corelib/thread/qorderedmutexlocker_p.h b/src/corelib/thread/qorderedmutexlocker_p.h index 7d52a29..702125c 100644 --- a/src/corelib/thread/qorderedmutexlocker_p.h +++ b/src/corelib/thread/qorderedmutexlocker_p.h @@ -103,9 +103,11 @@ public: mtx2->lock(); return true; } - mtx1->unlock(); - mtx2->lock(); - mtx1->lock(); + if (!mtx2->tryLock()) { + mtx1->unlock(); + mtx2->lock(); + mtx1->lock(); + } return true; } diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 556093f..6c87a86 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -1061,6 +1061,11 @@ QByteArray &QByteArray::operator=(const char *str) \internal */ +/*! \fn bool QByteArray::isSharedWith(const QByteArray &other) + + \internal +*/ + /*! \fn char QByteArray::at(int i) const Returns the character at index position \a i in the byte array. diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index ef97716..ec592f5f 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -164,6 +164,7 @@ public: inline const char *constData() const; inline void detach(); bool isDetached() const; + inline bool isSharedWith(const QByteArray &other) const { return d == other.d; } void clear(); #ifdef Q_COMPILER_MANGLES_RETURN_TYPE diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index d758325..7111e68 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -847,6 +847,11 @@ void QHashData::checkSanity() \internal */ +/*! \fn bool QHash::isSharedWith(const QHash<Key, T> &other) const + + \internal +*/ + /*! \fn void QHash::clear() Removes all items from the hash. diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 5e93523..2832ca2 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -299,6 +299,7 @@ public: inline void detach() { if (d->ref != 1) detach_helper(); } inline bool isDetached() const { return d->ref == 1; } inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } + inline bool isSharedWith(const QHash<Key, T> &other) const { return d == other.d; } void clear(); diff --git a/src/corelib/tools/qlinkedlist.cpp b/src/corelib/tools/qlinkedlist.cpp index 5a53c32..909d940 100644 --- a/src/corelib/tools/qlinkedlist.cpp +++ b/src/corelib/tools/qlinkedlist.cpp @@ -197,6 +197,11 @@ QLinkedListData QLinkedListData::shared_null = { \internal */ +/*! \fn bool QLinkedList::isSharedWith(const QLinkedList<T> &other) const + + \internal +*/ + /*! \fn bool QLinkedList::isEmpty() const Returns true if the list contains no items; otherwise returns diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index f6de966..fe586a4 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -93,6 +93,7 @@ public: { if (d->ref != 1) detach_helper(); } inline bool isDetached() const { return d->ref == 1; } inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } + inline bool isSharedWith(const QLinkedList<T> &other) const { return d == other.d; } inline bool isEmpty() const { return d->size == 0; } diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index f1df9bd..3f124c9 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -599,6 +599,11 @@ void **QListData::erase(void **xi) \internal */ +/*! \fn bool QList::isSharedWith(const QList<T> &other) const + + \internal +*/ + /*! \fn bool QList::isEmpty() const Returns true if the list contains no items; otherwise returns diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 120442d..1d08c41 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -130,6 +130,7 @@ public: inline bool isDetached() const { return d->ref == 1; } inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } + inline bool isSharedWith(const QList<T> &other) const { return d == other.d; } inline bool isEmpty() const { return p.isEmpty(); } diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index 8d79cb3..ff10fa1 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -4293,6 +4293,7 @@ bool QLocalePrivate::validateChars(const QString &str, NumberMode numMode, QByte const bool scientific = numMode == DoubleScientificMode; bool lastWasE = false; + bool lastWasDigit = false; int eCnt = 0; int decPointCnt = 0; bool dec = false; @@ -4307,6 +4308,7 @@ bool QLocalePrivate::validateChars(const QString &str, NumberMode numMode, QByte if (dec && decDigits != -1 && decDigits < ++decDigitCnt) return false; } + lastWasDigit = true; } else { switch (c) { case '.': @@ -4344,7 +4346,10 @@ bool QLocalePrivate::validateChars(const QString &str, NumberMode numMode, QByte break; case ',': - return false; + //it can only be placed after a digit which is before the decimal point + if (!lastWasDigit || decPointCnt > 0) + return false; + break; case 'e': if (scientific) { @@ -4362,10 +4367,12 @@ bool QLocalePrivate::validateChars(const QString &str, NumberMode numMode, QByte // If it's not a valid digit, it shall be Invalid. return false; } + lastWasDigit = false; } lastWasE = c == 'e'; - buff->append(c); + if (c != ',') + buff->append(c); } return true; diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index 82cbe78..3143ee4 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -473,6 +473,11 @@ void QMapData::dump() \internal */ +/*! \fn bool QMap::isSharedWith(const QMap<Key, T> &other) const + + \internal +*/ + /*! \fn void QMap::setInsertInOrder(bool sharable) \internal diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index e71064c..9ba9ca5 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -182,6 +182,7 @@ public: inline void detach() { if (d->ref != 1) detach_helper(); } inline bool isDetached() const { return d->ref == 1; } inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } + inline bool isSharedWith(const QMap<Key, T> &other) const { return d == other.d; } inline void setInsertInOrder(bool ordered) { d->insertInOrder = ordered; } void clear(); diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index c1470c4..a791ae9 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -1092,7 +1092,12 @@ QString::QString(QChar ch) \internal */ -/*! \fn void QString::isDetached() const +/*! \fn bool QString::isDetached() const + + \internal +*/ + +/*! \fn bool QString::isSharedWith(const QString &other) const \internal */ diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index c1def0f..5e3b0fd 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -122,6 +122,7 @@ public: inline void detach(); inline bool isDetached() const; + inline bool isSharedWith(const QString &other) const { return d == other.d; } void clear(); inline const QChar at(int i) const; diff --git a/src/corelib/tools/qstringlist.cpp b/src/corelib/tools/qstringlist.cpp index 4b0f488..e9bdf57 100644 --- a/src/corelib/tools/qstringlist.cpp +++ b/src/corelib/tools/qstringlist.cpp @@ -404,7 +404,17 @@ void QtPrivate::QStringList_replaceInStrings(QStringList *that, const QRegExp &r */ QString QtPrivate::QStringList_join(const QStringList *that, const QString &sep) { + int totalLength = 0; + const int size = that->size(); + + for (int i = 0; i < size; ++i) + totalLength += that->at(i).size(); + + if(size > 0) + totalLength += sep.size() * (size - 1); + QString res; + res.reserve(totalLength); for (int i = 0; i < that->size(); ++i) { if (i) res += sep; diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 48b52d2..3a13540 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -392,6 +392,11 @@ int QVectorData::grow(int sizeofTypedData, int size, int sizeofT, bool excessive \internal */ +/*! \fn bool QVector::isSharedWith(const QVector<T> &other) const + + \internal +*/ + /*! \fn T *QVector::data() Returns a pointer to the data stored in the vector. The pointer diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 90b7442..61b22fd 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -134,6 +134,7 @@ public: inline void detach() { if (d->ref != 1) detach_helper(); } inline bool isDetached() const { return d->ref == 1; } inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } + inline bool isSharedWith(const QVector<T> &other) const { return d == other.d; } inline T *data() { detach(); return p->array; } inline const T *data() const { return p->array; } diff --git a/src/corelib/xml/qxmlstream.cpp b/src/corelib/xml/qxmlstream.cpp index 5717ca2..1bf00b8 100644 --- a/src/corelib/xml/qxmlstream.cpp +++ b/src/corelib/xml/qxmlstream.cpp @@ -3003,8 +3003,7 @@ QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q) deleteDevice = false; #ifndef QT_NO_TEXTCODEC codec = QTextCodec::codecForMib(106); // utf8 - encoder = codec->makeEncoder(); - encoder->state.flags |= QTextCodec::IgnoreHeader; // no byte order mark for utf8 + encoder = codec->makeEncoder(QTextCodec::IgnoreHeader); // no byte order mark for utf8 #endif inStartElement = inEmptyElement = false; wroteSomething = false; @@ -3278,9 +3277,7 @@ void QXmlStreamWriter::setCodec(QTextCodec *codec) if (codec) { d->codec = codec; delete d->encoder; - d->encoder = codec->makeEncoder(); - if (codec->mibEnum() == 106) - d->encoder->state.flags |= QTextCodec::IgnoreHeader; // no byte order mark for utf8 + d->encoder = codec->makeEncoder(QTextCodec::IgnoreHeader); // no byte order mark for utf8 } } diff --git a/src/gui/dialogs/qfiledialog_win.cpp b/src/gui/dialogs/qfiledialog_win.cpp index 7dcdc3f..c7cef1e 100644 --- a/src/gui/dialogs/qfiledialog_win.cpp +++ b/src/gui/dialogs/qfiledialog_win.cpp @@ -53,31 +53,17 @@ #include <qdir.h> #include <qstringlist.h> #include <qlibrary.h> +#include "qfiledialog_win_p.h" #ifndef QT_NO_THREAD # include <private/qmutexpool_p.h> #endif #ifdef Q_WS_WINCE +#include <shlobj.h> #include <commdlg.h> -# ifndef BFFM_SETSELECTION -# define BFFM_SETSELECTION (WM_USER + 102) -# endif -// Windows Mobile has a broken definition for BROWSEINFO -// Only compile fix -typedef struct qt_priv_browseinfo { - HWND hwndOwner; - LPCITEMIDLIST pidlRoot; - LPWSTR pszDisplayName; - LPCWSTR lpszTitle; - UINT ulFlags; - BFFCALLBACK lpfn; - LPARAM lParam; - int iImage; -} qt_BROWSEINFO; bool qt_priv_ptr_valid = false; #else -#include "qfiledialog_win_p.h" //we have to declare them here because they're not present for all SDK/compilers static const IID QT_IID_IFileOpenDialog = {0xd57c7288, 0xd4ad, 0x4768, {0xbe, 0x02, 0x9d, 0x96, 0x95, 0x32, 0xd9, 0x60} }; static const IID QT_IID_IShellItem = {0x43826d1e, 0xe718, 0x42ee, {0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe} }; @@ -85,11 +71,6 @@ static const CLSID QT_CLSID_FileOpenDialog = {0xdc1c5a9c, 0xe88a, 0x4dde, {0xa5, #endif -// Don't remove the lines below! -// -// resolving the W methods manually is needed, because Windows 95 doesn't include -// these methods in Shell32.lib (not even stubs!), so you'd get an unresolved symbol -// when Qt calls getExistingDirectory(), etc. typedef LPITEMIDLIST (WINAPI *PtrSHBrowseForFolder)(BROWSEINFO*); static PtrSHBrowseForFolder ptrSHBrowseForFolder = 0; typedef BOOL (WINAPI *PtrSHGetPathFromIDList)(LPITEMIDLIST,LPWSTR); @@ -125,7 +106,7 @@ static void qt_win_resolve_libs() ptrSHGetMalloc = (PtrSHGetMalloc) lib.resolve("SHGetMalloc"); #else // CE stores them in a different lib and does not use unicode version - HINSTANCE handle = LoadLibraryW(L"Ceshell"); + HINSTANCE handle = LoadLibrary(L"Ceshell"); ptrSHBrowseForFolder = (PtrSHBrowseForFolder)GetProcAddress(handle, L"SHBrowseForFolder"); ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList)GetProcAddress(handle, L"SHGetPathFromIDList"); ptrSHGetMalloc = (PtrSHGetMalloc)GetProcAddress(handle, L"SHGetMalloc"); @@ -462,7 +443,7 @@ static bool qt_win_set_IFileDialogOptions(IFileDialog *pfd, // Add the filters to the file dialog. if (numFilters) { wchar_t *szData = (wchar_t*)winfilters.utf16(); - COMDLG_FILTERSPEC *filterSpec = new COMDLG_FILTERSPEC[numFilters]; + qt_COMDLG_FILTERSPEC *filterSpec = new qt_COMDLG_FILTERSPEC[numFilters]; for(int i = 0; i<numFilters; i++) { filterSpec[i].pszName = szData+offsets[i*2]; filterSpec[i].pszSpec = szData+offsets[(i*2)+1]; @@ -526,7 +507,7 @@ static QStringList qt_win_CID_get_open_file_names(const QFileDialogArgs &args, modal_widget.setParent(args.parent, Qt::Window); QApplicationPrivate::enterModal(&modal_widget); // Multiple selection is allowed only in IFileOpenDialog. - IFileOpenDialog *pfd; + IFileOpenDialog *pfd = 0; HRESULT hr = CoCreateInstance(QT_CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, QT_IID_IFileOpenDialog, reinterpret_cast<void**>(&pfd)); @@ -718,11 +699,6 @@ static int __stdcall winGetExistDirCallbackProc(HWND hwnd, return 0; } -#ifndef BIF_NEWDIALOGSTYLE -#define BIF_NEWDIALOGSTYLE 0x0040 // Use the new dialog layout with the ability to resize -#endif - - QString qt_win_get_existing_directory(const QFileDialogArgs &args) { QString currentDir = QDir::currentPath(); @@ -747,11 +723,7 @@ QString qt_win_get_existing_directory(const QFileDialogArgs &args) path[0] = 0; tTitle = args.caption; -#if !defined(Q_WS_WINCE) BROWSEINFO bi; -#else - qt_BROWSEINFO bi; -#endif Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created)); bi.hwndOwner = (parent ? parent->winId() : 0); @@ -765,7 +737,7 @@ QString qt_win_get_existing_directory(const QFileDialogArgs &args) qt_win_resolve_libs(); if (ptrSHBrowseForFolder) { - LPITEMIDLIST pItemIDList = ptrSHBrowseForFolder((BROWSEINFO*)&bi); + LPITEMIDLIST pItemIDList = ptrSHBrowseForFolder(&bi); if (pItemIDList) { ptrSHGetPathFromIDList(pItemIDList, path); IMalloc *pMalloc; diff --git a/src/gui/dialogs/qfiledialog_win_p.h b/src/gui/dialogs/qfiledialog_win_p.h index 6d8c7c9..39e6f5d 100644 --- a/src/gui/dialogs/qfiledialog_win_p.h +++ b/src/gui/dialogs/qfiledialog_win_p.h @@ -39,9 +39,9 @@ ** ****************************************************************************/ -#include <shlobj.h> #include <objbase.h> -#if !defined(QFILEDIAG_WIN_P_H) && !defined(__shobjidl_h__) +#ifndef QFILEDIAG_WIN_P_H +#define QFILEDIAG_WIN_P_H //these are the interface declarations needed for the file dialog on Vista and up @@ -50,6 +50,8 @@ //declarations typedef DWORD SICHINTF; +typedef ULONG SFGAOF; +typedef DWORD SHCONTF; #define FOS_OVERWRITEPROMPT 0x2 #define FOS_STRICTFILETYPES 0x4 #define FOS_NOCHANGEDIR 0x8 @@ -83,6 +85,31 @@ typedef int GETPROPERTYSTOREFLAGS; #define GPS_BESTEFFORT 0x00000040 #define GPS_MASK_VALID 0x0000007F +typedef int (CALLBACK* BFFCALLBACK)(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData); +// message from browser +#define BFFM_INITIALIZED 1 +#define BFFM_SELCHANGED 2 +#define BFFM_ENABLEOK (WM_USER + 101) +#define BFFM_SETSELECTION (WM_USER + 103) +#define BFFM_SETSTATUSTEXT (WM_USER + 104) + +// Browsing for directory. +#define BIF_RETURNONLYFSDIRS 0x0001 +#define BIF_DONTGOBELOWDOMAIN 0x0002 +#define BIF_STATUSTEXT 0x0004 +#define BIF_RETURNFSANCESTORS 0x0008 +#define BIF_EDITBOX 0x0010 +#define BIF_VALIDATE 0x0020 +#define BIF_NEWDIALOGSTYLE 0x0040 +#define BIF_BROWSEINCLUDEURLS 0x0080 +#define BIF_UAHINT 0x0100 +#define BIF_NONEWFOLDERBUTTON 0x0200 +#define BIF_NOTRANSLATETARGETS 0x0400 +#define BIF_BROWSEFORCOMPUTER 0x1000 +#define BIF_BROWSEFORPRINTER 0x2000 +#define BIF_BROWSEINCLUDEFILES 0x4000 +#define BIF_SHAREABLE 0x8000 + //the enums typedef enum { SIATTRIBFLAGS_AND = 0x1, @@ -115,11 +142,34 @@ typedef FDE_SHAREVIOLATION_RESPONSE FDE_OVERWRITE_RESPONSE; typedef struct { LPCWSTR pszName; LPCWSTR pszSpec; -} COMDLG_FILTERSPEC; +} qt_COMDLG_FILTERSPEC; +#ifndef PROPERTYKEY_DEFINED +#define PROPERTYKEY_DEFINED typedef struct { GUID fmtid; DWORD pid; } PROPERTYKEY; +#endif + +typedef struct { + USHORT cb; + BYTE abID[1]; +} SHITEMID, *LPSHITEMID; +typedef struct { + SHITEMID mkid; +} ITEMIDLIST, *LPITEMIDLIST; +typedef const ITEMIDLIST *LPCITEMIDLIST; +typedef struct { + HWND hwndOwner; + LPCITEMIDLIST pidlRoot; + LPWSTR pszDisplayName; + LPCWSTR lpszTitle; + UINT ulFlags; + BFFCALLBACK lpfn; + LPARAM lParam; + int iImage; +} BROWSEINFO; + DECLARE_INTERFACE(IFileDialogEvents); DECLARE_INTERFACE_(IShellItem, IUnknown) { @@ -157,7 +207,7 @@ DECLARE_INTERFACE_(IModalWindow, IUnknown) }; DECLARE_INTERFACE_(IFileDialog, IModalWindow) { - STDMETHOD(SetFileTypes)(THIS_ UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec) PURE; + STDMETHOD(SetFileTypes)(THIS_ UINT cFileTypes, const qt_COMDLG_FILTERSPEC *rgFilterSpec) PURE; STDMETHOD(SetFileTypeIndex)(THIS_ UINT iFileType) PURE; STDMETHOD(GetFileTypeIndex)(THIS_ UINT *piFileType) PURE; STDMETHOD(Advise)(THIS_ IFileDialogEvents *pfde, DWORD *pdwCookie) PURE; diff --git a/src/gui/dialogs/qfileinfogatherer.cpp b/src/gui/dialogs/qfileinfogatherer.cpp index 1f61957..c75cdfd 100644 --- a/src/gui/dialogs/qfileinfogatherer.cpp +++ b/src/gui/dialogs/qfileinfogatherer.cpp @@ -354,6 +354,7 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil } if (!updatedFiles.isEmpty()) emit updates(path, updatedFiles); + emit directoryLoaded(path); } void QFileInfoGatherer::fetch(const QFileInfo &fileInfo, QTime &base, bool &firstTime, QList<QPair<QString, QFileInfo> > &updatedFiles, const QString &path) { diff --git a/src/gui/dialogs/qfileinfogatherer_p.h b/src/gui/dialogs/qfileinfogatherer_p.h index 0242178..b8b58a2 100644 --- a/src/gui/dialogs/qfileinfogatherer_p.h +++ b/src/gui/dialogs/qfileinfogatherer_p.h @@ -155,6 +155,7 @@ Q_SIGNALS: void updates(const QString &directory, const QList<QPair<QString, QFileInfo> > &updates); void newListOfFiles(const QString &directory, const QStringList &listOfFiles) const; void nameResolved(const QString &fileName, const QString &resolvedName) const; + void directoryLoaded(const QString &path); public: QFileInfoGatherer(QObject *parent = 0); diff --git a/src/gui/dialogs/qfilesystemmodel.cpp b/src/gui/dialogs/qfilesystemmodel.cpp index 6fd947c..0e1837b 100644 --- a/src/gui/dialogs/qfilesystemmodel.cpp +++ b/src/gui/dialogs/qfilesystemmodel.cpp @@ -150,6 +150,14 @@ QT_BEGIN_NAMESPACE */ /*! + \since 4.7 + \fn void QFileSystemModel::directoryLoaded(const QString &path) + + This signal is emitted when the gatherer thread has finished to load the \a path. + +*/ + +/*! \fn bool QFileSystemModel::remove(const QModelIndex &index) const Removes the model item \a index from the file system model and \bold{deletes the @@ -673,7 +681,7 @@ QVariant QFileSystemModel::data(const QModelIndex &index, int role) const case Qt::EditRole: case Qt::DisplayRole: switch (index.column()) { - case 0: return d->name(index); + case 0: return d->displayName(index); case 1: return d->size(index); case 2: return d->type(index); case 3: return d->time(index); @@ -789,13 +797,25 @@ QString QFileSystemModelPrivate::name(const QModelIndex &index) const if (resolvedSymLinks.contains(fullPath)) return resolvedSymLinks[fullPath]; } - // ### TODO it would be nice to grab the volume name if dirNode->parent == root return dirNode->fileName; } /*! \internal */ +QString QFileSystemModelPrivate::displayName(const QModelIndex &index) const +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + QFileSystemNode *dirNode = node(index); + if (!dirNode->volumeName.isNull()) + return dirNode->volumeName + QLatin1String(" (") + name(index) + QLatin1Char(')'); +#endif + return name(index); +} + +/*! + \internal +*/ QIcon QFileSystemModelPrivate::icon(const QModelIndex &index) const { if (!index.isValid()) @@ -1640,6 +1660,18 @@ QFileSystemModelPrivate::QFileSystemNode* QFileSystemModelPrivate::addNode(QFile #ifndef QT_NO_FILESYSTEMWATCHER node->populate(info); #endif +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + //The parentNode is "" so we are listing the drives + if (parentNode->fileName.isEmpty()) { + wchar_t name[MAX_PATH + 1]; + //GetVolumeInformation requires to add trailing backslash + const QString nodeName = fileName + QLatin1String("\\"); + BOOL success = ::GetVolumeInformation((wchar_t *)(nodeName.utf16()), + name, MAX_PATH + 1, NULL, 0, NULL, NULL, 0); + if (success && name[0]) + node->volumeName = QString::fromWCharArray(name); + } +#endif parentNode->children.insert(fileName, node); return node; } @@ -1869,6 +1901,8 @@ void QFileSystemModelPrivate::init() q, SLOT(_q_fileSystemChanged(QString,QList<QPair<QString,QFileInfo> >))); q->connect(&fileInfoGatherer, SIGNAL(nameResolved(QString,QString)), q, SLOT(_q_resolvedName(QString,QString))); + q->connect(&fileInfoGatherer, SIGNAL(directoryLoaded(QString)), + q, SIGNAL(directoryLoaded(QString))); q->connect(&delayedSortTimer, SIGNAL(timeout()), q, SLOT(_q_performDelayedSort()), Qt::QueuedConnection); } diff --git a/src/gui/dialogs/qfilesystemmodel.h b/src/gui/dialogs/qfilesystemmodel.h index 4dcfe26..d8178c7 100644 --- a/src/gui/dialogs/qfilesystemmodel.h +++ b/src/gui/dialogs/qfilesystemmodel.h @@ -70,6 +70,7 @@ class Q_GUI_EXPORT QFileSystemModel : public QAbstractItemModel Q_SIGNALS: void rootPathChanged(const QString &newPath); void fileRenamed(const QString &path, const QString &oldName, const QString &newName); + void directoryLoaded(const QString &path); public: enum Roles { diff --git a/src/gui/dialogs/qfilesystemmodel_p.h b/src/gui/dialogs/qfilesystemmodel_p.h index 6c85a7c..03e0bfb 100644 --- a/src/gui/dialogs/qfilesystemmodel_p.h +++ b/src/gui/dialogs/qfilesystemmodel_p.h @@ -97,6 +97,9 @@ public: } QString fileName; +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + QString volumeName; +#endif inline qint64 size() const { if (info && !info->isDir()) return info->size(); return 0; } inline QString type() const { if (info) return info->displayType; return QLatin1String(""); } @@ -278,6 +281,7 @@ public: QIcon icon(const QModelIndex &index) const; QString name(const QModelIndex &index) const; + QString displayName(const QModelIndex &index) const; QString filePath(const QModelIndex &index) const; QString size(const QModelIndex &index) const; static QString size(qint64 bytes); diff --git a/src/gui/embedded/qscreen_qws.cpp b/src/gui/embedded/qscreen_qws.cpp index 8eb8123..65a3fb5 100644 --- a/src/gui/embedded/qscreen_qws.cpp +++ b/src/gui/embedded/qscreen_qws.cpp @@ -39,6 +39,7 @@ ** ****************************************************************************/ +#include "qplatformdefs.h" #include "qscreen_qws.h" #include "qcolormap.h" @@ -3223,13 +3224,13 @@ QScreen * qt_probe_bus() return qt_dodriver("unaccel.so",0,0); } - DIR * dirptr=opendir("/proc/bus/pci"); + QT_DIR *dirptr = QT_OPENDIR("/proc/bus/pci"); if(!dirptr) return qt_dodriver("unaccel.so",0,0); - DIR * dirptr2; - dirent * cards; + QT_DIR * dirptr2; + QT_DIRENT *cards; - dirent * busses=readdir(dirptr); + QT_DIRENT *busses = QT_READDIR(dirptr); while(busses) { if(busses->d_name[0]!='.') { @@ -3237,9 +3238,9 @@ QScreen * qt_probe_bus() strcpy(buf,"/proc/bus/pci/"); qstrcpy(buf+14,busses->d_name); int p=strlen(buf); - dirptr2=opendir(buf); + dirptr2 = QT_OPENDIR(buf); if(dirptr2) { - cards=readdir(dirptr2); + cards = QT_READDIR(dirptr2); while(cards) { if(cards->d_name[0]!='.') { buf[p]='/'; @@ -3248,14 +3249,14 @@ QScreen * qt_probe_bus() if(ret) return ret; } - cards=readdir(dirptr2); + cards = QT_READDIR(dirptr2); } - closedir(dirptr2); + QT_CLOSEDIR(dirptr2); } } - busses=readdir(dirptr); + busses = QT_READDIR(dirptr); } - closedir(dirptr); + QT_CLOSEDIR(dirptr); return qt_dodriver("unaccel.so",0,0); } diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index cea723c..83a6b9c 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -4213,6 +4213,8 @@ static void _q_paintItem(QGraphicsItem *item, QPainter *painter, widgetItem->paintWindowFrame(painter, option, widget); if (painterStateProtection) painter->restore(); + } else if (widgetItem->autoFillBackground()) { + painter->fillRect(option->exposedRect, widgetItem->palette().window()); } widgetItem->paint(painter, option, widget); diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp index 5e01785..87416b4 100644 --- a/src/gui/graphicsview/qgraphicswidget.cpp +++ b/src/gui/graphicsview/qgraphicswidget.cpp @@ -967,6 +967,36 @@ void QGraphicsWidget::setPalette(const QPalette &palette) } /*! + \property QGraphicsWidget::autoFillBackground + \brief whether the widget background is filled automatically + \since 4.7 + + If enabled, this property will cause Qt to fill the background of the + widget before invoking the paint() method. The color used is defined by the + QPalette::Window color role from the widget's \l{QPalette}{palette}. + + In addition, Windows are always filled with QPalette::Window, unless the + WA_OpaquePaintEvent or WA_NoSystemBackground attributes are set. + + By default, this property is false. + + \sa Qt::WA_OpaquePaintEvent, Qt::WA_NoSystemBackground, +*/ +bool QGraphicsWidget::autoFillBackground() const +{ + Q_D(const QGraphicsWidget); + return d->autoFillBackground; +} +void QGraphicsWidget::setAutoFillBackground(bool enabled) +{ + Q_D(QGraphicsWidget); + if (d->autoFillBackground != enabled) { + d->autoFillBackground = enabled; + update(); + } +} + +/*! If this widget is currently managed by a layout, this function notifies the layout that the widget's size hints have changed and the layout may need to resize and reposition the widget accordingly. diff --git a/src/gui/graphicsview/qgraphicswidget.h b/src/gui/graphicsview/qgraphicswidget.h index f1d382b..4bea5be 100644 --- a/src/gui/graphicsview/qgraphicswidget.h +++ b/src/gui/graphicsview/qgraphicswidget.h @@ -82,6 +82,7 @@ class Q_GUI_EXPORT QGraphicsWidget : public QGraphicsObject, public QGraphicsLay Q_PROPERTY(Qt::WindowFlags windowFlags READ windowFlags WRITE setWindowFlags) Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle) Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry) + Q_PROPERTY(bool autoFillBackground READ autoFillBackground WRITE setAutoFillBackground) public: QGraphicsWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); ~QGraphicsWidget(); @@ -103,6 +104,9 @@ public: QPalette palette() const; void setPalette(const QPalette &palette); + bool autoFillBackground() const; + void setAutoFillBackground(bool enabled); + void resize(const QSizeF &size); inline void resize(qreal w, qreal h) { resize(QSizeF(w, h)); } QSizeF size() const; diff --git a/src/gui/graphicsview/qgraphicswidget_p.h b/src/gui/graphicsview/qgraphicswidget_p.h index 2c5b3bf..3ab8737 100644 --- a/src/gui/graphicsview/qgraphicswidget_p.h +++ b/src/gui/graphicsview/qgraphicswidget_p.h @@ -80,6 +80,7 @@ public: inSetGeometry(0), polished(0), inSetPos(0), + autoFillBackground(0), focusPolicy(Qt::NoFocus), focusNext(0), focusPrev(0), @@ -172,6 +173,7 @@ public: quint32 inSetGeometry : 1; quint32 polished: 1; quint32 inSetPos : 1; + quint32 autoFillBackground : 1; // Focus Qt::FocusPolicy focusPolicy; diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 4f5efa1..6bcf72b 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -1835,7 +1835,7 @@ void QImage::setColor(int i, QRgb c) qAlpha() to access the pixels. \sa bytesPerLine(), bits(), {QImage#Pixel Manipulation}{Pixel - Manipulation} + Manipulation}, constScanLine() */ uchar *QImage::scanLine(int i) { @@ -1865,6 +1865,28 @@ const uchar *QImage::scanLine(int i) const /*! + Returns a pointer to the pixel data at the scanline with index \a + i. The first scanline is at index 0. + + The scanline data is aligned on a 32-bit boundary. + + Note that QImage uses \l{Implicit Data Sharing} {implicit data + sharing}, but this function does \e not perform a deep copy of the + shared pixel data, because the returned data is const. + + \sa scanLine(), constBits() + \since 4.7 +*/ +const uchar *QImage::constScanLine(int i) const +{ + if (!d) + return 0; + + Q_ASSERT(i >= 0 && i < height()); + return d->data + i * d->bytes_per_line; +} + +/*! Returns a pointer to the first pixel data. This is equivalent to scanLine(0). @@ -1873,7 +1895,7 @@ const uchar *QImage::scanLine(int i) const data, thus ensuring that this QImage is the only one using the current return value. - \sa scanLine(), byteCount() + \sa scanLine(), byteCount(), constBits() */ uchar *QImage::bits() { @@ -1901,6 +1923,20 @@ const uchar *QImage::bits() const } +/*! + Returns a pointer to the first pixel data. + + Note that QImage uses \l{Implicit Data Sharing} {implicit data + sharing}, but this function does \e not perform a deep copy of the + shared pixel data, because the returned data is const. + + \sa bits(), constScanLine() + \since 4.7 +*/ +const uchar *QImage::constBits() const +{ + return d ? d->data : 0; +} /*! \fn void QImage::reset() diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index ac973a1..dd13782 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -182,6 +182,7 @@ public: uchar *bits(); const uchar *bits() const; + const uchar *constBits() const; #ifdef QT_DEPRECATED QT_DEPRECATED int numBytes() const; #endif @@ -189,6 +190,7 @@ public: uchar *scanLine(int); const uchar *scanLine(int) const; + const uchar *constScanLine(int) const; int bytesPerLine() const; bool valid(int x, int y) const; diff --git a/src/gui/image/qpicture.cpp b/src/gui/image/qpicture.cpp index fb70e36..3220a67 100644 --- a/src/gui/image/qpicture.cpp +++ b/src/gui/image/qpicture.cpp @@ -440,36 +440,6 @@ bool QPicture::play(QPainter *painter) return true; // no end-command } - -// -// QFakeDevice is used to create fonts with a custom DPI -// -class QFakeDevice : public QPaintDevice -{ -public: - QFakeDevice() { dpi_x = qt_defaultDpiX(); dpi_y = qt_defaultDpiY(); } - void setDpiX(int dpi) { dpi_x = dpi; } - void setDpiY(int dpi) { dpi_y = dpi; } - QPaintEngine *paintEngine() const { return 0; } - int metric(PaintDeviceMetric m) const - { - switch(m) { - case PdmPhysicalDpiX: - case PdmDpiX: - return dpi_x; - case PdmPhysicalDpiY: - case PdmDpiY: - return dpi_y; - default: - return QPaintDevice::metric(m); - } - } - -private: - int dpi_x; - int dpi_y; -}; - /*! \internal Iterates over the internal picture data and draws the picture using @@ -679,30 +649,29 @@ bool QPicture::exec(QPainter *painter, QDataStream &s, int nrecords) if (d->formatMajor >= 9) { s >> dbl; - QFont fnt(font); - if (dbl != 1.0) { - QFakeDevice fake; - fake.setDpiX(qRound(dbl*qt_defaultDpiX())); - fake.setDpiY(qRound(dbl*qt_defaultDpiY())); - fnt = QFont(font, &fake); - } + QFont fnt(font, painter->device()); + + qreal scale = painter->device()->logicalDpiY() / (dbl*qt_defaultDpiY()); + painter->save(); + painter->scale(1/scale, 1/scale); qreal justificationWidth; s >> justificationWidth; int flags = Qt::TextSingleLine | Qt::TextDontClip | Qt::TextForceLeftToRight; - QSizeF size(1, 1); + QSizeF size(scale, scale); if (justificationWidth > 0) { - size.setWidth(justificationWidth); + size.setWidth(justificationWidth*scale); flags |= Qt::TextJustificationForced; flags |= Qt::AlignJustify; } QFontMetrics fm(fnt); - QPointF pt(p.x(), p.y() - fm.ascent()); + QPointF pt(p.x()*scale, p.y()*scale - fm.ascent()); qt_format_text(fnt, QRectF(pt, size), flags, /*opt*/0, str, /*brect=*/0, /*tabstops=*/0, /*...*/0, /*tabarraylen=*/0, painter); + painter->restore(); } else { qt_format_text(font, QRectF(p, QSizeF(1, 1)), Qt::TextSingleLine | Qt::TextDontClip, /*opt*/0, str, /*brect=*/0, /*tabstops=*/0, /*...*/0, /*tabarraylen=*/0, painter); diff --git a/src/gui/itemviews/qdirmodel.cpp b/src/gui/itemviews/qdirmodel.cpp index ea608c1..378a238 100644 --- a/src/gui/itemviews/qdirmodel.cpp +++ b/src/gui/itemviews/qdirmodel.cpp @@ -185,12 +185,12 @@ void QDirModelPrivate::invalidate() /*! \class QDirModel - + \obsolete \brief The QDirModel class provides a data model for the local filesystem. \ingroup model-view - \note The usage of QDirModel is not recommended anymore. The + The usage of QDirModel is not recommended anymore. The QFileSystemModel class is a more performant alternative. This class provides access to the local filesystem, providing functions diff --git a/src/gui/itemviews/qfileiconprovider.cpp b/src/gui/itemviews/qfileiconprovider.cpp index 360ed14..b854dc2 100644 --- a/src/gui/itemviews/qfileiconprovider.cpp +++ b/src/gui/itemviews/qfileiconprovider.cpp @@ -72,7 +72,7 @@ QT_BEGIN_NAMESPACE /*! \class QFileIconProvider - \brief The QFileIconProvider class provides file icons for the QDirModel class. + \brief The QFileIconProvider class provides file icons for the QDirModel and the QFileSystemModel classes. */ /*! diff --git a/src/gui/itemviews/qsortfilterproxymodel.cpp b/src/gui/itemviews/qsortfilterproxymodel.cpp index 41c984e..472af7d 100644 --- a/src/gui/itemviews/qsortfilterproxymodel.cpp +++ b/src/gui/itemviews/qsortfilterproxymodel.cpp @@ -1481,7 +1481,6 @@ QSortFilterProxyModel::QSortFilterProxyModel(QObject *parent) d->filter_column = 0; d->filter_role = Qt::DisplayRole; d->dynamic_sortfilter = false; - connect(this, SIGNAL(modelReset()), this, SLOT(invalidate())); } /*! diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index f2bd288..30cb043 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -201,6 +201,7 @@ embedded { OBJECTIVE_HEADERS += \ qcocoawindow_mac_p.h \ + qcocoapanel_mac_p.h \ qcocoawindowdelegate_mac_p.h \ qcocoaview_mac_p.h \ qcocoaapplication_mac_p.h \ diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 9a639c1..65840c8 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -923,14 +923,6 @@ void QApplicationPrivate::initialize() // Set up which span functions should be used in raster engine... qInitDrawhelperAsm(); - -#ifdef QT_MAC_USE_COCOA - // Use the raster graphics system by default on Cocoa, override with - // -graphicssystem native - if (graphics_system_name.isEmpty()) { - graphics_system_name = QLatin1String("raster"); - } -#endif #if !defined(Q_WS_X11) && !defined(Q_WS_QWS) // initialize the graphics system - on X11 this is initialized inside diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index 6aebef5..961a3f6 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -1239,10 +1239,6 @@ void qt_init(QApplicationPrivate *priv, int) [cocoaApp setMenu:[qtMenuLoader menu]]; [newDelegate setMenuLoader:qtMenuLoader]; [qtMenuLoader release]; - - NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager]; - [eventManager setEventHandler:newDelegate andSelector:@selector(getUrl:withReplyEvent:) - forEventClass:kInternetEventClass andEventID:kAEGetURL]; } #endif // Register for Carbon tablet proximity events on the event monitor target. @@ -1341,12 +1337,39 @@ void QApplication::setMainWidget(QWidget *mainWidget) /***************************************************************************** QApplication cursor stack *****************************************************************************/ +#ifdef QT_MAC_USE_COCOA +void QApplicationPrivate::disableUsageOfCursorRects(bool disable) +{ + // In Cocoa there are two competing ways of setting the cursor; either + // by using cursor rects (see qcocoaview_mac.mm), or by pushing/popping + // the cursor manually. When we use override cursors, it makes most sense + // to use the latter. But then we need to tell cocoa to stop using the + // first approach so it doesn't change the cursor back when hovering over + // a cursor rect: + QWidgetList topLevels = qApp->topLevelWidgets(); + for (int i=0; i<topLevels.size(); ++i) { + if (NSWindow *window = qt_mac_window_for(topLevels.at(i))) + disable ? [window disableCursorRects] : [window enableCursorRects]; + } +} + +void QApplicationPrivate::updateOverrideCursor() +{ + // Sometimes Cocoa forgets that we have set a Cursor + // manually. In those cases, remind it again: + if (QCursor *override = qApp->overrideCursor()) + [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(*override)) set]; +} +#endif + void QApplication::setOverrideCursor(const QCursor &cursor) { qApp->d_func()->cursor_list.prepend(cursor); #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; + if (qApp->d_func()->cursor_list.size() == 1) + qApp->d_func()->disableUsageOfCursorRects(true); [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) push]; #else if (qApp && qApp->activeWindow()) @@ -1363,6 +1386,8 @@ void QApplication::restoreOverrideCursor() #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; [NSCursor pop]; + if (qApp->d_func()->cursor_list.isEmpty()) + qApp->d_func()->disableUsageOfCursorRects(false); #else if (qApp && qApp->activeWindow()) { const QCursor def(Qt::ArrowCursor); @@ -2435,6 +2460,28 @@ QApplicationPrivate::globalEventProcessor(EventHandlerCallRef er, EventRef event #endif } +#ifdef QT_MAC_USE_COCOA +void QApplicationPrivate::qt_initAfterNSAppStarted() +{ + setupAppleEvents(); + updateOverrideCursor(); +} + +void QApplicationPrivate::setupAppleEvents() +{ + // This function is called from the event dispatcher when NSApplication has + // finished initialization, which appears to be just after [NSApplication run] has + // started to execute. By setting up our apple events handlers this late, we override + // the ones set up by NSApplication. + QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate]; + NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager]; + [eventManager setEventHandler:newDelegate andSelector:@selector(appleEventQuit:withReplyEvent:) + forEventClass:kCoreEventClass andEventID:kAEQuitApplication]; + [eventManager setEventHandler:newDelegate andSelector:@selector(getUrl:withReplyEvent:) + forEventClass:kInternetEventClass andEventID:kAEGetURL]; +} +#endif + // In Carbon this is your one stop for apple events. // In Cocoa, it ISN'T. This is the catch-all Apple Event handler that exists // for the time between instantiating the NSApplication, but before the @@ -2982,7 +3029,7 @@ void onApplicationWindowChangedActivation(QWidget *widget, bool activated) } QMenuBar::macUpdateMenuBar(); - + QApplicationPrivate::updateOverrideCursor(); #else Q_UNUSED(widget); Q_UNUSED(activated); diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 9c001ab..59565d4 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -457,6 +457,12 @@ public: static OSStatus globalEventProcessor(EventHandlerCallRef, EventRef, void *); static OSStatus globalAppleEventProcessor(const AppleEvent *, AppleEvent *, long); static OSStatus tabletProximityCallback(EventHandlerCallRef, EventRef, void *); +#ifdef QT_MAC_USE_COCOA + static void qt_initAfterNSAppStarted(); + static void setupAppleEvents(); + static void updateOverrideCursor(); + static void disableUsageOfCursorRects(bool disable); +#endif static bool qt_mac_apply_settings(); #endif diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index 6caac9f..20b8030 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -63,7 +63,7 @@ #include "private/qsoftkeymanager_p.h" #include "apgwgnam.h" // For CApaWindowGroupName -#include <MdaAudioTonePlayer.h> // For CMdaAudioToneUtility +#include <mdaaudiotoneplayer.h> // For CMdaAudioToneUtility #if defined(Q_WS_S60) # if !defined(QT_NO_IM) diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp index 2a1f655..667db39 100644 --- a/src/gui/kernel/qapplication_x11.cpp +++ b/src/gui/kernel/qapplication_x11.cpp @@ -614,6 +614,11 @@ static int (*original_xio_errhandler)(Display *dpy); static int qt_x_errhandler(Display *dpy, XErrorEvent *err) { + if (X11->display != dpy) { + // only handle X errors for our display + return 0; + } + switch (err->error_code) { case BadAtom: if (err->request_code == 20 /* X_GetProperty */ diff --git a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm index 47a8026..ab71a05 100644 --- a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm +++ b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm @@ -317,5 +317,12 @@ static void cleanupCocoaApplicationDelegate() qt_sendSpontaneousEvent(qAppInstance(), &qtEvent); } +- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent +{ + Q_UNUSED(event); + Q_UNUSED(replyEvent); + [NSApp terminate:self]; +} + @end #endif diff --git a/src/gui/kernel/qcocoapanel_mac.mm b/src/gui/kernel/qcocoapanel_mac.mm index e535aac..3012093 100644 --- a/src/gui/kernel/qcocoapanel_mac.mm +++ b/src/gui/kernel/qcocoapanel_mac.mm @@ -46,6 +46,7 @@ #import <private/qcocoawindowdelegate_mac_p.h> #import <private/qcocoaview_mac_p.h> #import <private/qcocoawindowcustomthemeframe_mac_p.h> +#import <private/qcocoaapplication_mac_p.h> #include <QtGui/QWidget> diff --git a/src/gui/kernel/qcocoapanel_mac_p.h b/src/gui/kernel/qcocoapanel_mac_p.h index fc83bd8..3678f81 100644 --- a/src/gui/kernel/qcocoapanel_mac_p.h +++ b/src/gui/kernel/qcocoapanel_mac_p.h @@ -54,10 +54,16 @@ #ifdef QT_MAC_USE_COCOA #import <Cocoa/Cocoa.h> +QT_FORWARD_DECLARE_CLASS(QStringList); + @interface QT_MANGLE_NAMESPACE(QCocoaPanel) : NSPanel { bool leftButtonIsRightButton; + QStringList *currentCustomDragTypes; } + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; +- (void)registerDragTypes; + @end #endif + diff --git a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h b/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h index 1a265d0..2117551 100644 --- a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h +++ b/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h @@ -54,8 +54,32 @@ QT_BEGIN_NAMESPACE extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); // qcocoaview.mm extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp +extern const QStringList& qEnabledDraggedTypes(); // qmime_mac.cpp +extern bool qt_blockCocoaSettingModalWindowLevel; // qeventdispatcher_mac_p.h + +Q_GLOBAL_STATIC(QPointer<QWidget>, currentDragTarget); + QT_END_NAMESPACE +- (id)initWithContentRect:(NSRect)contentRect + styleMask:(NSUInteger)windowStyle + backing:(NSBackingStoreType)bufferingType + defer:(BOOL)deferCreation +{ + self = [super initWithContentRect:contentRect styleMask:windowStyle + backing:bufferingType defer:deferCreation]; + if (self) { + currentCustomDragTypes = 0; + } + return self; +} + +- (void)dealloc +{ + delete currentCustomDragTypes; + [super dealloc]; +} + - (BOOL)canBecomeKeyWindow { QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)]; @@ -65,6 +89,39 @@ QT_END_NAMESPACE return !(isPopup || isToolTip); } +- (BOOL)canBecomeMainWindow +{ + QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)]; + + bool isToolTip = (widget->windowType() == Qt::ToolTip); + bool isPopup = (widget->windowType() == Qt::Popup); + return !(isPopup || isToolTip); +} + +- (void)orderWindow:(NSWindowOrderingMode)orderingMode relativeTo:(NSInteger)otherWindowNumber +{ + if (qt_blockCocoaSettingModalWindowLevel) { + // To avoid windows popping in front while restoring modal sessions + // in the event dispatcher, we block cocoa from ordering this window + // to front. The result of not doing this can be seen if executing + // a native color dialog on top of another executing dialog. + return; + } + [super orderWindow:orderingMode relativeTo:otherWindowNumber]; +} + +- (void)setLevel:(NSInteger)windowLevel +{ + if (qt_blockCocoaSettingModalWindowLevel) { + // To avoid windows popping in front while restoring modal sessions + // in the event dispatcher, we block cocoa from ordering this window + // to front. The result of not doing this can be seen if executing + // a native color dialog on top of another executing dialog. + return; + } + [super setLevel:windowLevel]; +} + - (void)toggleToolbarShown:(id)sender { macSendToolbarChangeEvent([self QT_MANGLE_NAMESPACE(qt_qwidget)]); @@ -103,6 +160,23 @@ QT_END_NAMESPACE qt_dispatchTabletProximityEvent(tabletEvent); } +- (void)qtDispatcherToQAction:(id)sender +{ + // If this window is modal, the menu bar will be modally shaddowed. + // In that case, since the window will be in the first responder chain, + // we can still catch the trigger here and forward it to the menu bar. + // This is needed as a single modal dialog on Qt should be able to access + // the application menu (e.g. quit). + [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender]; +} + +- (void)terminate:(id)sender +{ + // This function is called from the quit item in the menubar when this window + // is in the first responder chain (see also qtDispatcherToQAction above) + [NSApp terminate:sender]; +} + - (void)sendEvent:(NSEvent *)event { QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self]; @@ -185,3 +259,112 @@ QT_END_NAMESPACE return [super frameViewClassForStyleMask:styleMask]; } +-(void)registerDragTypes +{ + // Calling registerForDraggedTypes below is slow, so only do + // it once for each window, or when the custom types change. + QMacCocoaAutoReleasePool pool; + const 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:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(customTypes[i]))]; + } + [self registerForDraggedTypes:supportedTypes]; + } +} + +- (QWidget *)dragTargetHitTest:(id <NSDraggingInfo>)sender +{ + // Do a hittest to find the NSView under the + // mouse, and return the corresponding QWidget: + NSPoint windowPoint = [sender draggingLocation]; + NSView *candidateView = [[self contentView] hitTest:windowPoint]; + if (![candidateView isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) + return 0; + return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(candidateView) qt_qwidget]; +} + +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender +{ + // The user dragged something into the window. Send a draggingEntered message + // to the QWidget under the mouse. As the drag moves over the window, and over + // different widgets, we will handle enter and leave events from within + // draggingUpdated below. The reason why we handle this ourselves rather than + // subscribing for drag events directly in QCocoaView is that calling + // registerForDraggedTypes on the views will severly degrade initialization time + // for an application that uses a lot of drag subscribing widgets. + + QWidget *target = [self dragTargetHitTest:sender]; + if (!target) + return [super draggingEntered:sender]; + if (target->testAttribute(Qt::WA_DropSiteRegistered) == false) + return NSDragOperationNone; + + *currentDragTarget() = target; + return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender]; + } + +- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender +{ + QWidget *target = [self dragTargetHitTest:sender]; + if (!target) + return [super draggingUpdated:sender]; + + if (target == *currentDragTarget()) { + // The drag continues to move over the widget that we have sendt + // a draggingEntered message to. So just update the view: + return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingUpdated:sender]; + } else { + // The widget under the mouse has changed. + // So we need to fake enter/leave events: + if (*currentDragTarget()) + [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender]; + if (target->testAttribute(Qt::WA_DropSiteRegistered) == false) { + *currentDragTarget() = 0; + return NSDragOperationNone; + } + *currentDragTarget() = target; + return [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingEntered:sender]; + } +} + +- (void)draggingExited:(id < NSDraggingInfo >)sender +{ + QWidget *target = [self dragTargetHitTest:sender]; + if (!target) + return [super draggingExited:sender]; + + if (*currentDragTarget()) { + [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) draggingExited:sender]; + *currentDragTarget() = 0; + } +} + +- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender +{ + QWidget *target = [self dragTargetHitTest:sender]; + if (!target) + return [super performDragOperation:sender]; + + BOOL dropResult = NO; + if (*currentDragTarget()) { + dropResult = [reinterpret_cast<NSView *>((*currentDragTarget())->winId()) performDragOperation:sender]; + *currentDragTarget() = 0; + } + return dropResult; +} + diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index f61d2fe..8c5d166 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -81,7 +81,6 @@ Q_GLOBAL_STATIC(DnDParams, qMacDnDParams); extern void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos); // qcursor_mac.mm extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm -extern const QStringList& qEnabledDraggedTypes(); // qmime_mac.cpp extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp @@ -212,7 +211,6 @@ extern "C" { composingText = new QString(); composing = false; sendKeyEvents = true; - currentCustomTypes = 0; [self setHidden:YES]; return self; } @@ -227,36 +225,12 @@ extern "C" { object:self]; } --(void)registerDragTypes -{ - QMacCocoaAutoReleasePool pool; - // Calling registerForDraggedTypes is slow, so only do it once for each widget - // or when the custom types change. - const QStringList& customTypes = qEnabledDraggedTypes(); - if (currentCustomTypes == 0 || *currentCustomTypes != customTypes) { - if (currentCustomTypes == 0) - currentCustomTypes = new QStringList(); - *currentCustomTypes = 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:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(customTypes[i]))]; - } - [self registerForDraggedTypes:supportedTypes]; - } -} - - (void)resetCursorRects { + // [NSView addCursorRect] is slow, so bail out early if we can: + if (NSIsEmptyRect([self visibleRect])) + return; + QWidget *cursorWidget = qwidget; if (cursorWidget->testAttribute(Qt::WA_TransparentForMouseEvents)) @@ -300,15 +274,9 @@ extern "C" { - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender { - if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) - return NSDragOperationNone; + // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly + // from Cocoa. They modify the drag target, and might fake enter/leave events. NSPoint windowPoint = [sender draggingLocation]; - if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) { - // pass the drag enter event to the view underneath. - NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; - if (candidateView && candidateView != self) - return [candidateView draggingEntered:sender]; - } dragEnterSequence = [sender draggingSequenceNumber]; [self addDropData:sender]; QMimeData *mimeData = dropData; @@ -361,13 +329,9 @@ extern "C" { } - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender { + // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly + // from Cocoa. They modify the drag target, and might fake enter/leave events. NSPoint windowPoint = [sender draggingLocation]; - if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) { - // pass the drag move event to the view underneath. - NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; - if (candidateView && candidateView != self) - return [candidateView draggingUpdated:sender]; - } // in cases like QFocusFrame, the view under the mouse might // not have received the drag enter. Generate a synthetic // drag enter event for that view. @@ -417,14 +381,10 @@ extern "C" { - (void)draggingExited:(id < NSDraggingInfo >)sender { + // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly + // from Cocoa. They modify the drag target, and might fake enter/leave events. + Q_UNUSED(sender); dragEnterSequence = -1; - if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) { - // try sending the leave event to the last view which accepted drag enter. - DnDParams *dndParams = [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]; - NSView *candidateView = [[[self window] contentView] hitTest:dndParams->activeDragEnterPos]; - if (candidateView && candidateView != self) - return [candidateView draggingExited:sender]; - } // drag enter event was rejected, so ignore the move event. if (dropData) { QDragLeaveEvent de; @@ -435,14 +395,10 @@ extern "C" { - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender { + // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly + // from Cocoa. They modify the drag target, and might fake enter/leave events. NSPoint windowPoint = [sender draggingLocation]; dragEnterSequence = -1; - if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) { - // pass the drop event to the view underneath. - NSView *candidateView = [[[self window] contentView] hitTest:windowPoint]; - if (candidateView && candidateView != self) - return [candidateView performDragOperation:sender]; - } [self addDropData:sender]; NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint]; @@ -472,8 +428,6 @@ extern "C" { { delete composingText; [[NSNotificationCenter defaultCenter] removeObserver:self]; - delete currentCustomTypes; - [self unregisterDraggedTypes]; [super dealloc]; } @@ -615,6 +569,10 @@ extern "C" { - (void)updateTrackingAreas { + // [NSView addTrackingArea] is slow, so bail out early if we can: + if (NSIsEmptyRect([self visibleRect])) + return; + QMacCocoaAutoReleasePool pool; if (NSArray *trackingArray = [self trackingAreas]) { NSUInteger size = [trackingArray count]; @@ -644,6 +602,8 @@ extern "C" { - (void)mouseEntered:(NSEvent *)event { + if (qwidgetprivate->data.in_destructor) + return; QEvent enterEvent(QEvent::Enter); NSPoint windowPoint = [event locationInWindow]; NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint]; diff --git a/src/gui/kernel/qcocoaview_mac_p.h b/src/gui/kernel/qcocoaview_mac_p.h index 797b4d5..4bb10c5 100644 --- a/src/gui/kernel/qcocoaview_mac_p.h +++ b/src/gui/kernel/qcocoaview_mac_p.h @@ -87,7 +87,6 @@ Q_GUI_EXPORT int composingLength; bool sendKeyEvents; QString *composingText; - QStringList *currentCustomTypes; NSInteger dragEnterSequence; } - (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; @@ -97,7 +96,6 @@ Q_GUI_EXPORT - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender; - (void)draggingExited:(id < NSDraggingInfo >)sender; - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; -- (void)registerDragTypes; - (void)removeDropData; - (void)addDropData:(id <NSDraggingInfo>)sender; - (void)setSupportedActions:(NSDragOperation)actions; diff --git a/src/gui/kernel/qcocoawindow_mac.mm b/src/gui/kernel/qcocoawindow_mac.mm index a644dfe..f1b642b 100644 --- a/src/gui/kernel/qcocoawindow_mac.mm +++ b/src/gui/kernel/qcocoawindow_mac.mm @@ -46,6 +46,7 @@ #import <private/qcocoaview_mac_p.h> #import <private/qt_cocoa_helpers_mac_p.h> #import <private/qcocoawindowcustomthemeframe_mac_p.h> +#import <private/qcocoaapplication_mac_p.h> #include <QtGui/QWidget> diff --git a/src/gui/kernel/qcocoawindow_mac_p.h b/src/gui/kernel/qcocoawindow_mac_p.h index c0d8252..403a1a5 100644 --- a/src/gui/kernel/qcocoawindow_mac_p.h +++ b/src/gui/kernel/qcocoawindow_mac_p.h @@ -70,9 +70,12 @@ QT_FORWARD_DECLARE_CLASS(QStringList); @interface QT_MANGLE_NAMESPACE(QCocoaWindow) : NSWindow { bool leftButtonIsRightButton; + QStringList *currentCustomDragTypes; } + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; +- (void)registerDragTypes; + @end #endif diff --git a/src/gui/kernel/qdnd_s60.cpp b/src/gui/kernel/qdnd_s60.cpp index 24f0090..1aa30af 100644 --- a/src/gui/kernel/qdnd_s60.cpp +++ b/src/gui/kernel/qdnd_s60.cpp @@ -52,7 +52,7 @@ #include "qdnd_p.h" #include "qt_s60_p.h" -#include <COECNTRL.H> +#include <coecntrl.h> // pointer cursor #include <w32std.h> #include <gdi.h> diff --git a/src/gui/kernel/qeventdispatcher_mac.mm b/src/gui/kernel/qeventdispatcher_mac.mm index eda75db..2a1da41 100644 --- a/src/gui/kernel/qeventdispatcher_mac.mm +++ b/src/gui/kernel/qeventdispatcher_mac.mm @@ -90,11 +90,16 @@ #ifndef QT_NO_THREAD # include "qmutex.h" +#endif QT_BEGIN_NAMESPACE QT_USE_NAMESPACE -#endif + +/***************************************************************************** + Internal variables and functions + *****************************************************************************/ +bool qt_blockCocoaSettingModalWindowLevel = false; /***************************************************************************** Externals @@ -540,6 +545,12 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherMac); d->interrupt = false; + +#ifdef QT_MAC_USE_COCOA + bool interruptLater = false; + QtMacInterruptDispatcherHelp::cancelInterruptLater(); +#endif + // In case we end up recursing while we now process events, make sure // that we send remaining posted Qt events before this call returns: wakeUp(); @@ -554,25 +565,26 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) QMacCocoaAutoReleasePool pool; NSEvent* event = 0; - // If Qt is used as a plugin, or just added into a native cocoa - // application, we should not run or stop NSApplication; - // This will be done from outside Qt. - // And if processEvents is called manually (rather than from QEventLoop), we - // cannot enter a tight loop and block the call, but instead return after one flush: - bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning]; - bool canExec_Qt = flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec; + // If Qt is used as a plugin, or as an extension in a native cocoa + // application, we should not run or stop NSApplication; This will be + // done from the application itself. And if processEvents is called + // manually (rather than from a QEventLoop), we cannot enter a tight + // loop and block this call, but instead we need to return after one flush: + const bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning]; + const bool canExec_Qt = flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec; if (canExec_Qt && canExec_3rdParty) { // We can use exec-mode, meaning that we can stay in a tight loop until - // interrupted. This is mostly an optimization, but it also allow us - // to use [NSApp run], which is the recommended way of running applications - // in cocoa. [NSApp run] should be called at least once for any cocoa app. + // interrupted. This is mostly an optimization, but it allow us to use + // [NSApp run], which is the normal code path for cocoa applications. if (NSModalSession session = d->currentModalSession()) { QBoolBlocker execGuard(d->currentExecIsNSAppRun, false); - while (!d->interrupt && [NSApp runModalSession:session] == NSRunContinuesResponse) + + while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt) qt_mac_waitForMoreModalSessionEvents(); + if (!d->interrupt && session == d->currentModalSessionCached) { - // INVARIANT: Someone called e.g. [NSApp stopModal:] from outside the event + // Someone called [NSApp stopModal:] from outside the event // dispatcher (e.g to stop a native dialog). But that call wrongly stopped // 'session' as well. As a result, we need to restart all internal sessions: d->temporarilyStopAllModalSessions(); @@ -583,54 +595,63 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) [NSApp run]; } retVal = true; - } else do { - // INVARIANT: We cannot block the thread (and run in a tight loop). + } else { + // We cannot block the thread (and run in a tight loop). // Instead we will process all current pending events and return. - bool mustRelease = false; - - if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) { - // Process a pending user input event - mustRelease = true; - event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst()); - } else { - if (NSModalSession session = d->currentModalSession()) { - if (flags & QEventLoop::WaitForMoreEvents) - qt_mac_waitForMoreModalSessionEvents(); - NSInteger status = [NSApp runModalSession:session]; - if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) { - // INVARIANT: Someone called e.g. [NSApp stopModal:] from outside the event - // dispatcher (e.g to stop a native dialog). But that call wrongly stopped - // 'session' as well. As a result, we need to restart all internal sessions: - d->temporarilyStopAllModalSessions(); - } - retVal = true; - break; + d->ensureNSAppInitialized(); + do { + bool releaseEvent = false; + + if (!(flags & QEventLoop::ExcludeUserInputEvents) + && !d->queuedUserInputEvents.isEmpty()) { + // Process a pending user input event + releaseEvent = true; + event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst()); } else { - event = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:nil - inMode:NSDefaultRunLoopMode - dequeue: YES]; - - if (event != nil) { - if (flags & QEventLoop::ExcludeUserInputEvents) { - if (IsMouseOrKeyEvent(event)) { - // retain event here? - [event retain]; - d->queuedUserInputEvents.append(event); - continue; + if (NSModalSession session = d->currentModalSession()) { + if (flags & QEventLoop::WaitForMoreEvents) + qt_mac_waitForMoreModalSessionEvents(); + NSInteger status = [NSApp runModalSession:session]; + if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) { + // INVARIANT: Someone called [NSApp stopModal:] from outside the event + // dispatcher (e.g to stop a native dialog). But that call wrongly stopped + // 'session' as well. As a result, we need to restart all internal sessions: + d->temporarilyStopAllModalSessions(); + } + retVal = true; + break; + } else { + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:nil + inMode:NSDefaultRunLoopMode + dequeue: YES]; + + if (event != nil) { + if (flags & QEventLoop::ExcludeUserInputEvents) { + if (IsMouseOrKeyEvent(event)) { + [event retain]; + d->queuedUserInputEvents.append(event); + continue; + } } } } } - } - if (event) { - if (!filterEvent(event) && qt_mac_send_event(flags, event, 0)) - retVal = true; - if (mustRelease) - [event release]; - } - } while(!d->interrupt && event != nil); - + if (event) { + if (!filterEvent(event) && qt_mac_send_event(flags, event, 0)) + retVal = true; + if (releaseEvent) + [event release]; + } + } while (!d->interrupt && event != nil); + + // Since the window that holds modality might have changed while processing + // events, we we need to interrupt when we return back the previous process + // event recursion to ensure that we spin the correct modal session. + // We do the interruptLater at the end of the function to ensure that we don't + // disturb the 'wait for more events' below (as deleteLater will post an event): + interruptLater = true; + } #else do { EventRef event; @@ -682,25 +703,19 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) } } + // If we're interrupted, we need to interrupt the _current_ + // recursion as well to check if it is still supposed to be + // executing. This way we wind down the stack until we land + // on a recursion that again calls processEvents (typically + // from QEventLoop), and set interrupt to false: + if (d->interrupt) + interrupt(); + #ifdef QT_MAC_USE_COCOA - // In case we _now_ process events using [NSApp run], we need to stop it to - // ensure that: - // 1. the QEventLoop that called us is still executing, or - // 2. we have a modal session that needs to be spun instead. - // In case this is a plain call to processEvents (perhaps from a loop) - // from the application (rather than from a QEventLoop), we delay the - // interrupting until we/ actually enter a lower loop level (hence the - // deffered delete of the object below): - QtMacInterruptDispatcherHelp::interruptLater(); + if (interruptLater) + QtMacInterruptDispatcherHelp::interruptLater(); #endif - if (d->interrupt) { - // We should continue to leave all recursion to processEvents until - // processEvents is called again (e.g. from a QEventLoop that - // was not yet told to quit: - interrupt(); - } - return retVal; } @@ -729,39 +744,60 @@ void QEventDispatcherMac::flush() *****************************************************************************/ MacTimerHash QEventDispatcherMacPrivate::macTimerHash; bool QEventDispatcherMacPrivate::blockSendPostedEvents = false; +bool QEventDispatcherMacPrivate::interrupt = false; #ifdef QT_MAC_USE_COCOA QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack; bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false; +bool QEventDispatcherMacPrivate::modalSessionsTemporarilyStopped = false; bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false; +bool QEventDispatcherMacPrivate::cleanupModalSessionsNeeded = false; NSModalSession QEventDispatcherMacPrivate::currentModalSessionCached = 0; -int QEventDispatcherMacPrivate::activeModalSessionCount() +void QEventDispatcherMacPrivate::ensureNSAppInitialized() { - // Returns the number of modal sessions created - // (and not just pushed onto the stack, pending to be created) - int count = 0; - for (int i=cocoaModalSessionStack.size()-1; i>=0; --i) { - QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; - if (info.session) - ++count; - } - return count; + // Some elements in Cocoa require NSApplication to be running before + // they get fully initialized, in particular the menu bar. This + // function is intended for cases where a dialog is told to execute before + // QApplication::exec is called, or the application spins the events loop + // manually rather than calling QApplication:exec. + // The function makes sure that NSApplication starts running, but stops + // it again as soon as the send posted events callback is called. That way + // we let Cocoa finish the initialization it seems to need. We'll only + // apply this trick at most once for any application, and we avoid doing it + // for the common case where main just starts QApplication::exec. + if (nsAppRunCalledByQt || [NSApp isRunning]) + return; + nsAppRunCalledByQt = true; + QBoolBlocker block1(interrupt, true); + QBoolBlocker block2(currentExecIsNSAppRun, true); + [NSApp run]; } void QEventDispatcherMacPrivate::temporarilyStopAllModalSessions() { - // Stop all created modal session, and as such, make then - // pending again. The next call to currentModalSession will - // recreate the session on top again: + // Flush, and Stop, all created modal session, and as + // such, make them pending again. The next call to + // currentModalSession will recreate them again. The + // reason to stop all session like this is that otherwise + // a call [NSApp stop] would not stop NSApp, but rather + // the current modal session. So if we need to stop NSApp + // we need to stop all the modal session first. To avoid changing + // the stacking order of the windows while doing so, we put + // up a block that is used in QCocoaWindow and QCocoaPanel: + QBoolBlocker block1(blockSendPostedEvents, true); + QBoolBlocker block2(qt_blockCocoaSettingModalWindowLevel, true); + int stackSize = cocoaModalSessionStack.size(); for (int i=stackSize-1; i>=0; --i) { QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; if (info.session) { + [NSApp runModalSession:info.session]; [NSApp endModalSession:info.session]; info.session = 0; } } + modalSessionsTemporarilyStopped = true; currentModalSessionCached = 0; } @@ -775,23 +811,6 @@ NSModalSession QEventDispatcherMacPrivate::currentModalSession() if (cocoaModalSessionStack.isEmpty()) return 0; - // Since this code will end up calling our Qt event handler - // (also from beginModalSessionForWindow), we need to block - // that to avoid side effects of events beeing delivered: - QBoolBlocker block(blockSendPostedEvents, true); - - if (![NSApp isRunning]) { - // Sadly, we need to introduce this little event flush - // to stop dialogs from blinking/poping in front if a - // modal session restart was needed: - while (NSEvent *event = [NSApp nextEventMatchingMask:0 - untilDate:nil - inMode:NSDefaultRunLoopMode - dequeue: YES]) { - qt_mac_send_event(0, event, 0); - } - } - int sessionCount = cocoaModalSessionStack.size(); for (int i=0; i<sessionCount; ++i) { QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; @@ -804,11 +823,28 @@ NSModalSession QEventDispatcherMacPrivate::currentModalSession() NSWindow *window = qt_mac_window_for(info.widget); if (!window) continue; + + ensureNSAppInitialized(); + QBoolBlocker block1(blockSendPostedEvents, true); info.session = [NSApp beginModalSessionForWindow:window]; } currentModalSessionCached = info.session; } + if (modalSessionsTemporarilyStopped && currentModalSessionCached) { + // After a call to temporarilyStopAllModalSessions, cocoa have + // now posted events to restore ended modal session windows to + // the correct window level. Those events will be processed + // _after_ our new calls to beginModalSessionForWindow have + // taken effect, which will end up stacking the windows wrong on + // screen. To work around this, we block cocoa from changing the + // stacking order of the windows, and flush out the pending events + // (the block is used in QCocoaWindow and QCocoaPanel): + QBoolBlocker block1(blockSendPostedEvents, true); + QBoolBlocker block2(qt_blockCocoaSettingModalWindowLevel, true); + [NSApp runModalSession:currentModalSessionCached]; + } + modalSessionsTemporarilyStopped = false; return currentModalSessionCached; } @@ -844,6 +880,34 @@ void QEventDispatcherMacPrivate::updateChildrenWorksWhenModal() } } +void QEventDispatcherMacPrivate::cleanupModalSessions() +{ + // Go through the list of modal sessions, and end those + // that no longer has a widget assosiated; no widget means + // the the session has logically ended. The reason we wait like + // this to actually end the sessions for real (rather than at the + // point they were marked as stopped), is that ending a session + // when no other session runs below it on the stack will make cocoa + // drop some events on the floor. + QMacCocoaAutoReleasePool pool; + int stackSize = cocoaModalSessionStack.size(); + + for (int i=stackSize-1; i>=0; --i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (info.widget) { + currentModalSessionCached = info.session; + break; + } + cocoaModalSessionStack.remove(i); + currentModalSessionCached = 0; + if (info.session) + [NSApp endModalSession:info.session]; + } + + updateChildrenWorksWhenModal(); + cleanupModalSessionsNeeded = false; +} + void QEventDispatcherMacPrivate::beginModalSession(QWidget *widget) { // Add a new, empty (null), NSModalSession to the stack. @@ -852,7 +916,7 @@ void QEventDispatcherMacPrivate::beginModalSession(QWidget *widget) // is non-zero, and the session pointer is zero (it will become active upon a call to // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if // the widget pointer is zero, and the session pointer is non-zero (it will be fully - // stopped in endModalSession(). + // stopped in cleanupModalSessions()). QCocoaModalSessionInfo info = {widget, 0}; cocoaModalSessionStack.push(info); updateChildrenWorksWhenModal(); @@ -869,38 +933,21 @@ void QEventDispatcherMacPrivate::endModalSession(QWidget *widget) int stackSize = cocoaModalSessionStack.size(); for (int i=stackSize-1; i>=0; --i) { QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; - if (info.widget == widget) + if (info.widget == widget) { info.widget = 0; - } - - // Now we stop, and remove, all sessions marked as pending - // to be stopped on _top_ of the stack, if any: - bool needToInterruptEventDispatcher = false; - bool needToUpdateChildrenWorksWhenModal = false; - - for (int i=stackSize-1; i>=0; --i) { - QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; - if (info.widget) - break; - cocoaModalSessionStack.remove(i); - needToUpdateChildrenWorksWhenModal = true; - currentModalSessionCached = 0; - if (info.session) { - [NSApp endModalSession:info.session]; - needToInterruptEventDispatcher = true; + if (i == stackSize-1) { + // The top sessions ended. Interrupt the event dispatcher + // to start spinning the correct session immidiatly: + cleanupModalSessionsNeeded = true; + QEventDispatcherMac::instance()->interrupt(); + } } } - - if (needToUpdateChildrenWorksWhenModal) - updateChildrenWorksWhenModal(); - if (needToInterruptEventDispatcher) - QEventDispatcherMac::instance()->interrupt(); } #endif QEventDispatcherMacPrivate::QEventDispatcherMacPrivate() - : interrupt(false) { } @@ -959,13 +1006,39 @@ Boolean QEventDispatcherMacPrivate::postedEventSourceEqualCallback(const void *i inline static void processPostedEvents(QEventDispatcherMacPrivate *const d, const bool blockSendPostedEvents) { - if (blockSendPostedEvents || d->interrupt) { + if (blockSendPostedEvents) { + // We're told to not send posted events (because the event dispatcher + // is currently working on setting up the correct session to run). But + // we still need to make sure that we don't fall asleep until pending events + // are sendt, so we just signal this need, and return: CFRunLoopSourceSignal(d->postedEventsSource); - } else { - if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) { - d->lastSerial = d->serialNumber; - QApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + return; + } + +#ifdef QT_MAC_USE_COCOA + if (d->cleanupModalSessionsNeeded) + d->cleanupModalSessions(); +#endif + + if (d->interrupt) { +#ifdef QT_MAC_USE_COCOA + if (d->currentExecIsNSAppRun) { + // The event dispatcher has been interrupted. But since + // [NSApplication run] is running the event loop, we + // delayed stopping it until now (to let cocoa process + // pending cocoa events first). + if (d->currentModalSessionCached) + d->temporarilyStopAllModalSessions(); + [NSApp stop:NSApp]; + d->cancelWaitForMoreEvents(); } +#endif + return; + } + + if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) { + d->lastSerial = d->serialNumber; + QApplicationPrivate::sendPostedEvents(0, 0, d->threadData); } } @@ -975,6 +1048,9 @@ void QEventDispatcherMacPrivate::firstLoopEntry(CFRunLoopObserverRef ref, { Q_UNUSED(ref); Q_UNUSED(activity); +#ifdef QT_MAC_USE_COCOA + QApplicationPrivate::qt_initAfterNSAppStarted(); +#endif processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); } @@ -983,6 +1059,19 @@ void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info) processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); } +#ifdef QT_MAC_USE_COCOA +void QEventDispatcherMacPrivate::cancelWaitForMoreEvents() +{ + // In case the event dispatcher is waiting for more + // events somewhere, we post a dummy event to wake it up: + QMacCocoaAutoReleasePool pool; + static const short NSAppShouldStopForQt = SHRT_MAX; + [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint + modifierFlags:0 timestamp:0. windowNumber:0 context:0 + subtype:NSAppShouldStopForQt data1:0 data2:0] atStart:NO]; +} +#endif + void QEventDispatcherMac::interrupt() { Q_D(QEventDispatcherMac); @@ -992,20 +1081,14 @@ void QEventDispatcherMac::interrupt() #ifndef QT_MAC_USE_COCOA CFRunLoopStop(mainRunLoop()); #else - QMacCocoaAutoReleasePool pool; - // In case we wait for more events inside - // processEvents (or NSApp run), post a dummy to wake it up: - static const short NSAppShouldStopForQt = SHRT_MAX; - [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint - modifierFlags:0 timestamp:0. windowNumber:0 context:0 - subtype:NSAppShouldStopForQt data1:0 data2:0] atStart:NO]; - - if (d->activeModalSessionCount() == 0) { - // We should only stop NSApp if we actually started it (and - // not some 3rd party application, e.g. if we are a plugin). - if (d->nsAppRunCalledByQt) - [NSApp stop:NSApp]; - } + // We do nothing more here than setting d->interrupt = true, and + // poke the event loop if it is sleeping. Actually stopping + // NSApp, or the current modal session, is done inside the send + // posted events callback. We do this to ensure that all current pending + // cocoa events gets delivered before we stop. Otherwise, if we now stop + // the last event loop recursion, cocoa will just drop pending posted + // events on the floor before we get a chance to reestablish a new session. + d->cancelWaitForMoreEvents(); #endif } @@ -1046,17 +1129,18 @@ QEventDispatcherMac::~QEventDispatcherMac() CFRelease(d->firstTimeObserver); } -///////////////////////////////////////////////////////////////////////////// - #ifdef QT_MAC_USE_COCOA QtMacInterruptDispatcherHelp* QtMacInterruptDispatcherHelp::instance = 0; QtMacInterruptDispatcherHelp::QtMacInterruptDispatcherHelp() : cancelled(false) { - // This is the whole point of encapsulation this code - // inside a class; we can make the code (inside destructor) - // execute on lower loop level: + // The whole point of this class is that we enable a way to interrupt + // the event dispatcher when returning back to a lower recursion level + // than where interruptLater was called. This is needed to detect if + // [NSApp run] should still be running at the recursion level it is at. + // Since the interrupt is canceled if processEvents is called before + // this object gets deleted, we also avoid interrupting unnecessary. deleteLater(); } @@ -1064,34 +1148,26 @@ QtMacInterruptDispatcherHelp::~QtMacInterruptDispatcherHelp() { if (cancelled) return; - instance = 0; - - if (QEventDispatcherMacPrivate::currentExecIsNSAppRun) { - int activeCount = QEventDispatcherMacPrivate::activeModalSessionCount(); - if (activeCount > 0) { - // The problem we now have hit: [NSApp stop] will not stop NSApp - // if a session is active; it will stop the session instead. - // So to stop NSApp, we need to temporarily stop all the - // sessions, then stop NSApp, then restart the session on top again. - // We need to do this to ensure that we're not stuck inside - // [NSApp run] when we really should be running a modal session: - QEventDispatcherMacPrivate::temporarilyStopAllModalSessions(); - } - } - // Always interrupt once more in case the modal session stack changed - // while processEvents was called manually from within the application: QEventDispatcherMac::instance()->interrupt(); } -void QtMacInterruptDispatcherHelp::interruptLater() { - if (instance) { - instance->cancelled = true; - delete instance; - } +void QtMacInterruptDispatcherHelp::cancelInterruptLater() +{ + if (!instance) + return; + instance->cancelled = true; + delete instance; + instance = 0; +} + +void QtMacInterruptDispatcherHelp::interruptLater() +{ + cancelInterruptLater(); instance = new QtMacInterruptDispatcherHelp; } #endif QT_END_NAMESPACE + diff --git a/src/gui/kernel/qeventdispatcher_mac_p.h b/src/gui/kernel/qeventdispatcher_mac_p.h index a335f16..e932532 100644 --- a/src/gui/kernel/qeventdispatcher_mac_p.h +++ b/src/gui/kernel/qeventdispatcher_mac_p.h @@ -174,13 +174,17 @@ public: static QStack<QCocoaModalSessionInfo> cocoaModalSessionStack; static bool currentExecIsNSAppRun; static bool nsAppRunCalledByQt; + static bool cleanupModalSessionsNeeded; + static bool modalSessionsTemporarilyStopped; static NSModalSession currentModalSessionCached; - static void updateChildrenWorksWhenModal(); static NSModalSession currentModalSession(); - static int activeModalSessionCount(); + static void updateChildrenWorksWhenModal(); static void temporarilyStopAllModalSessions(); static void beginModalSession(QWidget *widget); static void endModalSession(QWidget *widget); + static void cancelWaitForMoreEvents(); + static void cleanupModalSessions(); + static void ensureNSAppInitialized(); #endif MacSocketHash macSockets; @@ -190,7 +194,7 @@ public: CFRunLoopObserverRef firstTimeObserver; QAtomicInt serialNumber; int lastSerial; - bool interrupt; + static bool interrupt; private: static Boolean postedEventSourceEqualCallback(const void *info1, const void *info2); static void postedEventsSourcePerformCallback(void *info); @@ -211,6 +215,7 @@ class QtMacInterruptDispatcherHelp : public QObject public: static void interruptLater(); + static void cancelInterruptLater(); }; #endif diff --git a/src/gui/kernel/qguieventdispatcher_glib.cpp b/src/gui/kernel/qguieventdispatcher_glib.cpp index 7b30741..ac2b5f7 100644 --- a/src/gui/kernel/qguieventdispatcher_glib.cpp +++ b/src/gui/kernel/qguieventdispatcher_glib.cpp @@ -216,4 +216,9 @@ void QGuiEventDispatcherGlib::startingUp() g_source_add_poll(&d->x11EventSource->source, &d->x11EventSource->pollfd); } +void QGuiEventDispatcherGlib::flush() +{ + XFlush(X11->display); +} + QT_END_NAMESPACE diff --git a/src/gui/kernel/qguieventdispatcher_glib_p.h b/src/gui/kernel/qguieventdispatcher_glib_p.h index ff778cc..758650b 100644 --- a/src/gui/kernel/qguieventdispatcher_glib_p.h +++ b/src/gui/kernel/qguieventdispatcher_glib_p.h @@ -71,6 +71,7 @@ public: bool processEvents(QEventLoop::ProcessEventsFlags flags); void startingUp(); + void flush(); }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qkeysequence.cpp b/src/gui/kernel/qkeysequence.cpp index 2a13546..99bf971 100644 --- a/src/gui/kernel/qkeysequence.cpp +++ b/src/gui/kernel/qkeysequence.cpp @@ -861,6 +861,8 @@ QKeySequence::QKeySequence() Up to four key codes may be entered by separating them with commas, e.g. "Alt+X,Ctrl+S,Q". + \a key should be in NativeText format. + This constructor is typically used with \link QObject::tr() tr \endlink(), so that shortcut keys can be replaced in translations: @@ -877,6 +879,16 @@ QKeySequence::QKeySequence(const QString &key) } /*! + \since 4.x + Creates a key sequence from the \a key string based on \a format. +*/ +QKeySequence::QKeySequence(const QString &key, QKeySequence::SequenceFormat format) +{ + d = new QKeySequencePrivate(); + assign(key, format); +} + +/*! Constructs a key sequence with up to 4 keys \a k1, \a k2, \a k3 and \a k4. @@ -1055,9 +1067,24 @@ QKeySequence QKeySequence::mnemonic(const QString &text) contain up to four key codes, provided they are separated by a comma; for example, "Alt+X,Ctrl+S,Z". The return value is the number of key codes added. + \a keys should be in NativeText format. */ int QKeySequence::assign(const QString &ks) { + return assign(ks, NativeText); +} + +/*! + \fn int QKeySequence::assign(const QString &keys, QKeySequence::SequenceFormat format) + \since 4.x + + Adds the given \a keys to the key sequence (based on \a format). + \a keys may contain up to four key codes, provided they are + separated by a comma; for example, "Alt+X,Ctrl+S,Z". The return + value is the number of key codes added. +*/ +int QKeySequence::assign(const QString &ks, QKeySequence::SequenceFormat format) +{ QString keyseq = ks; QString part; int n = 0; @@ -1086,7 +1113,7 @@ int QKeySequence::assign(const QString &ks) } part = keyseq.left(-1 == p ? keyseq.length() : p - diff); keyseq = keyseq.right(-1 == p ? 0 : keyseq.length() - (p + 1)); - d->key[n] = decodeString(part); + d->key[n] = QKeySequencePrivate::decodeString(part, format); ++n; } return n; @@ -1557,12 +1584,7 @@ QString QKeySequence::toString(SequenceFormat format) const */ QKeySequence QKeySequence::fromString(const QString &str, SequenceFormat format) { - QStringList sl = str.split(QLatin1String(", ")); - int keys[4] = {0, 0, 0, 0}; - int total = qMin(sl.count(), 4); - for (int i = 0; i < total; ++i) - keys[i] = QKeySequencePrivate::decodeString(sl[i], format); - return QKeySequence(keys[0], keys[1], keys[2], keys[3]); + return QKeySequence(str, format); } /***************************************************************************** diff --git a/src/gui/kernel/qkeysequence.h b/src/gui/kernel/qkeysequence.h index 1409e28..5a973e3 100644 --- a/src/gui/kernel/qkeysequence.h +++ b/src/gui/kernel/qkeysequence.h @@ -141,8 +141,14 @@ public: Quit }; + enum SequenceFormat { + NativeText, + PortableText + }; + QKeySequence(); QKeySequence(const QString &key); + QKeySequence(const QString &key, SequenceFormat format); QKeySequence(int k1, int k2 = 0, int k3 = 0, int k4 = 0); QKeySequence(const QKeySequence &ks); QKeySequence(StandardKey key); @@ -160,11 +166,6 @@ public: #endif }; - enum SequenceFormat { - NativeText, - PortableText - }; - QString toString(SequenceFormat format = PortableText) const; static QKeySequence fromString(const QString &str, SequenceFormat format = PortableText); @@ -194,6 +195,7 @@ private: static int decodeString(const QString &ks); static QString encodeString(int key); int assign(const QString &str); + int assign(const QString &str, SequenceFormat format); void setKey(int key, int index); QKeySequencePrivate *d; diff --git a/src/gui/kernel/qmime_mac.cpp b/src/gui/kernel/qmime_mac.cpp index 0431f2f..775bf1b 100644 --- a/src/gui/kernel/qmime_mac.cpp +++ b/src/gui/kernel/qmime_mac.cpp @@ -154,6 +154,7 @@ CFStringRef qt_mac_mime_typeUTI = CFSTR("com.pasteboard.trolltech.marker"); \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 @@ -909,6 +910,61 @@ QList<QByteArray> QMacPasteboardMimeUrl::convertFromMime(const QString &mime, QV 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; +} + #ifdef QT3_SUPPORT class QMacPasteboardMimeQt3Any : public QMacPasteboardMime { private: @@ -1116,6 +1172,7 @@ void QMacPasteboardMime::initialize() new QMacPasteboardMimeFileUri; new QMacPasteboardMimeUrl; new QMacPasteboardMimeTypeName; + new QMacPasteboardMimeVCard; //make sure our "non-standard" types are always last! --Sam new QMacPasteboardMimeAny; #ifdef QT3_SUPPORT diff --git a/src/gui/kernel/qnsthemeframe_mac_p.h b/src/gui/kernel/qnsthemeframe_mac_p.h index 77a6963..d061b1b 100644 --- a/src/gui/kernel/qnsthemeframe_mac_p.h +++ b/src/gui/kernel/qnsthemeframe_mac_p.h @@ -157,7 +157,7 @@ - (void)resetCursorRects; - (char)shouldBeTreatedAsInkEvent:fp8; - (char)_shouldBeTreatedAsInkEventInInactiveWindow:fp8; -- hitTest:(struct _NSPoint)fp8; +//- hitTest:(struct _NSPoint)fp8; // collides with hittest in qcocoasharedwindowmethods_mac_p.h - (NSRect)_leftGroupRect; - (NSRect)_rightGroupRect; - (void)_updateWidgets; diff --git a/src/gui/kernel/qsound_s60.cpp b/src/gui/kernel/qsound_s60.cpp index 1832b85..df2830b 100644 --- a/src/gui/kernel/qsound_s60.cpp +++ b/src/gui/kernel/qsound_s60.cpp @@ -51,7 +51,7 @@ #include <private/qcore_symbian_p.h> #include <e32std.h> -#include <MdaAudioSamplePlayer.h> +#include <mdaaudiosampleplayer.h> QT_BEGIN_NAMESPACE diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm index e36ab9b..13c25fd 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm @@ -143,6 +143,9 @@ extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds) { +#ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; +#endif OSWindowRef wnd = static_cast<OSWindowRef>(window); if (wnd) { QWidget *widget; @@ -1278,4 +1281,19 @@ void qt_cocoaChangeOverrideCursor(const QCursor &cursor) } #endif +@implementation DebugNSApplication { +} +- (void)sendEvent:(NSEvent *)event +{ + NSLog(@"NSAppDebug: sendEvent: %@", event); + return [super sendEvent:event]; +} + +- (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)sender +{ + NSLog(@"NSAppDebug: sendAction: %s to %@ from %@", anAction, aTarget, sender); + return [super sendAction:anAction to:aTarget from:sender]; +} +@end + QT_END_NAMESPACE diff --git a/src/gui/kernel/qt_cocoa_helpers_mac_p.h b/src/gui/kernel/qt_cocoa_helpers_mac_p.h index 6f0c4ce..ace8255 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac_p.h +++ b/src/gui/kernel/qt_cocoa_helpers_mac_p.h @@ -181,6 +181,10 @@ inline QString qt_mac_NSStringToQString(const NSString *nsstr) inline NSString *qt_mac_QStringToNSString(const QString &qstr) { return [reinterpret_cast<const NSString *>(QCFString::toCFStringRef(qstr)) autorelease]; } + +@interface DebugNSApplication : NSApplication {} +@end + #endif QT_END_NAMESPACE diff --git a/src/gui/kernel/qt_x11_p.h b/src/gui/kernel/qt_x11_p.h index d110084..b2ce754 100644 --- a/src/gui/kernel/qt_x11_p.h +++ b/src/gui/kernel/qt_x11_p.h @@ -331,7 +331,7 @@ struct QXdndDropTransaction class QMimeData; struct QX11Data; -extern QX11Data *qt_x11Data; +extern Q_GUI_EXPORT QX11Data *qt_x11Data; enum DesktopEnvironment { DE_UNKNOWN, diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index 3dbc843..3ff46f5 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -1898,13 +1898,15 @@ void QWidgetPrivate::determineWindowClass() wclass = kDocumentWindowClass; else if(popup || (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5 && type == Qt::SplashScreen)) wclass = kModalWindowClass; - else if(q->testAttribute(Qt::WA_ShowModal) || type == Qt::Dialog) + else if(type == Qt::Dialog) wclass = kMovableModalWindowClass; else if(type == Qt::ToolTip) wclass = kHelpWindowClass; else if(type == Qt::Tool || (QSysInfo::MacintoshVersion < QSysInfo::MV_10_5 && type == Qt::SplashScreen)) wclass = kFloatingWindowClass; + else if(q->testAttribute(Qt::WA_ShowModal)) + wclass = kMovableModalWindowClass; else wclass = kDocumentWindowClass; @@ -2008,8 +2010,6 @@ void QWidgetPrivate::determineWindowClass() for(int i = 0; tmp_wattr && known_attribs[i].name; i++) { if((tmp_wattr & known_attribs[i].tag) == known_attribs[i].tag) { tmp_wattr ^= known_attribs[i].tag; - qDebug("Qt: internal: * %s %s", known_attribs[i].name, - (GetAvailableWindowAttributes(wclass) & known_attribs[i].tag) ? "" : "(*)"); } } if(tmp_wattr) @@ -2204,6 +2204,41 @@ void QWidgetPrivate::finishCreateWindow_sys_Carbon(OSWindowRef windowRef) applyMaxAndMinSizeOnWindow(); } #else // QT_MAC_USE_COCOA + +void QWidgetPrivate::setWindowLevel() +{ + Q_Q(QWidget); + const QWidget * const windowParent = q->window()->parentWidget(); + const QWidget * const primaryWindow = windowParent ? windowParent->window() : 0; + NSInteger winLevel = -1; + + if (q->windowType() == Qt::Popup) { + winLevel = NSPopUpMenuWindowLevel; + // Popup should be in at least the same level as its parent. + if (primaryWindow) { + OSWindowRef parentRef = qt_mac_window_for(primaryWindow); + winLevel = qMax([parentRef level], winLevel); + } + } else if (q->windowType() == Qt::Tool) { + winLevel = NSFloatingWindowLevel; + } else if (q->windowType() == Qt::Dialog) { + // Correct modality level (NSModalPanelWindowLevel) will be + // set by cocoa when creating a modal session later. + winLevel = NSNormalWindowLevel; + } + + // StayOnTop window should appear above Tool windows. + if (data.window_flags & Qt::WindowStaysOnTopHint) + winLevel = NSPopUpMenuWindowLevel; + // Tooltips should appear above StayOnTop windows. + if (q->windowType() == Qt::ToolTip) + winLevel = NSScreenSaverWindowLevel; + // All other types are Normal level. + if (winLevel == -1) + winLevel = NSNormalWindowLevel; + [qt_mac_window_for(q) setLevel:winLevel]; +} + void QWidgetPrivate::finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ voidWindowRef) { Q_Q(QWidget); @@ -2276,6 +2311,10 @@ void QWidgetPrivate::finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ voidWin q->setAttribute(Qt::WA_WState_WindowOpacitySet, false); } + if (qApp->overrideCursor()) + [windowRef disableCursorRects]; + + setWindowLevel(); macUpdateHideOnSuspend(); macUpdateOpaqueSizeGrip(); macUpdateIgnoreMouseEvents(); @@ -2706,6 +2745,35 @@ void QWidgetPrivate::transferChildren() } } +#ifdef QT_MAC_USE_COCOA +void QWidgetPrivate::setSubWindowStacking(bool set) +{ + Q_Q(QWidget); + if (!q->isWindow() || !q->testAttribute(Qt::WA_WState_Created)) + return; + + if (QWidget *parent = q->parentWidget()) { + if (parent->testAttribute(Qt::WA_WState_Created)) { + if (set) + [qt_mac_window_for(parent) addChildWindow:qt_mac_window_for(q) ordered:NSWindowAbove]; + else + [qt_mac_window_for(parent) removeChildWindow:qt_mac_window_for(q)]; + } + } + + QList<QWidget *> widgets = q->findChildren<QWidget *>(); + for (int i=0; i<widgets.size(); ++i) { + QWidget *child = widgets.at(i); + if (child->isWindow() && child->testAttribute(Qt::WA_WState_Created)) { + if (set) + [qt_mac_window_for(q) addChildWindow:qt_mac_window_for(child) ordered:NSWindowAbove]; + else + [qt_mac_window_for(q) removeChildWindow:qt_mac_window_for(child)]; + } + } +} +#endif + void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) { Q_Q(QWidget); @@ -2860,7 +2928,6 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) current = current->parentWidget(); } } - invalidateBuffer(q->rect()); qt_event_request_window_change(q); } @@ -3322,13 +3389,23 @@ void QWidgetPrivate::show_sys() #else // sync the opacity value back (in case of a fade). [window setAlphaValue:q->windowOpacity()]; - [window makeKeyAndOrderFront:window]; - - // If this window is app modal, we need to start spinning - // a modal session for it. Interrupting - // the event dispatcher will make this happend: - if (data.window_modality == Qt::ApplicationModal) - QEventDispatcherMac::instance()->interrupt(); + setSubWindowStacking(true); + + QWidget *top = 0; + if (QApplicationPrivate::tryModalHelper(q, &top)) { + [window makeKeyAndOrderFront:window]; + // If this window is app modal, we need to start spinning + // a modal session for it. Interrupting + // the event dispatcher will make this happend: + if (data.window_modality == Qt::ApplicationModal) + QEventDispatcherMac::instance()->interrupt(); + } else { + // The window is modally shaddowed, so we need to make + // sure that we don't pop in front of the modal window: + [window orderFront:window]; + if (NSWindow *modalWin = qt_mac_window_for(top)) + [modalWin orderFront:window]; + } #endif if (q->windowType() == Qt::Popup) { if (q->focusWidget()) @@ -3347,8 +3424,6 @@ void QWidgetPrivate::show_sys() } else if (!q->testAttribute(Qt::WA_ShowWithoutActivating)) { #ifndef QT_MAC_USE_COCOA qt_event_request_activate(q); -#else - [qt_mac_window_for(q) makeKeyWindow]; #endif } } else if(topData()->embedded || !q->parentWidget() || q->parentWidget()->isVisible()) { @@ -3391,6 +3466,9 @@ void QWidgetPrivate::hide_sys() QMacCocoaAutoReleasePool pool; if(q->isWindow()) { +#ifdef QT_MAC_USE_COCOA + setSubWindowStacking(false); +#endif OSWindowRef window = qt_mac_window_for(q); if(qt_mac_is_macsheet(q)) { #ifndef QT_MAC_USE_COCOA @@ -3456,12 +3534,15 @@ void QWidgetPrivate::hide_sys() } #endif } - if(q->isActiveWindow() && !(q->windowType() == Qt::Popup)) { +#ifndef QT_MAC_USE_COCOA + // If the window we now hide was the active window, we need + // to find, and activate another window on screen. NB: Cocoa takes care of this + // logic for us (and distinquishes between main windows and key windows) + if (q->isActiveWindow() && !(q->windowType() == Qt::Popup)) { QWidget *w = 0; if(q->parentWidget()) w = q->parentWidget()->window(); if(!w || (!w->isVisible() && !w->isMinimized())) { -#ifndef QT_MAC_USE_COCOA for (WindowPtr wp = GetFrontWindowOfClass(kMovableModalWindowClass, true); wp; wp = GetNextWindowOfClass(wp, kMovableModalWindowClass, true)) { if((w = qt_mac_find_window(wp))) @@ -3481,24 +3562,12 @@ void QWidgetPrivate::hide_sys() break; } } -#else - NSArray *windows = [NSApp windows]; - NSUInteger totalWindows = [windows count]; - for (NSUInteger i = 0; i < totalWindows; ++i) { - OSWindowRef wp = [windows objectAtIndex:i]; - if ((w = qt_mac_find_window(wp))) - break; - } -#endif } if(w && w->isVisible() && !w->isMinimized()) { -#ifndef QT_MAC_USE_COCOA - qt_event_request_activate(w); -#else - [qt_mac_window_for(w) makeKeyWindow]; -#endif + qt_event_request_activate(w); } } +#endif } else { invalidateBuffer(q->rect()); #ifndef QT_MAC_USE_COCOA @@ -3510,6 +3579,8 @@ void QWidgetPrivate::hide_sys() if (!QWidget::mouseGrabber()){ QWidget *enterWidget = QApplication::widgetAt(QCursor::pos()); + if (enterWidget && enterWidget->data->in_destructor) + enterWidget = 0; QApplicationPrivate::dispatchEnterLeave(enterWidget, qt_mouseover); qt_mouseover = enterWidget; } @@ -4584,9 +4655,12 @@ void QWidgetPrivate::registerDropSite(bool on) #ifndef QT_MAC_USE_COCOA SetControlDragTrackingEnabled(qt_mac_nativeview_for(q), on); #else - NSView *view = qt_mac_nativeview_for(q); - if (on && [view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) { - [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view) registerDragTypes]; + NSWindow *win = qt_mac_window_for(q); + if (on) { + if ([win isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaWindow) class]]) + [static_cast<QT_MANGLE_NAMESPACE(QCocoaWindow) *>(win) registerDragTypes]; + else if ([win isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaPanel) class]]) + [static_cast<QT_MANGLE_NAMESPACE(QCocoaPanel) *>(win) registerDragTypes]; } #endif } @@ -4740,7 +4814,7 @@ void QWidgetPrivate::setModal_sys() bool alreadySheet = [windowRef styleMask] & NSDocModalWindowMask; if (windowParent && q->windowModality() == Qt::WindowModal){ - // Window should be window-modal, which implies a sheet. + // INVARIANT: Window should be window-modal (which implies a sheet). if (!alreadySheet) { // NB: the following call will call setModal_sys recursivly: recreateMacWindow(); @@ -4757,47 +4831,19 @@ void QWidgetPrivate::setModal_sys() [static_cast<NSPanel *>(windowRef) setWorksWhenModal:YES]; } } else { - // Window shold not be window-modal, and as such, not a sheet. + // INVARIANT: Window shold _not_ be window-modal (and as such, not a sheet). if (alreadySheet){ // NB: the following call will call setModal_sys recursivly: recreateMacWindow(); windowRef = qt_mac_window_for(q); } - if (q->windowModality() == Qt::ApplicationModal) { - [windowRef setLevel:NSModalPanelWindowLevel]; - } else if (primaryWindow && primaryWindow->windowModality() == Qt::ApplicationModal) { - // INVARIANT: Our window is a dialog that has a dialog parent that is - // application modal, or . This means that q is supposed to be on top of this - // dialog and not be modally shaddowed: - [windowRef setLevel:NSModalPanelWindowLevel]; + if (q->windowModality() == Qt::NonModal + && primaryWindow && primaryWindow->windowModality() == Qt::ApplicationModal) { + // INVARIANT: Our window has a parent that is application modal. + // This means that q is supposed to be on top of this window and + // not be modally shaddowed: if ([windowRef isKindOfClass:[NSPanel class]]) [static_cast<NSPanel *>(windowRef) setWorksWhenModal:YES]; - } else { - // INVARIANT: q should not be modal. - NSInteger winLevel = -1; - if (q->windowType() == Qt::Popup) { - winLevel = NSPopUpMenuWindowLevel; - // Popup should be in at least the same level as its parent. - if (primaryWindow) { - OSWindowRef parentRef = qt_mac_window_for(primaryWindow); - winLevel = qMax([parentRef level], winLevel); - } - } else if (q->windowType() == Qt::Tool) { - winLevel = NSFloatingWindowLevel; - } else if (q->windowType() == Qt::Dialog) { - winLevel = NSModalPanelWindowLevel; - } - - // StayOnTop window should appear above Tool windows. - if (data.window_flags & Qt::WindowStaysOnTopHint) - winLevel = NSPopUpMenuWindowLevel; - // Tooltips should appear above StayOnTop windows. - if (q->windowType() == Qt::ToolTip) - winLevel = NSScreenSaverWindowLevel; - // All other types are Normal level. - if (winLevel == -1) - winLevel = NSNormalWindowLevel; - [windowRef setLevel:winLevel]; } } diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index c9d655c..d3bc2d8 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -762,6 +762,8 @@ public: void initWindowPtr(); void finishCreateWindow_sys_Carbon(OSWindowRef windowRef); #else + void setSubWindowStacking(bool set); + void setWindowLevel(); void finishCreateWindow_sys_Cocoa(void * /*NSWindow * */ windowRef); void syncCocoaMask(); void finishCocoaMaskSetup(); diff --git a/src/gui/mac/qt_menu.nib/classes.nib b/src/gui/mac/qt_menu.nib/classes.nib index fed50a3..0031e0e 100644 --- a/src/gui/mac/qt_menu.nib/classes.nib +++ b/src/gui/mac/qt_menu.nib/classes.nib @@ -5,14 +5,6 @@ <key>IBClasses</key> <array> <dict> - <key>CLASS</key> - <string>FirstResponder</string> - <key>LANGUAGE</key> - <string>ObjC</string> - <key>SUPERCLASS</key> - <string>NSObject</string> - </dict> - <dict> <key>ACTIONS</key> <dict> <key>hide</key> @@ -52,6 +44,14 @@ <key>SUPERCLASS</key> <string>NSResponder</string> </dict> + <dict> + <key>CLASS</key> + <string>FirstResponder</string> + <key>LANGUAGE</key> + <string>ObjC</string> + <key>SUPERCLASS</key> + <string>NSObject</string> + </dict> </array> <key>IBVersion</key> <string>1</string> diff --git a/src/gui/mac/qt_menu.nib/info.nib b/src/gui/mac/qt_menu.nib/info.nib index 768cb8b..02e5cca 100644 --- a/src/gui/mac/qt_menu.nib/info.nib +++ b/src/gui/mac/qt_menu.nib/info.nib @@ -3,7 +3,7 @@ <plist version="1.0"> <dict> <key>IBFramework Version</key> - <string>670</string> + <string>672</string> <key>IBOldestOS</key> <integer>5</integer> <key>IBOpenObjects</key> @@ -11,7 +11,7 @@ <integer>57</integer> </array> <key>IBSystem Version</key> - <string>9G55</string> + <string>9L31a</string> <key>targetFramework</key> <string>IBCocoaFramework</string> </dict> diff --git a/src/gui/mac/qt_menu.nib/keyedobjects.nib b/src/gui/mac/qt_menu.nib/keyedobjects.nib Binary files differindex 18a6648..3edb0ed 100644 --- a/src/gui/mac/qt_menu.nib/keyedobjects.nib +++ b/src/gui/mac/qt_menu.nib/keyedobjects.nib diff --git a/src/gui/painting/qbrush.cpp b/src/gui/painting/qbrush.cpp index 1a83b1d..a82d0f9 100644 --- a/src/gui/painting/qbrush.cpp +++ b/src/gui/painting/qbrush.cpp @@ -912,7 +912,7 @@ void QBrush::setTransform(const QTransform &matrix) otherwise returns false. Two brushes are different if they have different styles, colors or - pixmaps. + transforms or different pixmaps or gradients depending on the style. \sa operator==() */ @@ -924,7 +924,7 @@ void QBrush::setTransform(const QTransform &matrix) otherwise returns false. Two brushes are equal if they have equal styles, colors and - pixmaps. + transforms and equal pixmaps or gradients depending on the style. \sa operator!=() */ @@ -933,26 +933,26 @@ bool QBrush::operator==(const QBrush &b) const { if (b.d == d) return true; - if (b.d->style == d->style && b.d->color == d->color) { - switch (d->style) { - case Qt::TexturePattern: { - QPixmap &us = (static_cast<QTexturedBrushData *>(d.data()))->pixmap(); - QPixmap &them = (static_cast<QTexturedBrushData *>(b.d.data()))->pixmap(); + if (b.d->style != d->style || b.d->color != d->color || b.d->transform != d->transform) + return false; + switch (d->style) { + case Qt::TexturePattern: + { + const QPixmap &us = (static_cast<QTexturedBrushData *>(d.data()))->pixmap(); + const QPixmap &them = (static_cast<QTexturedBrushData *>(b.d.data()))->pixmap(); return ((us.isNull() && them.isNull()) || us.cacheKey() == them.cacheKey()); } - case Qt::LinearGradientPattern: - case Qt::RadialGradientPattern: - case Qt::ConicalGradientPattern: - { - QGradientBrushData *d1 = static_cast<QGradientBrushData *>(d.data()); - QGradientBrushData *d2 = static_cast<QGradientBrushData *>(b.d.data()); - return d1->gradient == d2->gradient; - } - default: - return true; + case Qt::LinearGradientPattern: + case Qt::RadialGradientPattern: + case Qt::ConicalGradientPattern: + { + const QGradientBrushData *d1 = static_cast<QGradientBrushData *>(d.data()); + const QGradientBrushData *d2 = static_cast<QGradientBrushData *>(b.d.data()); + return d1->gradient == d2->gradient; } + default: + return true; } - return false; } /*! diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h index a7834ea..bc5f1ef 100644 --- a/src/gui/painting/qdatabuffer_p.h +++ b/src/gui/painting/qdatabuffer_p.h @@ -81,7 +81,9 @@ public: inline Type &at(int i) { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; } inline const Type &at(int i) const { Q_ASSERT(i >= 0 && i < siz); return buffer[i]; } + inline Type &last() { Q_ASSERT(!isEmpty()); return buffer[siz-1]; } inline const Type &last() const { Q_ASSERT(!isEmpty()); return buffer[siz-1]; } + inline Type &first() { Q_ASSERT(!isEmpty()); return buffer[0]; } inline const Type &first() const { Q_ASSERT(!isEmpty()); return buffer[0]; } inline void add(const Type &t) { @@ -90,6 +92,11 @@ public: ++siz; } + inline void pop_back() { + Q_ASSERT(siz > 0); + --siz; + } + inline void resize(int size) { reserve(size); siz = size; diff --git a/src/gui/painting/qemulationpaintengine.cpp b/src/gui/painting/qemulationpaintengine.cpp index f2f0c73..fd42736 100644 --- a/src/gui/painting/qemulationpaintengine.cpp +++ b/src/gui/painting/qemulationpaintengine.cpp @@ -172,6 +172,36 @@ void QEmulationPaintEngine::drawTextItem(const QPointF &p, const QTextItem &text QRectF rect(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal()); fillBGRect(rect); } + + QPainterState *s = state(); + Qt::BrushStyle style = qbrush_style(s->pen.brush()); + if (style >= Qt::LinearGradientPattern && style <= Qt::ConicalGradientPattern) + { + QPen savedPen = s->pen; + QGradient g = *s->pen.brush().gradient(); + + if (g.coordinateMode() > QGradient::LogicalMode) { + QTransform mat = s->pen.brush().transform(); + if (g.coordinateMode() == QGradient::StretchToDeviceMode) { + mat.scale(real_engine->painter()->device()->width(), real_engine->painter()->device()->height()); + } else if (g.coordinateMode() == QGradient::ObjectBoundingMode) { + const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); + QRectF r(p.x(), p.y() - ti.ascent.toReal(), ti.width.toReal(), (ti.ascent + ti.descent + 1).toReal()); + mat.translate(r.x(), r.y()); + mat.scale(r.width(), r.height()); + } + g.setCoordinateMode(QGradient::LogicalMode); + QBrush brush(g); + brush.setTransform(mat); + s->pen.setBrush(brush); + penChanged(); + real_engine->drawTextItem(p, textItem); + s->pen = savedPen; + penChanged(); + return; + } + } + real_engine->drawTextItem(p, textItem); } diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index fba3595..f78de34 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -1257,6 +1257,8 @@ Qt::FillRule QPainterPath::fillRule() const void QPainterPath::setFillRule(Qt::FillRule fillRule) { ensureData(); + if (d_func()->fillRule == fillRule) + return; detach(); d_func()->fillRule = fillRule; diff --git a/src/gui/statemachine/qguistatemachine.cpp b/src/gui/statemachine/qguistatemachine.cpp index 70f152d..63ad94e 100644 --- a/src/gui/statemachine/qguistatemachine.cpp +++ b/src/gui/statemachine/qguistatemachine.cpp @@ -469,12 +469,6 @@ static QEvent *cloneEvent(QEvent *e) case QEvent::UngrabKeyboard: return new QEvent(*e); -#ifdef QT_MAC_USE_COCOA - case QEvent::CocoaRequestModal: - Q_ASSERT_X(false, "cloneEvent()", "not implemented"); - break; -#endif - case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: diff --git a/src/gui/styles/qmacstyle_mac.mm b/src/gui/styles/qmacstyle_mac.mm index 30c3805..2313ac5 100644 --- a/src/gui/styles/qmacstyle_mac.mm +++ b/src/gui/styles/qmacstyle_mac.mm @@ -3109,6 +3109,18 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai break; case PE_PanelLineEdit: QWindowsStyle::drawPrimitive(pe, opt, p, w); + // Draw the focus frame for widgets other than QLineEdit (e.g. for line edits in Webkit). + // Focus frame is drawn outside the rectangle passed in the option-rect. + if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + if ((opt->state & State_HasFocus) && !qobject_cast<const QLineEdit*>(w)) { + int vmargin = pixelMetric(QStyle::PM_FocusFrameVMargin); + int hmargin = pixelMetric(QStyle::PM_FocusFrameHMargin); + QStyleOptionFrame focusFrame = *panel; + focusFrame.rect = panel->rect.adjusted(-hmargin, -vmargin, hmargin, vmargin); + drawControl(CE_FocusFrame, &focusFrame, p, w); + } + } + break; case PE_FrameTabWidget: if (const QStyleOptionTabWidgetFrame *twf @@ -4308,8 +4320,6 @@ QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt, rect.setY(0); rect.setHeight(widget->height()); } - if (opt->direction == Qt::RightToLeft) - rect.adjust(15, 0, -20, 0); } break; case SE_ProgressBarGroove: diff --git a/src/gui/styles/qs60style_s60.cpp b/src/gui/styles/qs60style_s60.cpp index d03574f..9d2dc66 100644 --- a/src/gui/styles/qs60style_s60.cpp +++ b/src/gui/styles/qs60style_s60.cpp @@ -58,8 +58,8 @@ #include <aknsskininstance.h> #include <aknsbasicbackgroundcontrolcontext.h> #include <avkon.mbg> -#include <AknFontAccess.h> -#include <AknLayoutFont.h> +#include <aknfontaccess.h> +#include <aknlayoutfont.h> #include <aknutils.h> #include <aknnavi.h> #include <gulicon.h> diff --git a/src/gui/styles/qstylesheetstyle.cpp b/src/gui/styles/qstylesheetstyle.cpp index 498313b..9002172 100644 --- a/src/gui/styles/qstylesheetstyle.cpp +++ b/src/gui/styles/qstylesheetstyle.cpp @@ -1065,7 +1065,7 @@ QRect QRenderRule::boxRect(const QRect& cr, int flags) const r.adjust(-p[LeftEdge], -p[TopEdge], p[RightEdge], p[BottomEdge]); } } - if (!hasNativeBorder() && (flags & Border)) { + if (hasBorder() && (flags & Border)) { const int *b = border()->borders; r.adjust(-b[LeftEdge], -b[TopEdge], b[RightEdge], b[BottomEdge]); } @@ -1350,6 +1350,12 @@ void QRenderRule::configurePalette(QPalette *p, QPalette::ColorRole fr, QPalette if (br != QPalette::NoRole) p->setBrush(br, bg->brush); p->setBrush(QPalette::Window, bg->brush); + if (bg->brush.style() == Qt::SolidPattern) { + p->setBrush(QPalette::Light, bg->brush.color().lighter(115)); + p->setBrush(QPalette::Midlight, bg->brush.color().lighter(107)); + p->setBrush(QPalette::Dark, bg->brush.color().darker(150)); + p->setBrush(QPalette::Shadow, bg->brush.color().darker(300)); + } } if (!hasPalette()) diff --git a/src/gui/text/qfontdatabase_s60.cpp b/src/gui/text/qfontdatabase_s60.cpp index f8de08f..3ae6fe9 100644 --- a/src/gui/text/qfontdatabase_s60.cpp +++ b/src/gui/text/qfontdatabase_s60.cpp @@ -50,7 +50,7 @@ #include "qendian.h" #include <private/qcore_symbian_p.h> #if defined(QT_NO_FREETYPE) -#include <OPENFONT.H> +#include <openfont.h> #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS #include <graphics/openfontrasterizer.h> // COpenFontRasterizer has moved to a new header file #endif // SYMBIAN_ENABLE_SPLIT_HEADERS diff --git a/src/gui/text/qfontdatabase_win.cpp b/src/gui/text/qfontdatabase_win.cpp index 05c8f08..b30a6c3 100644 --- a/src/gui/text/qfontdatabase_win.cpp +++ b/src/gui/text/qfontdatabase_win.cpp @@ -1101,7 +1101,6 @@ static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) if (AddFontResource((LPCWSTR)fnt->fileName.utf16()) == 0) return; #else - // supported from 2000 on, so no need to deal with the *A variant PtrAddFontResourceExW ptrAddFontResourceExW = (PtrAddFontResourceExW)QLibrary::resolve(QLatin1String("gdi32"), "AddFontResourceExW"); if (!ptrAddFontResourceExW diff --git a/src/gui/text/qfontengine_s60_p.h b/src/gui/text/qfontengine_s60_p.h index ff819e2..6d47b9d 100644 --- a/src/gui/text/qfontengine_s60_p.h +++ b/src/gui/text/qfontengine_s60_p.h @@ -56,7 +56,7 @@ #include "qconfig.h" #include "qfontengine_p.h" #include "qsize.h" -#include <OPENFONT.H> +#include <openfont.h> class CFbsBitmap; class CFbsBitmapDevice; diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index 21f3189..b8c9b94 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -61,6 +61,7 @@ #include <qapplication.h> #include "qtextcontrol_p.h" #include "private/qtextedit_p.h" +#include "private/qdataurl_p.h" #include "qtextdocument_p.h" #include <private/qprinter_p.h> @@ -1924,6 +1925,10 @@ QVariant QTextDocument::loadResource(int type, const QUrl &name) } #endif + // handle data: URLs + if (r.isNull() && name.scheme() == QLatin1String("data")) + r = qDecodeDataUrl(name).second; + // if resource was not loaded try to load it here if (!doc && r.isNull() && name.isRelative()) { QUrl currentURL = d->url; diff --git a/src/gui/util/qcompleter.cpp b/src/gui/util/qcompleter.cpp index cefdb27..b7be967 100644 --- a/src/gui/util/qcompleter.cpp +++ b/src/gui/util/qcompleter.cpp @@ -62,7 +62,7 @@ \snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 0 - A QDirModel can be used to provide auto completion of file names. + A QFileSystemModel can be used to provide auto completion of file names. For example: \snippet doc/src/snippets/code/src_gui_util_qcompleter.cpp 1 @@ -120,7 +120,7 @@ completion is then performed one level at a time. Let's take the example of a user typing in a file system path. - The model is a (hierarchical) QDirModel. The completion + The model is a (hierarchical) QFileSystemModel. The completion occurs for every element in the path. For example, if the current text is \c C:\Wind, QCompleter might suggest \c Windows to complete the current path element. Similarly, if the current text @@ -130,12 +130,12 @@ split the path into a list of strings that are matched at each level. For \c C:\Windows\Sy, it needs to be split as "C:", "Windows" and "Sy". The default implementation of splitPath(), splits the completionPrefix - using QDir::separator() if the model is a QDirModel. + using QDir::separator() if the model is a QFileSystemModel. To provide completions, QCompleter needs to know the path from an index. This is provided by pathFromIndex(). The default implementation of pathFromIndex(), returns the data for the \l{Qt::EditRole}{edit role} - for list models and the absolute file path if the mode is a QDirModel. + for list models and the absolute file path if the mode is a QFileSystemModel. \sa QAbstractItemModel, QLineEdit, QComboBox, {Completer Example} */ @@ -147,6 +147,7 @@ #include "QtGui/qscrollbar.h" #include "QtGui/qstringlistmodel.h" #include "QtGui/qdirmodel.h" +#include "QtGui/qfilesystemmodel.h" #include "QtGui/qheaderview.h" #include "QtGui/qlistview.h" #include "QtGui/qapplication.h" @@ -470,9 +471,13 @@ QMatchData QCompletionEngine::filterHistory() QAbstractItemModel *source = c->proxy->sourceModel(); if (curParts.count() <= 1 || c->proxy->showAll || !source) return QMatchData(); - bool dirModel = false; + bool isDirModel = false; + bool isFsModel = false; #ifndef QT_NO_DIRMODEL - dirModel = (qobject_cast<QDirModel *>(source) != 0); + isDirModel = (qobject_cast<QDirModel *>(source) != 0); +#endif +#ifndef QT_NO_FILESYSTEMMODEL + isFsModel = (qobject_cast<QFileSystemModel *>(source) != 0); #endif QVector<int> v; QIndexMapper im(v); @@ -482,7 +487,7 @@ QMatchData QCompletionEngine::filterHistory() QString str = source->index(i, c->column).data().toString(); if (str.startsWith(c->prefix, c->cs) #if (!defined(Q_OS_WIN) || defined(Q_OS_WINCE)) && !defined(Q_OS_SYMBIAN) - && (!dirModel || QDir::toNativeSeparators(str) != QDir::separator()) + && ((!isFsModel && !isDirModel) || QDir::toNativeSeparators(str) != QDir::separator()) #endif ) m.indices.append(i); @@ -838,6 +843,13 @@ void QCompleterPrivate::_q_complete(QModelIndex index, bool highlighted) completion += QDir::separator(); } #endif +#ifndef QT_NO_FILESYSTEMMODEL + // add a trailing separator in inline + if (mode == QCompleter::InlineCompletion) { + if (qobject_cast<QFileSystemModel *>(proxy->sourceModel()) && QFileInfo(completion).isDir()) + completion += QDir::separator(); + } +#endif } if (highlighted) { @@ -891,6 +903,14 @@ void QCompleterPrivate::showPopup(const QRect& rect) popup->show(); } +void QCompleterPrivate::_q_fileSystemModelDirectoryLoaded(const QString &path) +{ + Q_Q(QCompleter); + //the path given by QFileSystemModel does not end with / + if (q->completionPrefix() != path + QLatin1Char('/')) + q->complete(); +} + /*! Constructs a completer object with the given \a parent. */ @@ -971,7 +991,7 @@ QWidget *QCompleter::widget() const be list model or a tree model. If a model has been already previously set and it has the QCompleter as its parent, it is deleted. - For convenience, if \a model is a QDirModel, QCompleter switches its + For convenience, if \a model is a QFileSystemModel, QCompleter switches its caseSensitivity to Qt::CaseInsensitive on Windows and Qt::CaseSensitive on other platforms. @@ -995,6 +1015,18 @@ void QCompleter::setModel(QAbstractItemModel *model) #endif } #endif // QT_NO_DIRMODEL +#ifndef QT_NO_FILESYSTEMMODEL + QFileSystemModel *fsModel = qobject_cast<QFileSystemModel *>(model); + if (fsModel) { +#if (defined(Q_OS_WIN) && !defined(Q_OS_WINCE)) || defined(Q_OS_SYMBIAN) + setCaseSensitivity(Qt::CaseInsensitive); +#else + setCaseSensitivity(Qt::CaseSensitive); +#endif + setCompletionRole(QFileSystemModel::FileNameRole); + connect(fsModel, SIGNAL(directoryLoaded(QString)), this, SLOT(_q_fileSystemModelDirectoryLoaded(QString))); + } +#endif // QT_NO_FILESYSTEMMODEL } /*! @@ -1626,10 +1658,11 @@ QAbstractItemModel *QCompleter::completionModel() const The default implementation returns the \l{Qt::EditRole}{edit role} of the item for list models. It returns the absolute file path if the model is a - QDirModel. + QFileSystemModel. \sa splitPath() */ + QString QCompleter::pathFromIndex(const QModelIndex& index) const { Q_D(const QCompleter); @@ -1639,16 +1672,25 @@ QString QCompleter::pathFromIndex(const QModelIndex& index) const QAbstractItemModel *sourceModel = d->proxy->sourceModel(); if (!sourceModel) return QString(); + bool isDirModel = false; + bool isFsModel = false; #ifndef QT_NO_DIRMODEL - QDirModel *dirModel = qobject_cast<QDirModel *>(sourceModel); - if (!dirModel) + isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0; +#endif +#ifndef QT_NO_FILESYSTEMMODEL + isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) != 0; #endif + if (!isDirModel && !isFsModel) return sourceModel->data(index, d->role).toString(); QModelIndex idx = index; QStringList list; do { - QString t = sourceModel->data(idx, Qt::EditRole).toString(); + QString t; + if (isDirModel) + t = sourceModel->data(idx, Qt::EditRole).toString(); + else + t = sourceModel->data(idx, QFileSystemModel::FileNameRole).toString(); list.prepend(t); QModelIndex parent = idx.parent(); idx = parent.sibling(parent.row(), index.column()); @@ -1668,7 +1710,7 @@ QString QCompleter::pathFromIndex(const QModelIndex& index) const in the model(). The default implementation of splitPath() splits a file system path based on - QDir::separator() when the sourceModel() is a QDirModel. + QDir::separator() when the sourceModel() is a QFileSystemModel. When used with list models, the first item in the returned list is used for matching. @@ -1678,12 +1720,19 @@ QString QCompleter::pathFromIndex(const QModelIndex& index) const QStringList QCompleter::splitPath(const QString& path) const { bool isDirModel = false; + bool isFsModel = false; #ifndef QT_NO_DIRMODEL Q_D(const QCompleter); isDirModel = qobject_cast<QDirModel *>(d->proxy->sourceModel()) != 0; #endif +#ifndef QT_NO_FILESYSTEMMODEL +#ifdef QT_NO_DIRMODEL + Q_D(const QCompleter); +#endif + isFsModel = qobject_cast<QFileSystemModel *>(d->proxy->sourceModel()) != 0; +#endif - if (!isDirModel || path.isEmpty()) + if ((!isDirModel && !isFsModel) || path.isEmpty()) return QStringList(completionPrefix()); QString pathCopy = QDir::toNativeSeparators(path); diff --git a/src/gui/util/qcompleter.h b/src/gui/util/qcompleter.h index 5123a40..0cef9be 100644 --- a/src/gui/util/qcompleter.h +++ b/src/gui/util/qcompleter.h @@ -159,6 +159,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_complete(QModelIndex)) Q_PRIVATE_SLOT(d_func(), void _q_completionSelected(const QItemSelection&)) Q_PRIVATE_SLOT(d_func(), void _q_autoResizePopup()) + Q_PRIVATE_SLOT(d_func(), void _q_fileSystemModelDirectoryLoaded(const QString&)) }; #endif // QT_NO_COMPLETER diff --git a/src/gui/util/qcompleter_p.h b/src/gui/util/qcompleter_p.h index 44be4c0..8f00793 100644 --- a/src/gui/util/qcompleter_p.h +++ b/src/gui/util/qcompleter_p.h @@ -98,6 +98,7 @@ public: void _q_complete(QModelIndex, bool = false); void _q_completionSelected(const QItemSelection&); void _q_autoResizePopup(); + void _q_fileSystemModelDirectoryLoaded(const QString &path); void setCurrentIndex(QModelIndex, bool = true); }; diff --git a/src/gui/util/qdesktopservices_win.cpp b/src/gui/util/qdesktopservices_win.cpp index 2032fd9..aab7e16 100644 --- a/src/gui/util/qdesktopservices_win.cpp +++ b/src/gui/util/qdesktopservices_win.cpp @@ -97,19 +97,19 @@ static bool launchWebBrowser(const QUrl &url) if (url.scheme() == QLatin1String("mailto")) { //Retrieve the commandline for the default mail client //the default key used below is the command line for the mailto: shell command - DWORD bufferSize = 2 * MAX_PATH; + DWORD bufferSize = sizeof(wchar_t) * MAX_PATH; long returnValue = -1; QString command; HKEY handle; LONG res; - wchar_t keyValue[2 * MAX_PATH] = {0}; + wchar_t keyValue[MAX_PATH] = {0}; QString keyName(QLatin1String("mailto")); //Check if user has set preference, otherwise use default. - res = RegOpenKeyExW(HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\mailto\\UserChoice", - 0, KEY_READ, &handle); + res = RegOpenKeyEx(HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\mailto\\UserChoice", + 0, KEY_READ, &handle); if (res == ERROR_SUCCESS) { returnValue = RegQueryValueEx(handle, L"Progid", 0, 0, reinterpret_cast<unsigned char*>(keyValue), &bufferSize); if (!returnValue) @@ -121,8 +121,8 @@ static bool launchWebBrowser(const QUrl &url) if (res != ERROR_SUCCESS) return false; - bufferSize = 2 * MAX_PATH; - returnValue = RegQueryValueExW(handle, L"", 0, 0, reinterpret_cast<unsigned char*>(keyValue), &bufferSize); + bufferSize = sizeof(wchar_t) * MAX_PATH; + returnValue = RegQueryValueEx(handle, L"", 0, 0, reinterpret_cast<unsigned char*>(keyValue), &bufferSize); if (!returnValue) command = QString::fromRawData((QChar*)keyValue, bufferSize); RegCloseKey(handle); diff --git a/src/gui/util/qsystemtrayicon_win.cpp b/src/gui/util/qsystemtrayicon_win.cpp index 6db158e..092909f 100644 --- a/src/gui/util/qsystemtrayicon_win.cpp +++ b/src/gui/util/qsystemtrayicon_win.cpp @@ -53,7 +53,6 @@ #include <qt_windows.h> #include <commctrl.h> -#include <shlwapi.h> #include <QBitmap> #include <QLibrary> #include <QApplication> diff --git a/src/gui/widgets/qcocoamenu_mac_p.h b/src/gui/widgets/qcocoamenu_mac_p.h index 23ea705..d6ac8c5 100644 --- a/src/gui/widgets/qcocoamenu_mac_p.h +++ b/src/gui/widgets/qcocoamenu_mac_p.h @@ -52,10 +52,10 @@ #include "qmacdefines_mac.h" #ifdef QT_MAC_USE_COCOA -#include <qpointer.h> #import <Cocoa/Cocoa.h> QT_FORWARD_DECLARE_CLASS(QMenu) +QT_FORWARD_DECLARE_CLASS(QAction) #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 @@ -72,7 +72,7 @@ QT_FORWARD_DECLARE_CLASS(QMenu) @interface QT_MANGLE_NAMESPACE(QCocoaMenu) : NSMenu <NSMenuDelegate> { QMenu *qmenu; - QPointer<QAction> previousAction; + QAction *previousAction; } - (id)initWithQMenu:(QMenu*)menu; - (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action; diff --git a/src/gui/widgets/qcombobox.cpp b/src/gui/widgets/qcombobox.cpp index 72f32dc..be20a38 100644 --- a/src/gui/widgets/qcombobox.cpp +++ b/src/gui/widgets/qcombobox.cpp @@ -1268,7 +1268,8 @@ QComboBox::~QComboBox() By default, this property has a value of 10. - \note This property is ignored for non-editable comboboxes in Mac style. + \note This property is ignored for non-editable comboboxes in styles that returns + false for QStyle::SH_ComboBox_Popup such as the Mac style or the Gtk+ Style. */ int QComboBox::maxVisibleItems() const { @@ -2348,7 +2349,7 @@ void QComboBox::showPopup() toCheck.push(idx); #endif ++count; - if (!usePopup && count > d->maxVisibleItems) { + if (!usePopup && count >= d->maxVisibleItems) { toCheck.clear(); break; } diff --git a/src/gui/widgets/qfocusframe.cpp b/src/gui/widgets/qfocusframe.cpp index d9cd5bb..4f20bce0 100644 --- a/src/gui/widgets/qfocusframe.cpp +++ b/src/gui/widgets/qfocusframe.cpp @@ -53,11 +53,14 @@ class QFocusFramePrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QFocusFrame) QWidget *widget; - + QWidget *frameParent; + bool showFrameAboveWidget; public: QFocusFramePrivate() { widget = 0; + frameParent = 0; sendChildEvents = false; + showFrameAboveWidget = false; } void updateSize(); void update(); @@ -66,10 +69,10 @@ public: void QFocusFramePrivate::update() { Q_Q(QFocusFrame); - q->setParent(widget->parentWidget()); + q->setParent(frameParent); updateSize(); if (q->parentWidget()->rect().intersects(q->geometry())) { - if (q->style()->styleHint(QStyle::SH_FocusFrame_AboveWidget, 0, q)) + if (showFrameAboveWidget) q->raise(); else q->stackUnder(widget); @@ -84,7 +87,10 @@ void QFocusFramePrivate::updateSize() Q_Q(QFocusFrame); int vmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameVMargin), hmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin); - QRect geom(widget->x()-hmargin, widget->y()-vmargin, + QPoint pos(widget->x(), widget->y()); + if (q->parentWidget() != widget->parentWidget()) + pos = widget->parentWidget()->mapTo(q->parentWidget(), pos); + QRect geom(pos.x()-hmargin, pos.y()-vmargin, widget->width()+(hmargin*2), widget->height()+(vmargin*2)); if(q->geometry() == geom) return; @@ -176,14 +182,52 @@ void QFocusFrame::setWidget(QWidget *widget) { Q_D(QFocusFrame); - if(widget == d->widget) - return; - if(d->widget) - d->widget->removeEventFilter(this); - if(widget && !widget->isWindow() && widget->parentWidget()->windowType() != Qt::SubWindow) { + if (style()->styleHint(QStyle::SH_FocusFrame_AboveWidget, 0, this)) + d->showFrameAboveWidget = true; + else + d->showFrameAboveWidget = false; + + if (widget == d->widget) + return; + if (d->widget) { + // Remove event filters from the widget hierarchy. + QWidget *p = d->widget; + do { + p->removeEventFilter(this); + if (!d->showFrameAboveWidget || p == d->frameParent) + break; + p = p->parentWidget(); + }while (p); + } + if (widget && !widget->isWindow() && widget->parentWidget()->windowType() != Qt::SubWindow) { d->widget = widget; - widget->installEventFilter(this); + d->widget->installEventFilter(this); + QWidget *p = widget->parentWidget(); + QWidget *prev = 0; + if (d->showFrameAboveWidget) { + // Find the right parent for the focus frame. + while (p) { + // Traverse the hirerarchy of the 'widget' for setting event filter. + // During this if come across toolbar or a top level, use that + // as the parent for the focus frame. If we find a scroll area + // use its viewport as the parent. + bool isScrollArea = false; + if (p->isWindow() || p->inherits("QToolBar") || (isScrollArea = p->inherits("QAbstractScrollArea"))) { + d->frameParent = p; + // The previous one in the hierarchy will be the viewport. + if (prev && isScrollArea) + d->frameParent = prev; + break; + } else { + p->installEventFilter(this); + prev = p; + p = p->parentWidget(); + } + } + } else { + d->frameParent = p; + } d->update(); } else { d->widget = 0; @@ -210,9 +254,15 @@ QFocusFrame::widget() const void QFocusFrame::paintEvent(QPaintEvent *) { + Q_D(QFocusFrame); QStylePainter p(this); QStyleOption option; initStyleOption(&option); + int vmargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin); + int hmargin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin); + QWidgetPrivate *wd = qt_widget_private(d->widget); + QRect rect = wd->clipRect().adjusted(0, 0, hmargin*2, vmargin*2); + p.setClipRect(rect); p.drawControl(QStyle::CE_FocusFrame, option); } @@ -233,7 +283,13 @@ QFocusFrame::eventFilter(QObject *o, QEvent *e) hide(); break; case QEvent::ParentChange: - d->update(); + if (d->showFrameAboveWidget) { + QWidget *w = d->widget; + setWidget(0); + setWidget(w); + } else { + d->update(); + } break; case QEvent::Show: d->update(); @@ -254,6 +310,19 @@ QFocusFrame::eventFilter(QObject *o, QEvent *e) default: break; } + } else if (d->showFrameAboveWidget) { + // Handle changes in the parent widgets we are monitoring. + switch(e->type()) { + case QEvent::Move: + case QEvent::Resize: + d->updateSize(); + break; + case QEvent::ZOrderChange: + raise(); + break; + default: + break; + } } return false; } diff --git a/src/gui/widgets/qmenu_mac.mm b/src/gui/widgets/qmenu_mac.mm index cd7f9bd..32968ee 100644 --- a/src/gui/widgets/qmenu_mac.mm +++ b/src/gui/widgets/qmenu_mac.mm @@ -231,7 +231,7 @@ bool qt_mac_activate_action(MenuRef menu, uint command, QAction::ActionEvent act //now walk up firing for each "caused" widget (like in the platform independent menu) QWidget *caused = 0; - if (GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), 0, &caused) == noErr) { + if (action_e == QAction::Hover && GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), 0, &caused) == noErr) { MenuRef caused_menu = 0; if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused)) caused_menu = qmenu2->macMenu(); @@ -244,25 +244,17 @@ bool qt_mac_activate_action(MenuRef menu, uint command, QAction::ActionEvent act QWidget *widget = 0; GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget); if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) { - if (action_e == QAction::Trigger) { - emit qmenu->triggered(action->action); - } else if (action_e == QAction::Hover) { - action->action->showStatusText(widget); - emit qmenu->hovered(action->action); - } + action->action->showStatusText(widget); + emit qmenu->hovered(action->action); } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) { - if (action_e == QAction::Trigger) { - emit qmenubar->triggered(action->action); - } else if (action_e == QAction::Hover) { - action->action->showStatusText(widget); - emit qmenubar->hovered(action->action); - } + action->action->showStatusText(widget); + emit qmenubar->hovered(action->action); break; //nothing more.. } //walk up if (GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, - sizeof(caused), 0, &caused) != noErr) + sizeof(caused), 0, &caused) != noErr) break; if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused)) caused_menu = qmenu2->macMenu(); @@ -633,7 +625,7 @@ static NSMenuItem *createNSMenuItem(const QString &title) NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:qt_mac_QStringToNSString(title) action:@selector(qtDispatcherToQAction:) keyEquivalent:@""]; - [item setTarget:getMenuLoader()]; + [item setTarget:nil]; return item; } #endif @@ -733,32 +725,6 @@ bool qt_mac_menubar_is_open() return qt_mac_menus_open_count > 0; } -void qt_mac_clear_menubar() -{ - if (QApplication::testAttribute(Qt::AA_MacPluginApplication)) - return; - -#ifndef QT_MAC_USE_COCOA - MenuRef clear_menu = 0; - if (CreateNewMenu(0, 0, &clear_menu) == noErr) { - SetRootMenu(clear_menu); - ReleaseMenu(clear_menu); - } else { - qWarning("QMenu: Internal error at %s:%d", __FILE__, __LINE__); - } - ClearMenuBar(); - qt_mac_command_set_enabled(0, kHICommandPreferences, false); - InvalMenuBar(); -#else - QMacCocoaAutoReleasePool pool; - QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); - NSMenu *menu = [loader menu]; - [loader ensureAppMenuInMenu:menu]; - [NSApp setMainMenu:menu]; -#endif -} - - QMacMenuAction::~QMacMenuAction() { #ifdef QT_MAC_USE_COCOA @@ -1114,7 +1080,7 @@ QMenuPrivate::QMacMenuPrivate::addAction(QMacMenuAction *action, QMacMenuAction action->menu = merge; [cmd retain]; [cmd setAction:@selector(qtDispatcherToQAction:)]; - [cmd setTarget:getMenuLoader()]; + [cmd setTarget:nil]; [action->menuItem release]; action->menuItem = cmd; QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge); @@ -1914,57 +1880,53 @@ static bool qt_mac_is_ancestor(QWidget* possibleAncestor, QWidget *child) Returns true if the entries of menuBar should be disabled, based on the modality type of modalWidget. */ -static bool qt_mac_should_disable_menu(QMenuBar *menuBar, QWidget *modalWidget) +static bool qt_mac_should_disable_menu(QMenuBar *menuBar) { - if (modalWidget == 0 || menuBar == 0) + QWidget *modalWidget = qApp->activeModalWidget(); + if (!modalWidget) + return false; + + if (menuBar && menuBar == menubars()->value(modalWidget)) + // The menu bar is owned by the modal widget. + // In that case we should enable it: return false; - // If there is an application modal window on - // screen, the entries of the menubar should be disabled: + // When there is an application modal window on screen, the entries of + // the menubar should be disabled. The exception in Qt is that if the + // modal window is the only window on screen, then we enable the menu bar. QWidget *w = modalWidget; + QWidgetList topLevelWidgets = QApplication::topLevelWidgets(); while (w) { - if (w->isVisible() && w->windowModality() == Qt::ApplicationModal) - return true; + if (w->isVisible() && w->windowModality() == Qt::ApplicationModal) { + for (int i=0; i<topLevelWidgets.size(); ++i) { + QWidget *top = topLevelWidgets.at(i); + if (w != top && [qt_mac_window_for(top) isVisible]) { + // INVARIANT: we found another visible window + // on screen other than our modalWidget. We therefore + // disable the menu bar to follow normal modality logic: + return true; + } + } + // INVARIANT: We have only one window on screen that happends + // to be application modal. We choose to enable the menu bar + // in that case to e.g. enable the quit menu item. + return false; + } w = w->parentWidget(); } // INVARIANT: modalWidget is window modal. Disable menu entries - // if the menu bar belongs to an ancestor of modalWidget: - return qt_mac_is_ancestor(menuBar->parentWidget(), modalWidget); -} - -static void cancelAllMenuTracking() -{ -#ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - NSMenu *mainMenu = [NSApp mainMenu]; - [mainMenu cancelTracking]; - for (NSMenuItem *item in [mainMenu itemArray]) { - if ([item submenu]) { - [[item submenu] cancelTracking]; - } - } -#endif + // if the menu bar belongs to an ancestor of modalWidget. If menuBar + // is nil, we understand it as the default menu bar set by the nib: + return menuBar ? qt_mac_is_ancestor(menuBar->parentWidget(), modalWidget) : false; } -/*! - \internal - - This function will update the current menu bar and set it as the - active menu bar in the Menu Manager. - - \warning This function is not portable. - - \sa QMenu::macMenu(), QMenuBar::macMenu() -*/ -bool QMenuBar::macUpdateMenuBar() +static QWidget *findWindowThatShouldDisplayMenubar() { - cancelAllMenuTracking(); - QMenuBar *mb = 0; - //find a menu bar QWidget *w = qApp->activeWindow(); - if (!w) { + // We have no active window on screen. Try to + // find a window from the list of top levels: QWidgetList tlws = QApplication::topLevelWidgets(); for(int i = 0; i < tlws.size(); ++i) { QWidget *tlw = tlws.at(i); @@ -1975,6 +1937,12 @@ bool QMenuBar::macUpdateMenuBar() } } } + return w; +} + +static QMenuBar *findMenubarForWindow(QWidget *w) +{ + QMenuBar *mb = 0; if (w) { mb = menubars()->value(w); #ifndef QT_NO_MAINWINDOW @@ -1988,11 +1956,79 @@ bool QMenuBar::macUpdateMenuBar() while(w && !mb) mb = menubars()->value((w = w->parentWidget())); } - if (!mb) + + if (!mb) { + // We could not find a menu bar for the window. Lets + // check if we have a global (parentless) menu bar instead: mb = fallback; - //now set it + } + + return mb; +} + +static void cancelAllMenuTracking() +{ +#ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; + NSMenu *mainMenu = [NSApp mainMenu]; + [mainMenu cancelTracking]; + for (NSMenuItem *item in [mainMenu itemArray]) { + if ([item submenu]) { + [[item submenu] cancelTracking]; + } + } +#endif +} + +void qt_mac_clear_menubar() +{ + if (QApplication::testAttribute(Qt::AA_MacPluginApplication)) + return; + +#ifndef QT_MAC_USE_COCOA + MenuRef clear_menu = 0; + if (CreateNewMenu(0, 0, &clear_menu) == noErr) { + SetRootMenu(clear_menu); + ReleaseMenu(clear_menu); + } else { + qWarning("QMenu: Internal error at %s:%d", __FILE__, __LINE__); + } + ClearMenuBar(); + qt_mac_command_set_enabled(0, kHICommandPreferences, false); + InvalMenuBar(); +#else + QMacCocoaAutoReleasePool pool; + QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); + NSMenu *menu = [loader menu]; + [loader ensureAppMenuInMenu:menu]; + [NSApp setMainMenu:menu]; + const bool modal = qt_mac_should_disable_menu(0); + if (qt_mac_current_menubar.qmenubar || modal != qt_mac_current_menubar.modal) + qt_mac_set_modal_state(menu, modal); + qt_mac_current_menubar.qmenubar = 0; + qt_mac_current_menubar.modal = modal; +#endif +} + +/*! + \internal + + This function will update the current menu bar and set it as the + active menu bar in the Menu Manager. + + \warning This function is not portable. + + \sa QMenu::macMenu(), QMenuBar::macMenu() +*/ +bool QMenuBar::macUpdateMenuBar() +{ bool ret = false; + cancelAllMenuTracking(); + QWidget *w = findWindowThatShouldDisplayMenubar(); + QMenuBar *mb = findMenubarForWindow(w); + if (mb && mb->isNativeMenuBar()) { + bool modal = QApplicationPrivate::modalState(); #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; #endif @@ -2022,16 +2058,18 @@ bool QMenuBar::macUpdateMenuBar() } } #endif - QWidget *modalWidget = qApp->activeModalWidget(); - if (mb != menubars()->value(modalWidget)) { - qt_mac_set_modal_state(menu, qt_mac_should_disable_menu(mb, modalWidget)); - } + // Check if menu is modally shaddowed and should be disabled: + modal = qt_mac_should_disable_menu(mb); + if (mb != qt_mac_current_menubar.qmenubar || modal != qt_mac_current_menubar.modal) + qt_mac_set_modal_state(menu, modal); } qt_mac_current_menubar.qmenubar = mb; - qt_mac_current_menubar.modal = QApplicationPrivate::modalState(); + qt_mac_current_menubar.modal = modal; ret = true; } else if (qt_mac_current_menubar.qmenubar && qt_mac_current_menubar.qmenubar->isNativeMenuBar()) { - const bool modal = QApplicationPrivate::modalState(); + // INVARIANT: The currently active menu bar (if any) is not native. But we do have a + // native menu bar from before. So we need to decide whether or not is should be enabled: + const bool modal = qt_mac_should_disable_menu(qt_mac_current_menubar.qmenubar); if (modal != qt_mac_current_menubar.modal) { ret = true; if (OSMenuRef menu = qt_mac_current_menubar.qmenubar->macMenu()) { @@ -2043,14 +2081,12 @@ bool QMenuBar::macUpdateMenuBar() [NSApp setMainMenu:menu]; syncMenuBarItemsVisiblity(qt_mac_current_menubar.qmenubar->d_func()->mac_menubar); #endif - QWidget *modalWidget = qApp->activeModalWidget(); - if (qt_mac_current_menubar.qmenubar != menubars()->value(modalWidget)) { - qt_mac_set_modal_state(menu, qt_mac_should_disable_menu(mb, modalWidget)); - } + qt_mac_set_modal_state(menu, modal); } qt_mac_current_menubar.modal = modal; } } + if(!ret) qt_mac_clear_menubar(); return ret; diff --git a/src/gui/widgets/qplaintextedit.cpp b/src/gui/widgets/qplaintextedit.cpp index ab598d9..02ffe13 100644 --- a/src/gui/widgets/qplaintextedit.cpp +++ b/src/gui/widgets/qplaintextedit.cpp @@ -911,6 +911,7 @@ void QPlainTextEditPrivate::pageUpDown(QTextCursor::MoveOperation op, QTextCurso setTopBlock(block.blockNumber(), line); if (moveCursor) { + cursor.setVisualNavigation(true); // move using movePosition to keep the cursor's x lastY += verticalOffset(); bool moved = false; diff --git a/src/gui/widgets/qtoolbar.cpp b/src/gui/widgets/qtoolbar.cpp index 53050ac..7782448 100644 --- a/src/gui/widgets/qtoolbar.cpp +++ b/src/gui/widgets/qtoolbar.cpp @@ -534,6 +534,14 @@ void QToolBarPrivate::plug(const QRect &r) /*! + \fn void QToolBar::visibilityChanged(bool visible) + \since 4.7 + + This signal is emitted when the toolbar becomes \a visible (or + invisible). This happens when the widget is hidden or shown. +*/ + +/*! Constructs a QToolBar with the given \a parent. */ QToolBar::QToolBar(QWidget *parent) @@ -1123,6 +1131,7 @@ bool QToolBar::event(QEvent *event) // fallthrough intended case QEvent::Show: d->toggleViewAction->setChecked(event->type() == QEvent::Show); + emit visibilityChanged(event->type() == QEvent::Show); #if defined(Q_WS_MAC) if (toolbarInUnifiedToolBar(this)) { // I can static_cast because I did the qobject_cast in the if above, therefore diff --git a/src/gui/widgets/qtoolbar.h b/src/gui/widgets/qtoolbar.h index 90f20dd..b733477 100644 --- a/src/gui/widgets/qtoolbar.h +++ b/src/gui/widgets/qtoolbar.h @@ -143,6 +143,7 @@ Q_SIGNALS: void iconSizeChanged(const QSize &iconSize); void toolButtonStyleChanged(Qt::ToolButtonStyle toolButtonStyle); void topLevelChanged(bool topLevel); + void visibilityChanged(bool visible); protected: void actionEvent(QActionEvent *event); diff --git a/src/gui/widgets/qvalidator.cpp b/src/gui/widgets/qvalidator.cpp index a5276d3..0b5cc5a 100644 --- a/src/gui/widgets/qvalidator.cpp +++ b/src/gui/widgets/qvalidator.cpp @@ -400,8 +400,10 @@ QValidator::State QIntValidator::validate(QString & input, int&) const qlonglong entered = QLocalePrivate::bytearrayToLongLong(buff.constData(), 10, &ok, &overflow); if (overflow || !ok) return Invalid; - if (entered >= b && entered <= t) - return Acceptable; + if (entered >= b && entered <= t) { + locale().toInt(input, &ok); + return ok ? Acceptable : Intermediate; + } if (entered >= 0) { // the -entered < b condition is necessary to allow people to type @@ -412,6 +414,20 @@ QValidator::State QIntValidator::validate(QString & input, int&) const } } +/*! \reimp */ +void QIntValidator::fixup(QString &input) const +{ + QByteArray buff; + if (!locale().d()->validateChars(input, QLocalePrivate::IntegerMode, &buff)) { + QLocale cl(QLocale::C); + if (!cl.d()->validateChars(input, QLocalePrivate::IntegerMode, &buff)) + return; + } + bool ok, overflow; + qlonglong entered = QLocalePrivate::bytearrayToLongLong(buff.constData(), 10, &ok, &overflow); + if (ok && !overflow) + input = locale().toString(entered); +} /*! Sets the range of the validator to only accept integers between \a diff --git a/src/gui/widgets/qvalidator.h b/src/gui/widgets/qvalidator.h index 30afbd6..e996a01 100644 --- a/src/gui/widgets/qvalidator.h +++ b/src/gui/widgets/qvalidator.h @@ -105,6 +105,7 @@ public: ~QIntValidator(); QValidator::State validate(QString &, int &) const; + void fixup(QString &input) const; void setBottom(int); void setTop(int); diff --git a/src/multimedia/audio/qaudiodeviceinfo.cpp b/src/multimedia/audio/qaudiodeviceinfo.cpp index 092efc5..19b4669 100644 --- a/src/multimedia/audio/qaudiodeviceinfo.cpp +++ b/src/multimedia/audio/qaudiodeviceinfo.cpp @@ -105,8 +105,8 @@ public: The values supported by the the device for each of these parameters can be fetched with - supportedByteOrders(), supportedChannels(), supportedCodecs(), - supportedFrequencies(), supportedSampleSizes(), and + supportedByteOrders(), supportedChannelCounts(), supportedCodecs(), + supportedSampleRates(), supportedSampleSizes(), and supportedSampleTypes(). The combinations supported are dependent on the platform, audio plugins installed and the audio device capabilities. If you need a specific format, you can check if the device supports it with isFormatSupported(), or fetch a @@ -259,7 +259,18 @@ QStringList QAudioDeviceInfo::supportedCodecs() const } /*! - Returns a list of supported frequencies. + Returns a list of supported sample rates. +*/ + +QList<int> QAudioDeviceInfo::supportedSampleRates() const +{ + return supportedFrequencies(); +} + +/*! + \obsolete + + Use supportedSampleRates() instead. */ QList<int> QAudioDeviceInfo::supportedFrequencies() const @@ -268,7 +279,18 @@ QList<int> QAudioDeviceInfo::supportedFrequencies() const } /*! - Returns a list of supported channels. + Returns a list of supported channel counts. +*/ + +QList<int> QAudioDeviceInfo::supportedChannelCounts() const +{ + return supportedChannels(); +} + +/*! + \obsolete + + Use supportedChannelCount() instead. */ QList<int> QAudioDeviceInfo::supportedChannels() const diff --git a/src/multimedia/audio/qaudiodeviceinfo.h b/src/multimedia/audio/qaudiodeviceinfo.h index 62dc8a2..1cc0731 100644 --- a/src/multimedia/audio/qaudiodeviceinfo.h +++ b/src/multimedia/audio/qaudiodeviceinfo.h @@ -84,7 +84,9 @@ public: QStringList supportedCodecs() const; QList<int> supportedFrequencies() const; + QList<int> supportedSampleRates() const; QList<int> supportedChannels() const; + QList<int> supportedChannelCounts() const; QList<int> supportedSampleSizes() const; QList<QAudioFormat::Endian> supportedByteOrders() const; QList<QAudioFormat::SampleType> supportedSampleTypes() const; diff --git a/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp index f6b8154..955a880 100644 --- a/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp +++ b/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp @@ -95,14 +95,14 @@ QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const QAudioFormat nearest; if(mode == QAudio::AudioOutput) { nearest.setFrequency(44100); - nearest.setChannels(2); + nearest.setChannelCount(2); nearest.setByteOrder(QAudioFormat::LittleEndian); nearest.setSampleType(QAudioFormat::SignedInt); nearest.setSampleSize(16); nearest.setCodec(QLatin1String("audio/pcm")); } else { nearest.setFrequency(11025); - nearest.setChannels(1); + nearest.setChannelCount(1); nearest.setByteOrder(QAudioFormat::LittleEndian); nearest.setSampleType(QAudioFormat::SignedInt); nearest.setSampleSize(8); diff --git a/src/multimedia/audio/qaudioformat.cpp b/src/multimedia/audio/qaudioformat.cpp index 89ae0ff..e349b9e 100644 --- a/src/multimedia/audio/qaudioformat.cpp +++ b/src/multimedia/audio/qaudioformat.cpp @@ -111,7 +111,7 @@ public: \o Parameter \o Description \row - \o Frequency + \o Sample Rate \o Samples per second of audio data in Hertz. \row \o Number of channels @@ -143,8 +143,8 @@ public: Values are initialized as follows: \list - \o frequency() = -1 - \o channels() = -1 + \o sampleRate() = -1 + \o channelCount() = -1 \o sampleSize() = -1 \o byteOrder() = QAudioFormat::Endian(QSysInfo::ByteOrder) \o sampleType() = QAudioFormat::Unknown @@ -224,7 +224,18 @@ bool QAudioFormat::isValid() const } /*! - Sets the frequency to \a frequency. + Sets the sample rate to \a samplerate Hertz. +*/ + +void QAudioFormat::setSampleRate(int samplerate) +{ + d->frequency = samplerate; +} + +/*! + \obsolete + + Use setSampleRate() instead. */ void QAudioFormat::setFrequency(int frequency) @@ -233,7 +244,18 @@ void QAudioFormat::setFrequency(int frequency) } /*! - Returns the current frequency value. + Returns the current sample rate in Hertz. +*/ + +int QAudioFormat::sampleRate() const +{ + return d->frequency; +} + +/*! + \obsolete + + Use sampleRate() instead. */ int QAudioFormat::frequency() const @@ -242,7 +264,18 @@ int QAudioFormat::frequency() const } /*! - Sets the channels to \a channels. + Sets the channel count to \a channels. +*/ + +void QAudioFormat::setChannelCount(int channels) +{ + d->channels = channels; +} + +/*! + \obsolete + + Use setChannelCount() instead. */ void QAudioFormat::setChannels(int channels) @@ -251,7 +284,18 @@ void QAudioFormat::setChannels(int channels) } /*! - Returns the current channel value. + Returns the current channel count value. +*/ + +int QAudioFormat::channelCount() const +{ + return d->channels; +} + +/*! + \obsolete + + Use channelCount() instead. */ int QAudioFormat::channels() const diff --git a/src/multimedia/audio/qaudioformat.h b/src/multimedia/audio/qaudioformat.h index cb58d1c..6c835b7 100644 --- a/src/multimedia/audio/qaudioformat.h +++ b/src/multimedia/audio/qaudioformat.h @@ -75,9 +75,13 @@ public: void setFrequency(int frequency); int frequency() const; + void setSampleRate(int sampleRate); + int sampleRate() const; void setChannels(int channels); int channels() const; + void setChannelCount(int channelCount); + int channelCount() const; void setSampleSize(int sampleSize); int sampleSize() const; diff --git a/src/multimedia/audio/qaudioinput_win32_p.cpp b/src/multimedia/audio/qaudioinput_win32_p.cpp index 17e8bfb..84fd2a5 100644 --- a/src/multimedia/audio/qaudioinput_win32_p.cpp +++ b/src/multimedia/audio/qaudioinput_win32_p.cpp @@ -230,6 +230,11 @@ bool QAudioInputPrivate::open() } else { period_size = buffer_size/5; } +#ifdef Q_OS_WINCE + // For wince reduce size to 40ms for buffer size and 20ms period + buffer_size = settings.sampleRate()*settings.channelCount()*(settings.sampleSize()/8)*0.04; + period_size = buffer_size/2; +#endif timeStamp.restart(); elapsedTimeOffset = 0; wfx.nSamplesPerSec = settings.frequency(); diff --git a/src/network/access/qnetworkaccessdatabackend.cpp b/src/network/access/qnetworkaccessdatabackend.cpp index 52c554f..a2e7ef5 100644 --- a/src/network/access/qnetworkaccessdatabackend.cpp +++ b/src/network/access/qnetworkaccessdatabackend.cpp @@ -43,6 +43,7 @@ #include "qnetworkrequest.h" #include "qnetworkreply.h" #include "qurlinfo.h" +#include "private/qdataurl_p.h" QT_BEGIN_NAMESPACE @@ -78,53 +79,20 @@ void QNetworkAccessDataBackend::open() return; } - if (uri.host().isEmpty()) { - setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("text/plain;charset=US-ASCII")); - - // the following would have been the correct thing, but - // reality often differs from the specification. People have - // data: URIs with ? and # - //QByteArray data = QByteArray::fromPercentEncoding(uri.encodedPath()); - QByteArray data = QByteArray::fromPercentEncoding(uri.toEncoded()); - - // remove the data: scheme - data.remove(0, 5); - - // parse it: - int pos = data.indexOf(','); - if (pos != -1) { - QByteArray payload = data.mid(pos + 1); - data.truncate(pos); - data = data.trimmed(); - - // find out if the payload is encoded in Base64 - if (data.endsWith(";base64")) { - payload = QByteArray::fromBase64(payload); - data.chop(7); - } - - if (data.toLower().startsWith("charset")) { - int i = 7; // strlen("charset") - while (data.at(i) == ' ') - ++i; - if (data.at(i) == '=') - data.prepend("text/plain;"); - } - - if (!data.isEmpty()) - setHeader(QNetworkRequest::ContentTypeHeader, data.trimmed()); - - setHeader(QNetworkRequest::ContentLengthHeader, payload.size()); - emit metaDataChanged(); - - QByteDataBuffer list; - list.append(payload); - payload.clear(); // important because of implicit sharing! - writeDownstreamData(list); - - finished(); - return; - } + QPair<QString, QByteArray> decoded = qDecodeDataUrl(uri); + + if (! decoded.first.isNull()) { + setHeader(QNetworkRequest::ContentTypeHeader, decoded.first); + setHeader(QNetworkRequest::ContentLengthHeader, decoded.second.size()); + emit metaDataChanged(); + + QByteDataBuffer list; + list.append(decoded.second); + decoded.second.clear(); // important because of implicit sharing! + writeDownstreamData(list); + + finished(); + return; } // something wrong with this URI diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index 58123b2..519bc95 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -480,10 +480,24 @@ void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest, loadedFromCache = false; } +static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& prio) +{ + switch (prio) { + case QNetworkRequest::LowPriority: + return QHttpNetworkRequest::LowPriority; + case QNetworkRequest::HighPriority: + return QHttpNetworkRequest::HighPriority; + case QNetworkRequest::NormalPriority: + default: + return QHttpNetworkRequest::NormalPriority; + } +} + void QNetworkAccessHttpBackend::postRequest() { bool loadedFromCache = false; QHttpNetworkRequest httpRequest; + httpRequest.setPriority(convert(request().priority())); switch (operation()) { case QNetworkAccessManager::GetOperation: httpRequest.setOperation(QHttpNetworkRequest::Get); diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 6dcd05d..07ff7b4 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -218,8 +218,9 @@ class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate { public: inline QNetworkRequestPrivate() + : priority(QNetworkRequest::NormalPriority) #ifndef QT_NO_OPENSSL - : sslConfiguration(0) + , sslConfiguration(0) #endif { qRegisterMetaType<QNetworkRequest>(); } ~QNetworkRequestPrivate() @@ -234,6 +235,7 @@ public: : QSharedData(other), QNetworkHeadersPrivate(other) { url = other.url; + priority = other.priority; #ifndef QT_NO_OPENSSL sslConfiguration = 0; @@ -245,12 +247,14 @@ public: inline bool operator==(const QNetworkRequestPrivate &other) const { return url == other.url && + priority == other.priority && rawHeaders == other.rawHeaders && attributes == other.attributes; // don't compare cookedHeaders } QUrl url; + QNetworkRequest::Priority priority; #ifndef QT_NO_OPENSSL mutable QSslConfiguration *sslConfiguration; #endif @@ -516,6 +520,34 @@ QObject *QNetworkRequest::originatingObject() const return d->originatingObject.data(); } +/*! + \since 4.7 + + Return the priority of this request. + + \sa setPriority() +*/ +QNetworkRequest::Priority QNetworkRequest::priority() const +{ + return d->priority; +} + +/*! + \since 4.7 + + Set the priority of this request. + + \note The priority is only a hint to the network access manager. + It can use it or not. Currently it is used for HTTP to + decide which request should be sent first to a server. + + \sa priority() +*/ +void QNetworkRequest::setPriority(Priority priority) +{ + d->priority = priority; +} + static QByteArray headerName(QNetworkRequest::KnownHeaders header) { switch (header) { diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index fe01f8c..bc2d9da 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -89,6 +89,12 @@ public: AlwaysCache }; + enum Priority { + HighPriority = 1, + NormalPriority = 3, + LowPriority = 5 + }; + explicit QNetworkRequest(const QUrl &url = QUrl()); QNetworkRequest(const QNetworkRequest &other); ~QNetworkRequest(); @@ -123,6 +129,9 @@ public: void setOriginatingObject(QObject *object); QObject *originatingObject() const; + Priority priority() const; + void setPriority(Priority priority); + private: QSharedDataPointer<QNetworkRequestPrivate> d; friend class QNetworkRequestPrivate; diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp index 516b847..03b1bf0 100644 --- a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp +++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp @@ -145,7 +145,7 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc // threshold based on same algorithm as in qtriangulatingstroker.cpp int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * 3.14f / (curveInverseScale * 6)); if (threshold < 3) threshold = 3; - qreal one_over_threshold_minus_1 = 1.f / (threshold - 1); + qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1); for (int t=0; t<threshold; ++t) { QPointF pt = b.pointAt(t * one_over_threshold_minus_1); lineToArray(pt.x(), pt.y()); diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index caa679b..5e41cc1 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -77,6 +77,7 @@ #include <private/qfontengine_p.h> #include <private/qpixmapdata_gl_p.h> #include <private/qdatabuffer_p.h> +#include <private/qtriangulator_p.h> #include "qglgradientcache_p.h" #include "qglengineshadermanager_p.h" @@ -606,10 +607,13 @@ struct QGL2PEVectorPathCache { #ifdef QT_OPENGL_CACHE_AS_VBOS GLuint vbo; + GLuint ibo; #else float *vertices; + quint32 *indices; #endif int vertexCount; + int indexCount; GLenum primitiveType; qreal iscale; }; @@ -620,9 +624,12 @@ void QGL2PaintEngineExPrivate::cleanupVectorPath(QPaintEngineEx *engine, void *d #ifdef QT_OPENGL_CACHE_AS_VBOS Q_ASSERT(engine->type() == QPaintEngine::OpenGL2); static_cast<QGL2PaintEngineEx *>(engine)->d_func()->unusedVBOSToClean << c->vbo; + if (c->ibo) + d->unusedIBOSToClean << c->ibo; #else Q_UNUSED(engine); qFree(c->vertices); + qFree(c->indices); #endif delete c; } @@ -653,6 +660,9 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); + // ### Remove before release... + static bool do_vectorpath_cache = qgetenv("QT_OPENGL_NO_PATH_CACHE").isEmpty(); + // Check to see if there's any hints if (path.shape() == QVectorPath::RectangleHint) { QGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y()); @@ -664,6 +674,8 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) QVectorPath::CacheEntry *data = path.lookupCacheData(q); QGL2PEVectorPathCache *cache; + bool updateCache = false; + if (data) { cache = (QGL2PEVectorPathCache *) data->data; // Check if scale factor is exceeded for curved paths and generate curves if so... @@ -673,34 +685,39 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) #ifdef QT_OPENGL_CACHE_AS_VBOS glDeleteBuffers(1, &cache->vbo); cache->vbo = 0; + Q_ASSERT(cache->ibo == 0); #else qFree(cache->vertices); + Q_ASSERT(cache->indices == 0); #endif - cache->vertexCount = 0; + updateCache = true; } } } else { cache = new QGL2PEVectorPathCache; - cache->vertexCount = 0; data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath); + updateCache = true; } // Flatten the path at the current scale factor and fill it into the cache struct. - if (!cache->vertexCount) { + if (updateCache) { vertexCoordinateArray.clear(); vertexCoordinateArray.addPath(path, inverseScale, false); int vertexCount = vertexCoordinateArray.vertexCount(); int floatSizeInBytes = vertexCount * 2 * sizeof(float); cache->vertexCount = vertexCount; + cache->indexCount = 0; cache->primitiveType = GL_TRIANGLE_FAN; cache->iscale = inverseScale; #ifdef QT_OPENGL_CACHE_AS_VBOS glGenBuffers(1, &cache->vbo); glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); glBufferData(GL_ARRAY_BUFFER, floatSizeInBytes, vertexCoordinateArray.data(), GL_STATIC_DRAW); + cache->ibo = 0; #else cache->vertices = (float *) qMalloc(floatSizeInBytes); memcpy(cache->vertices, vertexCoordinateArray.data(), floatSizeInBytes); + cache->indices = 0; #endif } @@ -716,8 +733,6 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) } else { // printf(" - Marking path as cachable...\n"); // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable - // ### Remove before release... - static bool do_vectorpath_cache = qgetenv("QT_OPENGL_NO_PATH_CACHE").isEmpty(); if (do_vectorpath_cache) path.makeCacheable(); vertexCoordinateArray.clear(); @@ -727,31 +742,117 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) } } else { - // The path is too complicated & needs the stencil technique - vertexCoordinateArray.clear(); - vertexCoordinateArray.addPath(path, inverseScale, false); + bool useCache = path.isCacheable(); + if (useCache) { + QRectF bbox = path.controlPointRect(); + // If the path doesn't fit within these limits, it is possible that the triangulation will fail. + useCache &= (bbox.left() > -0x8000 * inverseScale) + && (bbox.right() < 0x8000 * inverseScale) + && (bbox.top() > -0x8000 * inverseScale) + && (bbox.bottom() < 0x8000 * inverseScale); + } - fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill()); + if (useCache) { + QVectorPath::CacheEntry *data = path.lookupCacheData(q); + QGL2PEVectorPathCache *cache; - glStencilMask(0xff); - glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + bool updateCache = false; + + if (data) { + cache = (QGL2PEVectorPathCache *) data->data; + // Check if scale factor is exceeded for curved paths and generate curves if so... + if (path.isCurved()) { + qreal scaleFactor = cache->iscale / inverseScale; + if (scaleFactor < 0.5 || scaleFactor > 2.0) { +#ifdef QT_OPENGL_CACHE_AS_VBOS + glDeleteBuffers(1, &cache->vbo); + glDeleteBuffers(1, &cache->ibo); +#else + qFree(cache->vertices); + qFree(cache->indices); +#endif + updateCache = true; + } + } + } else { + cache = new QGL2PEVectorPathCache; + data = const_cast<QVectorPath &>(path).addCacheData(q, cache, cleanupVectorPath); + updateCache = true; + } + + // Flatten the path at the current scale factor and fill it into the cache struct. + if (updateCache) { + QTriangleSet polys = qTriangulate(path, QTransform().scale(1 / inverseScale, 1 / inverseScale)); + cache->vertexCount = polys.vertices.size() / 2; + cache->indexCount = polys.indices.size(); + cache->primitiveType = GL_TRIANGLES; + cache->iscale = inverseScale; + +#ifdef QT_OPENGL_CACHE_AS_VBOS + glGenBuffers(1, &cache->vbo); + glGenBuffers(1, &cache->ibo); + glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(quint32) * polys.indices.size(), polys.indices.data(), GL_STATIC_DRAW); + + QVarLengthArray<float> vertices(polys.vertices.size()); + for (int i = 0; i < polys.vertices.size(); ++i) + vertices[i] = float(inverseScale * polys.vertices.at(i)); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_STATIC_DRAW); +#else + cache->vertices = (float *) qMalloc(sizeof(float) * polys.vertices.size()); + cache->indices = (quint32 *) qMalloc(sizeof(quint32) * polys.indices.size()); + memcpy(cache->indices, polys.indices.data(), sizeof(quint32) * polys.indices.size()); + for (int i = 0; i < polys.vertices.size(); ++i) + cache->vertices[i] = float(inverseScale * polys.vertices.at(i)); +#endif + } + + prepareForDraw(currentBrush.isOpaque()); +#ifdef QT_OPENGL_CACHE_AS_VBOS + glBindBuffer(GL_ARRAY_BUFFER, cache->vbo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache->ibo); + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, 0); + glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); +#else + setVertexAttributePointer(QT_VERTEX_COORDS_ATTR, cache->vertices); + glDrawElements(cache->primitiveType, cache->indexCount, GL_UNSIGNED_INT, cache->indices); +#endif - if (q->state()->clipTestEnabled) { - // Pass when high bit is set, replace stencil value with current clip - glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT); - } else if (path.hasWindingFill()) { - // Pass when any bit is set, replace stencil value with 0 - glStencilFunc(GL_NOTEQUAL, 0, 0xff); } else { - // Pass when high bit is set, replace stencil value with 0 - glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT); - } - prepareForDraw(currentBrush.isOpaque()); + // printf(" - Marking path as cachable...\n"); + // Tag it for later so that if the same path is drawn twice, it is assumed to be static and thus cachable + if (do_vectorpath_cache) + path.makeCacheable(); - // Stencil the brush onto the dest buffer - composite(vertexCoordinateArray.boundingRect()); - glStencilMask(0); - updateClipScissorTest(); + // The path is too complicated & needs the stencil technique + vertexCoordinateArray.clear(); + vertexCoordinateArray.addPath(path, inverseScale, false); + + fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill()); + + glStencilMask(0xff); + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + + if (q->state()->clipTestEnabled) { + // Pass when high bit is set, replace stencil value with current clip + glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT); + } else if (path.hasWindingFill()) { + // Pass when any bit is set, replace stencil value with 0 + glStencilFunc(GL_NOTEQUAL, 0, 0xff); + } else { + // Pass when high bit is set, replace stencil value with 0 + glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT); + } + prepareForDraw(currentBrush.isOpaque()); + + // Stencil the brush onto the dest buffer + composite(vertexCoordinateArray.boundingRect()); + glStencilMask(0); + updateClipScissorTest(); + } } } @@ -1605,6 +1706,10 @@ bool QGL2PaintEngineEx::end() glDeleteBuffers(d->unusedVBOSToClean.size(), d->unusedVBOSToClean.constData()); d->unusedVBOSToClean.clear(); } + if (!d->unusedIBOSToClean.isEmpty()) { + glDeleteBuffers(d->unusedIBOSToClean.size(), d->unusedIBOSToClean.constData()); + d->unusedIBOSToClean.clear(); + } #endif return false; diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index ce1b538..e3d0bfc 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -295,6 +295,7 @@ public: QSet<QVectorPath::CacheEntry *> pathCaches; QVector<GLuint> unusedVBOSToClean; + QVector<GLuint> unusedIBOSToClean; const GLfloat *vertexAttribPointers[3]; }; diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp index fed1658..6cb76ee 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp @@ -42,6 +42,8 @@ #include "qtextureglyphcache_gl_p.h" #include "qpaintengineex_opengl2_p.h" +QT_BEGIN_NAMESPACE + #ifdef Q_WS_WIN extern Q_GUI_EXPORT bool qt_cleartype_enabled; #endif @@ -243,3 +245,5 @@ int QGLTextureGlyphCache::glyphMargin() const return m_type == QFontEngineGlyphCache::Raster_RGBMask ? 2 : 0; #endif } + +QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qtriangulator.cpp b/src/opengl/gl2paintengineex/qtriangulator.cpp new file mode 100644 index 0000000..115f31b --- /dev/null +++ b/src/opengl/gl2paintengineex/qtriangulator.cpp @@ -0,0 +1,2983 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtriangulator_p.h" + +#include <QtGui/qdialog.h> +#include <QtGui/qevent.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpainterpath.h> +#include <QtGui/private/qbezier_p.h> +#include <QtGui/private/qdatabuffer_p.h> +#include <QtCore/qbitarray.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qqueue.h> +#include <QtCore/qglobal.h> +#include <QtCore/qpoint.h> +#include <QtCore/qalgorithms.h> +#include <QtDebug> + +#include <math.h> + +QT_BEGIN_NAMESPACE + +//#define Q_TRIANGULATOR_DEBUG + +#define Q_FIXED_POINT_SCALE 32 + +// Quick sort. +template <class T, class LessThan> +static void sort(T *array, int count, LessThan lessThan) +{ + // If the number of elements fall below some threshold, use insertion sort. + const int INSERTION_SORT_LIMIT = 7; // About 7 is fastest on my computer... + if (count <= INSERTION_SORT_LIMIT) { + for (int i = 1; i < count; ++i) { + T temp = array[i]; + int j = i; + while (j > 0 && lessThan(temp, array[j - 1])) { + array[j] = array[j - 1]; + --j; + } + array[j] = temp; + } + return; + } + + int high = count - 1; + int low = 0; + int mid = high / 2; + if (lessThan(array[mid], array[low])) + qSwap(array[mid], array[low]); + if (lessThan(array[high], array[mid])) + qSwap(array[high], array[mid]); + if (lessThan(array[mid], array[low])) + qSwap(array[mid], array[low]); + + --high; + ++low; + qSwap(array[mid], array[high]); + int pivot = high; + --high; + + while (low <= high) { + while (!lessThan(array[pivot], array[low])) { + ++low; + if (low > high) + goto sort_loop_end; + } + while (!lessThan(array[high], array[pivot])) { + --high; + if (low > high) + goto sort_loop_end; + } + qSwap(array[low], array[high]); + ++low; + --high; + } +sort_loop_end: + if (low != pivot) + qSwap(array[pivot], array[low]); + sort(array, low, lessThan); + sort(array + low + 1, count - low - 1, lessThan); +} + +// Quick sort. +template <class T> +static void sort(T *array, int count) +{ + // If the number of elements fall below some threshold, use insertion sort. + const int INSERTION_SORT_LIMIT = 25; // About 25 is fastest on my computer... + if (count <= INSERTION_SORT_LIMIT) { + for (int i = 1; i < count; ++i) { + T temp = array[i]; + int j = i; + while (j > 0 && (temp < array[j - 1])) { + array[j] = array[j - 1]; + --j; + } + array[j] = temp; + } + return; + } + + int high = count - 1; + int low = 0; + int mid = high / 2; + if ((array[mid] < array[low])) + qSwap(array[mid], array[low]); + if ((array[high] < array[mid])) + qSwap(array[high], array[mid]); + if ((array[mid] < array[low])) + qSwap(array[mid], array[low]); + + --high; + ++low; + qSwap(array[mid], array[high]); + int pivot = high; + --high; + + while (low <= high) { + while (!(array[pivot] < array[low])) { + ++low; + if (low > high) + goto sort_loop_end; + } + while (!(array[high] < array[pivot])) { + --high; + if (low > high) + goto sort_loop_end; + } + qSwap(array[low], array[high]); + ++low; + --high; + } +sort_loop_end: + if (low != pivot) + qSwap(array[pivot], array[low]); + sort(array, low); + sort(array + low + 1, count - low - 1); +} + +//============================================================================// +// QFraction // +//============================================================================// + +// Fraction must be in the range [0, 1) +struct QFraction +{ + // Comparison operators must not be called on invalid fractions. + inline bool operator < (const QFraction &other) const; + inline bool operator == (const QFraction &other) const; + inline bool operator != (const QFraction &other) const {return !(*this == other);} + inline bool operator > (const QFraction &other) const {return other < *this;} + inline bool operator >= (const QFraction &other) const {return !(*this < other);} + inline bool operator <= (const QFraction &other) const {return !(*this > other);} + + inline bool isValid() const {return denominator != 0;} + + // numerator and denominator must not have common denominators. + quint64 numerator, denominator; +}; + +static inline quint64 gcd(quint64 x, quint64 y) +{ + while (y != 0) { + quint64 z = y; + y = x % y; + x = z; + } + return x; +} + +static inline int compare(quint64 a, quint64 b) +{ + return (a > b) - (a < b); +} + +// Compare a/b with c/d. +// Return negative if less, 0 if equal, positive if greater. +// a < b, c < d +static int qCompareFractions(quint64 a, quint64 b, quint64 c, quint64 d) +{ + const quint64 LIMIT = Q_UINT64_C(0x100000000); + for (;;) { + // If the products 'ad' and 'bc' fit into 64 bits, they can be directly compared. + if (b < LIMIT && d < LIMIT) + return compare(a * d, b * c); + + if (a == 0 || c == 0) + return compare(a, c); + + // a/b < c/d <=> d/c < b/a + quint64 b_div_a = b / a; + quint64 d_div_c = d / c; + if (b_div_a != d_div_c) + return compare(d_div_c, b_div_a); + + // floor(d/c) == floor(b/a) + // frac(d/c) < frac(b/a) ? + // frac(x/y) = (x%y)/y + d -= d_div_c * c; //d %= c; + b -= b_div_a * a; //b %= a; + qSwap(a, d); + qSwap(b, c); + } +} + +// Fraction must be in the range [0, 1) +// Assume input is valid. +static QFraction qFraction(quint64 n, quint64 d) { + QFraction result; + if (n == 0) { + result.numerator = 0; + result.denominator = 1; + } else { + quint64 g = gcd(n, d); + result.numerator = n / g; + result.denominator = d / g; + } + return result; +} + +inline bool QFraction::operator < (const QFraction &other) const +{ + return qCompareFractions(numerator, denominator, other.numerator, other.denominator) < 0; +} + +inline bool QFraction::operator == (const QFraction &other) const +{ + return numerator == other.numerator && denominator == other.denominator; +} + +//============================================================================// +// QPodPoint // +//============================================================================// + +struct QPodPoint +{ + inline bool operator < (const QPodPoint &other) const + { + if (y != other.y) + return y < other.y; + return x < other.x; + } + + inline bool operator > (const QPodPoint &other) const {return other < *this;} + inline bool operator <= (const QPodPoint &other) const {return !(*this > other);} + inline bool operator >= (const QPodPoint &other) const {return !(*this < other);} + inline bool operator == (const QPodPoint &other) const {return x == other.x && y == other.y;} + inline bool operator != (const QPodPoint &other) const {return x != other.x || y != other.y;} + + inline QPodPoint &operator += (const QPodPoint &other) {x += other.x; y += other.y; return *this;} + inline QPodPoint &operator -= (const QPodPoint &other) {x -= other.x; y -= other.y; return *this;} + inline QPodPoint operator + (const QPodPoint &other) const {QPodPoint result = {x + other.x, y + other.y}; return result;} + inline QPodPoint operator - (const QPodPoint &other) const {QPodPoint result = {x - other.x, y - other.y}; return result;} + + int x; + int y; +}; + +static inline qint64 qCross(const QPodPoint &u, const QPodPoint &v) +{ + return qint64(u.x) * qint64(v.y) - qint64(u.y) * qint64(v.x); +} + +static inline qint64 qDot(const QPodPoint &u, const QPodPoint &v) +{ + return qint64(u.x) * qint64(v.x) + qint64(u.y) * qint64(v.y); +} + +// Return positive value if 'p' is to the right of the line 'v1'->'v2', negative if left of the +// line and zero if exactly on the line. +// The returned value is the z-component of the qCross product between 'v2-v1' and 'p-v1', +// which is twice the signed area of the triangle 'p'->'v1'->'v2' (positive for CW order). +static inline qint64 qPointDistanceFromLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2) +{ + return qCross(v2 - v1, p - v1); +} + +static inline bool qPointIsLeftOfLine(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2) +{ + return qPointDistanceFromLine(p, v1, v2) < 0; +} + +// Return: +// -1 if u < v +// 0 if u == v +// 1 if u > v +static int comparePoints(const QPodPoint &u, const QPodPoint &v) +{ + if (u.y < v.y) + return -1; + if (u.y > v.y) + return 1; + if (u.x < v.x) + return -1; + if (u.x > v.x) + return 1; + return 0; +} + +//============================================================================// +// QIntersectionPoint // +//============================================================================// + +struct QIntersectionPoint +{ + inline bool isValid() const {return xOffset.isValid() && yOffset.isValid();} + QPodPoint round() const; + inline bool isAccurate() const {return xOffset.numerator == 0 && yOffset.numerator == 0;} + bool operator < (const QIntersectionPoint &other) const; + bool operator == (const QIntersectionPoint &other) const; + inline bool operator != (const QIntersectionPoint &other) const {return !(*this == other);} + inline bool operator > (const QIntersectionPoint &other) const {return other < *this;} + inline bool operator >= (const QIntersectionPoint &other) const {return !(*this < other);} + inline bool operator <= (const QIntersectionPoint &other) const {return !(*this > other);} + bool isOnLine(const QPodPoint &u, const QPodPoint &v) const; + + QPodPoint upperLeft; + QFraction xOffset; + QFraction yOffset; +}; + +static inline QIntersectionPoint qIntersectionPoint(const QPodPoint &point) +{ + // upperLeft = point, xOffset = 0/1, yOffset = 0/1. + QIntersectionPoint p = {{point.x, point.y}, {0, 1}, {0, 1}}; + return p; +} + +static inline QIntersectionPoint qIntersectionPoint(int x, int y) +{ + // upperLeft = (x, y), xOffset = 0/1, yOffset = 0/1. + QIntersectionPoint p = {{x, y}, {0, 1}, {0, 1}}; + return p; +} + +static QIntersectionPoint qIntersectionPoint(const QPodPoint &u1, const QPodPoint &u2, const QPodPoint &v1, const QPodPoint &v2) +{ + QIntersectionPoint result = {{0, 0}, {0, 0}, {0, 0}}; + + QPodPoint u = u2 - u1; + QPodPoint v = v2 - v1; + qint64 d1 = qCross(u, v1 - u1); + qint64 d2 = qCross(u, v2 - u1); + qint64 det = d2 - d1; + qint64 d3 = qCross(v, u1 - v1); + qint64 d4 = d3 - det; //qCross(v, u2 - v1); + + // Check that the math is correct. + Q_ASSERT(d4 == qCross(v, u2 - v1)); + + // The intersection point can be expressed as: + // v1 - v * d1/det + // v2 - v * d2/det + // u1 + u * d3/det + // u2 + u * d4/det + + // I'm only interested in lines that are crossing, so ignore parallel lines even if they overlap. + if (det == 0) + return result; + + if (det < 0) { + det = -det; + d1 = -d1; + d2 = -d2; + d3 = -d3; + d4 = -d4; + } + + // I'm only interested in lines intersecting at their interior, not at their end points. + // The lines intersect at their interior if and only if 'd1 < 0', 'd2 > 0', 'd3 < 0' and 'd4 > 0'. + if (d1 >= 0 || d2 <= 0 || d3 <= 0 || d4 >= 0) + return result; + + // Calculate the intersection point as follows: + // v1 - v * d1/det | v1 <= v2 (component-wise) + // v2 - v * d2/det | v2 < v1 (component-wise) + + // Assuming 21 bits per vector component. + // TODO: Make code path for 31 bits per vector component. + if (v.x >= 0) { + result.upperLeft.x = v1.x + (-v.x * d1) / det; + result.xOffset = qFraction(quint64(-v.x * d1) % quint64(det), quint64(det)); + } else { + result.upperLeft.x = v2.x + (-v.x * d2) / det; + result.xOffset = qFraction(quint64(-v.x * d2) % quint64(det), quint64(det)); + } + + if (v.y >= 0) { + result.upperLeft.y = v1.y + (-v.y * d1) / det; + result.yOffset = qFraction(quint64(-v.y * d1) % quint64(det), quint64(det)); + } else { + result.upperLeft.y = v2.y + (-v.y * d2) / det; + result.yOffset = qFraction(quint64(-v.y * d2) % quint64(det), quint64(det)); + } + + Q_ASSERT(result.xOffset.isValid()); + Q_ASSERT(result.yOffset.isValid()); + return result; +} + +QPodPoint QIntersectionPoint::round() const +{ + QPodPoint result = upperLeft; + if (2 * xOffset.numerator >= xOffset.denominator) + ++result.x; + if (2 * yOffset.numerator >= yOffset.denominator) + ++result.y; + return result; +} + +bool QIntersectionPoint::operator < (const QIntersectionPoint &other) const +{ + if (upperLeft.y != other.upperLeft.y) + return upperLeft.y < other.upperLeft.y; + if (yOffset != other.yOffset) + return yOffset < other.yOffset; + if (upperLeft.x != other.upperLeft.x) + return upperLeft.x < other.upperLeft.x; + return xOffset < other.xOffset; +} + +bool QIntersectionPoint::operator == (const QIntersectionPoint &other) const +{ + return upperLeft == other.upperLeft && xOffset == other.xOffset && yOffset == other.yOffset; +} + +// Returns true if this point is on the infinite line passing through 'u' and 'v'. +bool QIntersectionPoint::isOnLine(const QPodPoint &u, const QPodPoint &v) const +{ + // TODO: Make code path for coordinates with more than 21 bits. + const QPodPoint p = upperLeft - u; + const QPodPoint q = v - u; + bool isHorizontal = p.y == 0 && yOffset.numerator == 0; + bool isVertical = p.x == 0 && xOffset.numerator == 0; + if (isHorizontal && isVertical) + return true; + if (isHorizontal) + return q.y == 0; + if (q.y == 0) + return false; + if (isVertical) + return q.x == 0; + if (q.x == 0) + return false; + + // At this point, 'p+offset' and 'q' cannot lie on the x or y axis. + + if (((q.x < 0) == (q.y < 0)) != ((p.x < 0) == (p.y < 0))) + return false; // 'p + offset' and 'q' pass through different quadrants. + + // Move all coordinates into the first quadrant. + quint64 nx, ny; + if (p.x < 0) + nx = quint64(-p.x) * xOffset.denominator - xOffset.numerator; + else + nx = quint64(p.x) * xOffset.denominator + xOffset.numerator; + if (p.y < 0) + ny = quint64(-p.y) * yOffset.denominator - yOffset.numerator; + else + ny = quint64(p.y) * yOffset.denominator + yOffset.numerator; + + return qFraction(quint64(qAbs(q.x)) * xOffset.denominator, quint64(qAbs(q.y)) * yOffset.denominator) == qFraction(nx, ny); +} + +//============================================================================// +// QMaxHeap // +//============================================================================// + +template <class T> +class QMaxHeap +{ +public: + inline int size() const {return m_data.size();} + inline bool empty() const {return m_data.isEmpty();} + inline bool isEmpty() const {return m_data.isEmpty();} + void push(const T &x); + T pop(); + inline const T &top() const {return m_data.first();} +private: + static inline int parent(int i) {return (i - 1) / 2;} + static inline int left(int i) {return 2 * i + 1;} + static inline int right(int i) {return 2 * i + 2;} + + QDataBuffer<T> m_data; +}; + +template <class T> +void QMaxHeap<T>::push(const T &x) +{ + int current = m_data.size(); + int parent = QMaxHeap::parent(current); + m_data.add(x); + while (current != 0 && m_data.at(parent) < x) { + m_data.at(current) = m_data.at(parent); + current = parent; + parent = QMaxHeap::parent(current); + } + m_data.at(current) = x; +} + +template <class T> +T QMaxHeap<T>::pop() +{ + T result = m_data.first(); + T back = m_data.last(); + m_data.pop_back(); + if (!m_data.isEmpty()) { + int current = 0; + for (;;) { + int left = QMaxHeap::left(current); + int right = QMaxHeap::right(current); + if (left >= m_data.size()) + break; + int greater = left; + if (right < m_data.size() && m_data.at(left) < m_data.at(right)) + greater = right; + if (m_data.at(greater) < back) + break; + m_data.at(current) = m_data.at(greater); + current = greater; + } + m_data.at(current) = back; + } + return result; +} + +//============================================================================// +// QRBTree // +//============================================================================// + +template <class T> +struct QRBTree +{ + struct Node + { + inline Node() : parent(0), left(0), right(0), red(true) { } + inline ~Node() {if (left) delete left; if (right) delete right;} + T data; + Node *parent; + Node *left; + Node *right; + bool red; + }; + + inline QRBTree() : root(0), freeList(0) { } + inline ~QRBTree(); + + inline void clear(); + + void attachBefore(Node *parent, Node *child); + void attachAfter(Node *parent, Node *child); + + inline Node *front(Node *node) const; + inline Node *back(Node *node) const; + Node *next(Node *node) const; + Node *previous(Node *node) const; + + inline void deleteNode(Node *&node); + inline Node *newNode(); + + // Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise. + // 'left' and 'right' cannot be null. + int order(Node *left, Node *right); + inline bool verify() const; + +private: + void rotateLeft(Node *node); + void rotateRight(Node *node); + void update(Node *node); + + inline void attachLeft(Node *parent, Node *child); + inline void attachRight(Node *parent, Node *child); + + int blackDepth(Node *top) const; + bool checkRedBlackProperty(Node *top) const; + + void swapNodes(Node *n1, Node *n2); + void detach(Node *node); + + // 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree. + void rebalance(Node *node); + +public: + Node *root; +private: + Node *freeList; +}; + +template <class T> +inline QRBTree<T>::~QRBTree() +{ + clear(); + while (freeList) { + // Avoid recursively calling the destructor, as this list may become large. + Node *next = freeList->right; + freeList->right = 0; + delete freeList; + freeList = next; + } +} + +template <class T> +inline void QRBTree<T>::clear() +{ + if (root) + delete root; + root = 0; +} + +template <class T> +void QRBTree<T>::rotateLeft(Node *node) +{ + // | | // + // N B // + // / \ / \ // + // A B ---> N D // + // / \ / \ // + // C D A C // + + Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); + ref = node->right; + node->right->parent = node->parent; + + // : // + // N // + // / :| // + // A B // + // / \ // + // C D // + + node->right = ref->left; + if (ref->left) + ref->left->parent = node; + + // : | // + // N B // + // / \ : \ // + // A C D // + + ref->left = node; + node->parent = ref; + + // | // + // B // + // / \ // + // N D // + // / \ // + // A C // +} + +template <class T> +void QRBTree<T>::rotateRight(Node *node) +{ + // | | // + // N A // + // / \ / \ // + // A B ---> C N // + // / \ / \ // + // C D D B // + + Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); + ref = node->left; + node->left->parent = node->parent; + + node->left = ref->right; + if (ref->right) + ref->right->parent = node; + + ref->right = node; + node->parent = ref; +} + +template <class T> +void QRBTree<T>::update(Node *node) // call this after inserting a node +{ + for (;;) { + Node *parent = node->parent; + + // if the node is the root, color it black + if (!parent) { + node->red = false; + return; + } + + // if the parent is black, the node can be left red + if (!parent->red) + return; + + // at this point, the parent is red and cannot be the root + Node *grandpa = parent->parent; + Q_ASSERT(grandpa); + + Node *uncle = (parent == grandpa->left ? grandpa->right : grandpa->left); + if (uncle && uncle->red) { + // grandpa's black, parent and uncle are red. + // let parent and uncle be black, grandpa red and recursively update grandpa. + Q_ASSERT(!grandpa->red); + parent->red = false; + uncle->red = false; + grandpa->red = true; + node = grandpa; + continue; + } + + // at this point, uncle is black + if (node == parent->right && parent == grandpa->left) + rotateLeft(node = parent); + else if (node == parent->left && parent == grandpa->right) + rotateRight(node = parent); + parent = node->parent; + + if (parent == grandpa->left) { + rotateRight(grandpa); + parent->red = false; + grandpa->red = true; + } else { + rotateLeft(grandpa); + parent->red = false; + grandpa->red = true; + } + return; + } +} + +template <class T> +inline void QRBTree<T>::attachLeft(Node *parent, Node *child) +{ + Q_ASSERT(!parent->left); + parent->left = child; + child->parent = parent; + update(child); +} + +template <class T> +inline void QRBTree<T>::attachRight(Node *parent, Node *child) +{ + Q_ASSERT(!parent->right); + parent->right = child; + child->parent = parent; + update(child); +} + +template <class T> +void QRBTree<T>::attachBefore(Node *parent, Node *child) +{ + if (!root) + update(root = child); + else if (!parent) + attachRight(back(root), child); + else if (parent->left) + attachRight(back(parent->left), child); + else + attachLeft(parent, child); +} + +template <class T> +void QRBTree<T>::attachAfter(Node *parent, Node *child) +{ + if (!root) + update(root = child); + else if (!parent) + attachLeft(front(root), child); + else if (parent->right) + attachLeft(front(parent->right), child); + else + attachRight(parent, child); +} + +template <class T> +void QRBTree<T>::swapNodes(Node *n1, Node *n2) +{ + // Since iterators must not be invalidated, it is not sufficient to only swap the data. + if (n1->parent == n2) { + n1->parent = n2->parent; + n2->parent = n1; + } else if (n2->parent == n1) { + n2->parent = n1->parent; + n1->parent = n2; + } else { + qSwap(n1->parent, n2->parent); + } + + qSwap(n1->left, n2->left); + qSwap(n1->right, n2->right); + qSwap(n1->red, n2->red); + + if (n1->parent) { + if (n1->parent->left == n2) + n1->parent->left = n1; + else + n1->parent->right = n1; + } else { + root = n1; + } + + if (n2->parent) { + if (n2->parent->left == n1) + n2->parent->left = n2; + else + n2->parent->right = n2; + } else { + root = n2; + } + + if (n1->left) + n1->left->parent = n1; + if (n1->right) + n1->right->parent = n1; + + if (n2->left) + n2->left->parent = n2; + if (n2->right) + n2->right->parent = n2; +} + +template <class T> +void QRBTree<T>::detach(Node *node) // call this before removing a node. +{ + if (node->right) + swapNodes(node, front(node->right)); + + Node *child = (node->left ? node->left : node->right); + + if (!node->red) { + if (child && child->red) + child->red = false; + else + rebalance(node); + } + + Node *&ref = (node->parent ? (node == node->parent->left ? node->parent->left : node->parent->right) : root); + ref = child; + if (child) + child->parent = node->parent; + node->left = node->right = node->parent = 0; +} + +// 'node' must be black. rebalance will reduce the depth of black nodes by one in the sibling tree. +template <class T> +void QRBTree<T>::rebalance(Node *node) +{ + Q_ASSERT(!node->red); + for (;;) { + if (!node->parent) + return; + + // at this point, node is not a parent, it is black, thus it must have a sibling. + Node *sibling = (node == node->parent->left ? node->parent->right : node->parent->left); + Q_ASSERT(sibling); + + if (sibling->red) { + sibling->red = false; + node->parent->red = true; + if (node == node->parent->left) + rotateLeft(node->parent); + else + rotateRight(node->parent); + sibling = (node == node->parent->left ? node->parent->right : node->parent->left); + Q_ASSERT(sibling); + } + + // at this point, the sibling is black. + Q_ASSERT(!sibling->red); + + if ((!sibling->left || !sibling->left->red) && (!sibling->right || !sibling->right->red)) { + bool parentWasRed = node->parent->red; + sibling->red = true; + node->parent->red = false; + if (parentWasRed) + return; + node = node->parent; + continue; + } + + // at this point, at least one of the sibling's children is red. + + if (node == node->parent->left) { + if (!sibling->right || !sibling->right->red) { + Q_ASSERT(sibling->left); + sibling->red = true; + sibling->left->red = false; + rotateRight(sibling); + + sibling = sibling->parent; + Q_ASSERT(sibling); + } + sibling->red = node->parent->red; + node->parent->red = false; + + Q_ASSERT(sibling->right->red); + sibling->right->red = false; + rotateLeft(node->parent); + } else { + if (!sibling->left || !sibling->left->red) { + Q_ASSERT(sibling->right); + sibling->red = true; + sibling->right->red = false; + rotateLeft(sibling); + + sibling = sibling->parent; + Q_ASSERT(sibling); + } + sibling->red = node->parent->red; + node->parent->red = false; + + Q_ASSERT(sibling->left->red); + sibling->left->red = false; + rotateRight(node->parent); + } + return; + } +} + +template <class T> +inline typename QRBTree<T>::Node *QRBTree<T>::front(Node *node) const +{ + while (node->left) + node = node->left; + return node; +} + +template <class T> +inline typename QRBTree<T>::Node *QRBTree<T>::back(Node *node) const +{ + while (node->right) + node = node->right; + return node; +} + +template <class T> +typename QRBTree<T>::Node *QRBTree<T>::next(Node *node) const +{ + if (node->right) + return front(node->right); + while (node->parent && node == node->parent->right) + node = node->parent; + return node->parent; +} + +template <class T> +typename QRBTree<T>::Node *QRBTree<T>::previous(Node *node) const +{ + if (node->left) + return back(node->left); + while (node->parent && node == node->parent->left) + node = node->parent; + return node->parent; +} + +template <class T> +int QRBTree<T>::blackDepth(Node *top) const +{ + if (!top) + return 0; + int leftDepth = blackDepth(top->left); + int rightDepth = blackDepth(top->right); + if (leftDepth != rightDepth) + return -1; + if (!top->red) + ++leftDepth; + return leftDepth; +} + +template <class T> +bool QRBTree<T>::checkRedBlackProperty(Node *top) const +{ + if (!top) + return true; + if (top->left && !checkRedBlackProperty(top->left)) + return false; + if (top->right && !checkRedBlackProperty(top->right)) + return false; + return !(top->red && ((top->left && top->left->red) || (top->right && top->right->red))); +} + +template <class T> +inline bool QRBTree<T>::verify() const +{ + return checkRedBlackProperty(root) && blackDepth(root) != -1; +} + +template <class T> +inline void QRBTree<T>::deleteNode(Node *&node) +{ + Q_ASSERT(node); + detach(node); + node->right = freeList; + freeList = node; + node = 0; +} + +template <class T> +inline typename QRBTree<T>::Node *QRBTree<T>::newNode() +{ + if (freeList) { + Node *node = freeList; + freeList = freeList->right; + node->parent = node->left = node->right = 0; + node->red = true; + return node; + } + return new Node; +} + +// Return 1 if 'left' comes after 'right', 0 if equal, and -1 otherwise. +// 'left' and 'right' cannot be null. +template <class T> +int QRBTree<T>::order(Node *left, Node *right) +{ + Q_ASSERT(left && right); + if (left == right) + return 0; + + QVector<Node *> leftAncestors; + QVector<Node *> rightAncestors; + while (left) { + leftAncestors.push_back(left); + left = left->parent; + } + while (right) { + rightAncestors.push_back(right); + right = right->parent; + } + Q_ASSERT(leftAncestors.back() == root && rightAncestors.back() == root); + + while (!leftAncestors.empty() && !rightAncestors.empty() && leftAncestors.back() == rightAncestors.back()) { + leftAncestors.pop_back(); + rightAncestors.pop_back(); + } + + if (!leftAncestors.empty()) + return (leftAncestors.back() == leftAncestors.back()->parent->left ? -1 : 1); + + if (!rightAncestors.empty()) + return (rightAncestors.back() == rightAncestors.back()->parent->right ? -1 : 1); + + // The code should never reach this point. + Q_ASSERT(!leftAncestors.empty() || !rightAncestors.empty()); + return 0; +} + +//============================================================================// +// QInt64Hash // +//============================================================================// + +// Copied from qhash.cpp +static const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 +}; + +// Copied from qhash.cpp +static inline int primeForNumBits(int numBits) +{ + return (1 << numBits) + prime_deltas[numBits]; +} + +static inline int primeForCount(int count) +{ + int low = 0; + int high = 32; + for (int i = 0; i < 5; ++i) { + int mid = (high + low) / 2; + if (count >= 1 << mid) + low = mid; + else + high = mid; + } + return primeForNumBits(high); +} + +// Hash set of quint64s. Elements cannot be removed without clearing the +// entire set. A value of -1 is used to mark unused entries. +class QInt64Set +{ +public: + inline QInt64Set(int capacity = 64); + inline ~QInt64Set() {if (m_array) delete[] m_array;} + inline bool isValid() const {return m_array;} + void insert(quint64 key); + bool contains(quint64 key) const; + inline void clear(); +private: + bool rehash(int capacity); + + static const quint64 UNUSED; + + quint64 *m_array; + int m_capacity; + int m_count; +}; + +const quint64 QInt64Set::UNUSED = quint64(-1); + +inline QInt64Set::QInt64Set(int capacity) +{ + m_capacity = primeForCount(capacity); + m_array = new quint64[m_capacity]; + if (m_array) + clear(); + else + m_capacity = 0; +} + +bool QInt64Set::rehash(int capacity) +{ + quint64 *oldArray = m_array; + int oldCapacity = m_capacity; + + m_capacity = capacity; + m_array = new quint64[m_capacity]; + if (m_array) { + clear(); + if (oldArray) { + for (int i = 0; i < oldCapacity; ++i) { + if (oldArray[i] != UNUSED) + insert(oldArray[i]); + } + delete[] oldArray; + } + return true; + } else { + m_capacity = oldCapacity; + m_array = oldArray; + return false; + } +} + +void QInt64Set::insert(quint64 key) +{ + if (m_count > 3 * m_capacity / 4) + rehash(primeForCount(2 * m_capacity)); + Q_ASSERT_X(m_array, "QInt64Hash<T>::insert", "Hash set not allocated."); + int index = int(key % m_capacity); + for (int i = 0; i < m_capacity; ++i) { + index += i; + if (index >= m_capacity) + index -= m_capacity; + if (m_array[index] == key) + return; + if (m_array[index] == UNUSED) { + ++m_count; + m_array[index] = key; + return; + } + } + Q_ASSERT_X(0, "QInt64Hash<T>::insert", "Hash set full."); +} + +bool QInt64Set::contains(quint64 key) const +{ + Q_ASSERT_X(m_array, "QInt64Hash<T>::contains", "Hash set not allocated."); + int index = int(key % m_capacity); + for (int i = 0; i < m_capacity; ++i) { + index += i; + if (index >= m_capacity) + index -= m_capacity; + if (m_array[index] == key) + return true; + if (m_array[index] == UNUSED) + return false; + } + return false; +} + +inline void QInt64Set::clear() +{ + Q_ASSERT_X(m_array, "QInt64Hash<T>::clear", "Hash set not allocated."); + for (int i = 0; i < m_capacity; ++i) + m_array[i] = UNUSED; + m_count = 0; +} + +//============================================================================// +// QRingBuffer // +//============================================================================// + +// T must be POD. +template <class T> +class QRingBuffer +{ +public: + inline QRingBuffer() : m_array(0), m_head(0), m_size(0), m_capacity(0) { } + inline ~QRingBuffer() {if (m_array) delete[] m_array;} + bool reallocate(int capacity); + inline const T &head() const {Q_ASSERT(m_size > 0); return m_array[m_head];} + inline const T &dequeue(); + inline void enqueue(const T &x); + inline bool isEmpty() const {return m_size == 0;} +private: + T *m_array; + int m_head; + int m_size; + int m_capacity; +}; + +template <class T> +bool QRingBuffer<T>::reallocate(int capacity) +{ + T *oldArray = m_array; + m_array = new T[capacity]; + if (m_array) { + if (oldArray) { + if (m_head + m_size > m_capacity) { + memcpy(m_array, oldArray + m_head, (m_capacity - m_head) * sizeof(T)); + memcpy(m_array + (m_capacity - m_head), oldArray, (m_head + m_size - m_capacity) * sizeof(T)); + } else { + memcpy(m_array, oldArray + m_head, m_size * sizeof(T)); + } + delete[] oldArray; + } + m_capacity = capacity; + m_head = 0; + return true; + } else { + m_array = oldArray; + return false; + } +} + +template <class T> +inline const T &QRingBuffer<T>::dequeue() +{ + Q_ASSERT(m_size > 0); + Q_ASSERT(m_array); + Q_ASSERT(m_capacity >= m_size); + int index = m_head; + if (++m_head >= m_capacity) + m_head -= m_capacity; + --m_size; + return m_array[index]; +} + +template <class T> +inline void QRingBuffer<T>::enqueue(const T &x) +{ + if (m_size == m_capacity) + reallocate(qMax(2 * m_capacity, 64)); + int index = m_head + m_size; + if (index >= m_capacity) + index -= m_capacity; + m_array[index] = x; + ++m_size; +} + +//============================================================================// +// QTriangulator // +//============================================================================// + +class QTriangulator +{ +public: + typedef QVarLengthArray<int, 6> ShortArray; + + //================================// + // QTriangulator::ComplexToSimple // + //================================// + friend class ComplexToSimple; + class ComplexToSimple + { + public: + inline ComplexToSimple(QTriangulator *parent) : m_parent(parent) { } + void decompose(); + private: + struct Edge + { + inline int &upper() {return pointingUp ? to : from;} + inline int &lower() {return pointingUp ? from : to;} + inline int upper() const {return pointingUp ? to : from;} + inline int lower() const {return pointingUp ? from : to;} + + QRBTree<int>::Node *node; + int from, to; // vertex + int next, previous; // edge + int winding; + bool mayIntersect; + bool pointingUp, originallyPointingUp; + }; + + friend class CompareEdges; + class CompareEdges + { + public: + inline CompareEdges(ComplexToSimple *parent) : m_parent(parent) { } + bool operator () (int i, int j) const; + private: + ComplexToSimple *m_parent; + }; + + struct Intersection + { + bool operator < (const Intersection &other) const {return other.intersectionPoint < intersectionPoint;} + + QIntersectionPoint intersectionPoint; + int vertex; + int leftEdge; + int rightEdge; + }; + + struct Split + { + int vertex; + int edge; + bool accurate; + }; + + struct Event + { + enum Type {Upper, Lower}; + inline bool operator < (const Event &other) const; + + QPodPoint point; + Type type; + int edge; + }; + +#ifdef Q_TRIANGULATOR_DEBUG + friend class DebugDialog; + friend class QTriangulator; + class DebugDialog : public QDialog + { + public: + DebugDialog(ComplexToSimple *parent, int currentVertex); + protected: + void paintEvent(QPaintEvent *); + void wheelEvent(QWheelEvent *); + void mouseMoveEvent(QMouseEvent *); + void mousePressEvent(QMouseEvent *); + private: + ComplexToSimple *m_parent; + QRectF m_window; + QPoint m_lastMousePos; + int m_vertex; + }; +#endif + + void initEdges(); + bool calculateIntersection(int left, int right); + bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const; + QRBTree<int>::Node *searchEdgeLeftOf(int edgeIndex) const; + QRBTree<int>::Node *searchEdgeLeftOf(int edgeIndex, QRBTree<int>::Node *after) const; + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> bounds(const QPodPoint &point) const; + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> outerBounds(const QPodPoint &point) const; + void splitEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint); + void reorderEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost); + void sortEdgeList(const QPodPoint eventPoint); + void fillPriorityQueue(); + void calculateIntersections(); + int splitEdge(int splitIndex); + bool splitEdgesAtIntersections(); + void insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i); + void removeUnwantedEdgesAndConnect(); + void removeUnusedPoints(); + + QTriangulator *m_parent; + QDataBuffer<Edge> m_edges; + QRBTree<int> m_edgeList; + QDataBuffer<Event> m_events; + QDataBuffer<Split> m_splits; + QMaxHeap<Intersection> m_topIntersection; + QInt64Set m_processedEdgePairs; + int m_initialPointCount; + }; +#ifdef Q_TRIANGULATOR_DEBUG + friend class ComplexToSimple::DebugDialog; +#endif + + //=================================// + // QTriangulator::SimpleToMonotone // + //=================================// + friend class SimpleToMonotone; + class SimpleToMonotone + { + public: + inline SimpleToMonotone(QTriangulator *parent) : m_parent(parent) { } + void decompose(); + private: + enum VertexType {MergeVertex, EndVertex, RegularVertex, StartVertex, SplitVertex}; + + struct Edge + { + QRBTree<int>::Node *node; + int helper, twin, next, previous; + quint32 from, to; + VertexType type; + bool pointingUp; + int upper() const {return (pointingUp ? to : from);} + int lower() const {return (pointingUp ? from : to);} + }; + + friend class CompareVertices; + class CompareVertices + { + public: + CompareVertices(SimpleToMonotone *parent) : m_parent(parent) { } + bool operator () (int i, int j) const; + private: + SimpleToMonotone *m_parent; + }; + + void setupDataStructures(); + void removeZeroLengthEdges(); + void fillPriorityQueue(); + bool edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const; + // Returns the rightmost edge not to the right of the given edge. + QRBTree<int>::Node *searchEdgeLeftOfEdge(int edgeIndex) const; + // Returns the rightmost edge left of the given point. + QRBTree<int>::Node *searchEdgeLeftOfPoint(int pointIndex) const; + void classifyVertex(int i); + void classifyVertices(); + bool pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3); + bool pointIsInSector(int vertex, int sector); + int findSector(int edge, int vertex); + void createDiagonal(int lower, int upper); + void monotoneDecomposition(); + + QTriangulator *m_parent; + QRBTree<int> m_edgeList; + QDataBuffer<Edge> m_edges; + QDataBuffer<int> m_upperVertex; + bool m_clockwiseOrder; + }; + + //====================================// + // QTriangulator::MonotoneToTriangles // + //====================================// + friend class MonotoneToTriangles; + class MonotoneToTriangles + { + public: + inline MonotoneToTriangles(QTriangulator *parent) : m_parent(parent) { } + void decompose(); + private: + inline quint32 indices(int index) const {return m_parent->m_indices.at(index + m_first);} + inline int next(int index) const {return (index + 1) % m_length;} + inline int previous(int index) const {return (index + m_length - 1) % m_length;} + inline bool less(int i, int j) const {return m_parent->m_vertices.at(indices(i)) < m_parent->m_vertices.at(indices(j));} + inline bool leftOfEdge(int i, int j, int k) const + { + return qPointIsLeftOfLine(m_parent->m_vertices.at(indices(i)), + m_parent->m_vertices.at(indices(j)), m_parent->m_vertices.at(indices(k))); + } + + QTriangulator *m_parent; + int m_first; + int m_length; + }; + + inline QTriangulator() { } + + // Call this only once. + void initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix); + // Call this only once. + void initialize(const QVectorPath &path, const QTransform &matrix, qreal lod); + // Call this only once. + void initialize(const QPainterPath &path, const QTransform &matrix, qreal lod); + // Call either triangulate() or polyline() only once. + QTriangleSet triangulate(); + QPolylineSet polyline(); +private: + QDataBuffer<QPodPoint> m_vertices; + QVector<quint32> m_indices; + uint m_hint; +}; + +//============================================================================// +// QTriangulator // +//============================================================================// + +QTriangleSet QTriangulator::triangulate() +{ + for (int i = 0; i < m_vertices.size(); ++i) { + Q_ASSERT(qAbs(m_vertices.at(i).x) < (1 << 21)); + Q_ASSERT(qAbs(m_vertices.at(i).y) < (1 << 21)); + } + + if (!(m_hint & (QVectorPath::OddEvenFill | QVectorPath::WindingFill))) + m_hint |= QVectorPath::OddEvenFill; + + if (m_hint & QVectorPath::NonConvexShapeMask) { + ComplexToSimple c2s(this); + c2s.decompose(); + SimpleToMonotone s2m(this); + s2m.decompose(); + } + MonotoneToTriangles m2t(this); + m2t.decompose(); + + QTriangleSet result; + result.indices = m_indices; + result.vertices.resize(2 * m_vertices.size()); + for (int i = 0; i < m_vertices.size(); ++i) { + result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE; + result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE; + } + return result; +} + +QPolylineSet QTriangulator::polyline() +{ + QPolylineSet result; + result.indices = m_indices; + result.vertices.resize(2 * m_vertices.size()); + for (int i = 0; i < m_vertices.size(); ++i) { + result.vertices[2 * i + 0] = qreal(m_vertices.at(i).x) / Q_FIXED_POINT_SCALE; + result.vertices[2 * i + 1] = qreal(m_vertices.at(i).y) / Q_FIXED_POINT_SCALE; + } + return result; +} + +void QTriangulator::initialize(const qreal *polygon, int count, uint hint, const QTransform &matrix) +{ + m_hint = hint; + m_vertices.resize(count); + m_indices.resize(count + 1); + for (int i = 0; i < count; ++i) { + qreal x, y; + matrix.map(polygon[2 * i + 0], polygon[2 * i + 1], &x, &y); + m_vertices.at(i).x = qRound(x * Q_FIXED_POINT_SCALE); + m_vertices.at(i).y = qRound(y * Q_FIXED_POINT_SCALE); + m_indices[i] = i; + } + m_indices[count] = Q_TRIANGULATE_END_OF_POLYGON; +} + +void QTriangulator::initialize(const QVectorPath &path, const QTransform &matrix, qreal lod) +{ + m_hint = path.hints(); + // Curved paths will be converted to complex polygons. + m_hint &= ~QVectorPath::CurvedShapeMask; + + const qreal *p = path.points(); + const QPainterPath::ElementType *e = path.elements(); + if (e) { + for (int i = 0; i < path.elementCount(); ++i, ++e, p += 2) { + switch (*e) { + case QPainterPath::MoveToElement: + if (!m_indices.isEmpty()) + m_indices.push_back(Q_TRIANGULATE_END_OF_POLYGON); + // Fall through. + case QPainterPath::LineToElement: + m_indices.push_back(quint32(m_vertices.size())); + m_vertices.resize(m_vertices.size() + 1); + qreal x, y; + matrix.map(p[0], p[1], &x, &y); + m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE); + m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE); + break; + case QPainterPath::CurveToElement: + { + qreal pts[8]; + for (int i = 0; i < 4; ++i) + matrix.map(p[2 * i - 2], p[2 * i - 1], &pts[2 * i + 0], &pts[2 * i + 1]); + for (int i = 0; i < 8; ++i) + pts[i] *= lod; + QBezier bezier = QBezier::fromPoints(QPointF(pts[0], pts[1]), QPointF(pts[2], pts[3]), QPointF(pts[4], pts[5]), QPointF(pts[6], pts[7])); + QPolygonF poly = bezier.toPolygon(); + // Skip first point, it already exists in 'm_vertices'. + for (int j = 1; j < poly.size(); ++j) { + m_indices.push_back(quint32(m_vertices.size())); + m_vertices.resize(m_vertices.size() + 1); + m_vertices.last().x = qRound(poly.at(j).x() * Q_FIXED_POINT_SCALE / lod); + m_vertices.last().y = qRound(poly.at(j).y() * Q_FIXED_POINT_SCALE / lod); + } + } + i += 2; + e += 2; + p += 4; + break; + default: + Q_ASSERT_X(0, "QTriangulator::triangulate", "Unexpected element type."); + break; + } + } + } else { + for (int i = 0; i < path.elementCount(); ++i, p += 2) { + m_indices.push_back(quint32(m_vertices.size())); + m_vertices.resize(m_vertices.size() + 1); + qreal x, y; + matrix.map(p[0], p[1], &x, &y); + m_vertices.last().x = qRound(x * Q_FIXED_POINT_SCALE); + m_vertices.last().y = qRound(y * Q_FIXED_POINT_SCALE); + } + } + m_indices.push_back(Q_TRIANGULATE_END_OF_POLYGON); +} + +void QTriangulator::initialize(const QPainterPath &path, const QTransform &matrix, qreal lod) +{ + initialize(qtVectorPathForPath(path), matrix, lod); +} + +//============================================================================// +// QTriangulator::ComplexToSimple // +//============================================================================// + +void QTriangulator::ComplexToSimple::decompose() +{ + m_initialPointCount = m_parent->m_vertices.size(); + initEdges(); + do { + calculateIntersections(); + } while (splitEdgesAtIntersections()); + + removeUnwantedEdgesAndConnect(); + removeUnusedPoints(); + + m_parent->m_indices.clear(); + QBitArray processed(m_edges.size(), false); + for (int first = 0; first < m_edges.size(); ++first) { + // If already processed, or if unused path, skip. + if (processed.at(first) || m_edges.at(first).next == -1) + continue; + + int i = first; + do { + Q_ASSERT(!processed.at(i)); + Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i); + m_parent->m_indices.push_back(m_edges.at(i).from); + processed.setBit(i); + i = m_edges.at(i).next; // CCW order + } while (i != first); + m_parent->m_indices.push_back(Q_TRIANGULATE_END_OF_POLYGON); + } +} + +void QTriangulator::ComplexToSimple::initEdges() +{ + // Initialize edge structure. + // 'next' and 'previous' are not being initialized at this point. + int first = 0; + for (int i = 0; i < m_parent->m_indices.size(); ++i) { + if (m_parent->m_indices.at(i) == Q_TRIANGULATE_END_OF_POLYGON) { + if (m_edges.size() != first) + m_edges.last().to = m_edges.at(first).from; + first = m_edges.size(); + } else { + Q_ASSERT(i + 1 < m_parent->m_indices.size()); + // {node, from, to, next, previous, winding, mayIntersect, pointingUp, originallyPointingUp} + Edge edge = {0, m_parent->m_indices.at(i), m_parent->m_indices.at(i + 1), -1, -1, 0, true, false, false}; + m_edges.add(edge); + } + } + if (first != m_edges.size()) + m_edges.last().to = m_edges.at(first).from; + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp = + m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); + } +} + +// Return true if new intersection was found +bool QTriangulator::ComplexToSimple::calculateIntersection(int left, int right) +{ + const Edge &e1 = m_edges.at(left); + const Edge &e2 = m_edges.at(right); + + const QPodPoint &u1 = m_parent->m_vertices.at(e1.from); + const QPodPoint &u2 = m_parent->m_vertices.at(e1.to); + const QPodPoint &v1 = m_parent->m_vertices.at(e2.from); + const QPodPoint &v2 = m_parent->m_vertices.at(e2.to); + if (qMax(u1.x, u2.x) <= qMin(v1.x, v2.x)) + return false; + + quint64 key = (left > right ? (quint64(right) << 32) | quint64(left) : (quint64(left) << 32) | quint64(right)); + if (m_processedEdgePairs.contains(key)) + return false; + m_processedEdgePairs.insert(key); + + Intersection intersection; + intersection.leftEdge = left; + intersection.rightEdge = right; + intersection.intersectionPoint = qIntersectionPoint(u1, u2, v1, v2); + + if (!intersection.intersectionPoint.isValid()) + return false; + + Q_ASSERT(intersection.intersectionPoint.isOnLine(u1, u2)); + Q_ASSERT(intersection.intersectionPoint.isOnLine(v1, v2)); + + intersection.vertex = m_parent->m_vertices.size(); + m_topIntersection.push(intersection); + m_parent->m_vertices.add(intersection.intersectionPoint.round()); + return true; +} + +bool QTriangulator::ComplexToSimple::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const +{ + const Edge &leftEdge = m_edges.at(leftEdgeIndex); + const Edge &rightEdge = m_edges.at(rightEdgeIndex); + const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper()); + const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower()); + const QPodPoint &upper = m_parent->m_vertices.at(leftEdge.upper()); + if (upper.x < qMin(l.x, u.x)) + return true; + if (upper.x > qMax(l.x, u.x)) + return false; + qint64 d = qPointDistanceFromLine(upper, l, u); + // d < 0: left, d > 0: right, d == 0: on top + if (d == 0) + d = qPointDistanceFromLine(m_parent->m_vertices.at(leftEdge.lower()), l, u); + return d < 0; +} + +QRBTree<int>::Node *QTriangulator::ComplexToSimple::searchEdgeLeftOf(int edgeIndex) const +{ + QRBTree<int>::Node *current = m_edgeList.root; + QRBTree<int>::Node *result = 0; + while (current) { + if (edgeIsLeftOfEdge(edgeIndex, current->data)) { + current = current->left; + } else { + result = current; + current = current->right; + } + } + return result; +} + +QRBTree<int>::Node *QTriangulator::ComplexToSimple::searchEdgeLeftOf(int edgeIndex, QRBTree<int>::Node *after) const +{ + if (!m_edgeList.root) + return after; + QRBTree<int>::Node *result = after; + QRBTree<int>::Node *current = (after ? m_edgeList.next(after) : m_edgeList.front(m_edgeList.root)); + while (current) { + if (edgeIsLeftOfEdge(edgeIndex, current->data)) + return result; + result = current; + current = m_edgeList.next(current); + } + return result; +} + +QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> QTriangulator::ComplexToSimple::bounds(const QPodPoint &point) const +{ + QRBTree<int>::Node *current = m_edgeList.root; + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> result(0, 0); + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = qPointDistanceFromLine(point, v1, v2); + if (d == 0) { + result.first = result.second = current; + break; + } + current = (d < 0 ? current->left : current->right); + } + if (current == 0) + return result; + + current = result.first->left; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = qPointDistanceFromLine(point, v1, v2); + Q_ASSERT(d >= 0); + if (d == 0) { + result.first = current; + current = current->left; + } else { + current = current->right; + } + } + + current = result.second->right; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = qPointDistanceFromLine(point, v1, v2); + Q_ASSERT(d <= 0); + if (d == 0) { + result.second = current; + current = current->right; + } else { + current = current->left; + } + } + + return result; +} + +QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> QTriangulator::ComplexToSimple::outerBounds(const QPodPoint &point) const +{ + QRBTree<int>::Node *current = m_edgeList.root; + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> result(0, 0); + + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = qPointDistanceFromLine(point, v1, v2); + if (d == 0) + break; + if (d < 0) { + result.second = current; + current = current->left; + } else { + result.first = current; + current = current->right; + } + } + + if (!current) + return result; + + QRBTree<int>::Node *mid = current; + + current = mid->left; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = qPointDistanceFromLine(point, v1, v2); + Q_ASSERT(d >= 0); + if (d == 0) { + current = current->left; + } else { + result.first = current; + current = current->right; + } + } + + current = mid->right; + while (current) { + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &v2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = qPointDistanceFromLine(point, v1, v2); + Q_ASSERT(d <= 0); + if (d == 0) { + current = current->right; + } else { + result.second = current; + current = current->left; + } + } + + return result; +} + +void QTriangulator::ComplexToSimple::splitEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost, int vertex, const QIntersectionPoint &intersectionPoint) +{ + Q_ASSERT(leftmost && rightmost); + + // Split. + for (;;) { + const QPodPoint &u = m_parent->m_vertices.at(m_edges.at(leftmost->data).from); + const QPodPoint &v = m_parent->m_vertices.at(m_edges.at(leftmost->data).to); + Q_ASSERT(intersectionPoint.isOnLine(u, v)); + const Split split = {vertex, leftmost->data, intersectionPoint.isAccurate()}; + if (intersectionPoint.xOffset.numerator != 0 || intersectionPoint.yOffset.numerator != 0 || (intersectionPoint.upperLeft != u && intersectionPoint.upperLeft != v)) + m_splits.add(split); + if (leftmost == rightmost) + break; + leftmost = m_edgeList.next(leftmost); + } +} + + +void QTriangulator::ComplexToSimple::reorderEdgeListRange(QRBTree<int>::Node *leftmost, QRBTree<int>::Node *rightmost) +{ + Q_ASSERT(leftmost && rightmost); + + QRBTree<int>::Node *storeLeftmost = leftmost; + QRBTree<int>::Node *storeRightmost = rightmost; + + // Reorder. + while (leftmost != rightmost) { + Edge &left = m_edges.at(leftmost->data); + Edge &right = m_edges.at(rightmost->data); + qSwap(left.node, right.node); + qSwap(leftmost->data, rightmost->data); + leftmost = m_edgeList.next(leftmost); + if (leftmost == rightmost) + break; + rightmost = m_edgeList.previous(rightmost); + } + + rightmost = m_edgeList.next(storeRightmost); + leftmost = m_edgeList.previous(storeLeftmost); + if (leftmost) + calculateIntersection(leftmost->data, storeLeftmost->data); + if (rightmost) + calculateIntersection(storeRightmost->data, rightmost->data); +} + +void QTriangulator::ComplexToSimple::sortEdgeList(const QPodPoint eventPoint) +{ + QIntersectionPoint eventPoint2 = qIntersectionPoint(eventPoint); + while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint < eventPoint2) { + Intersection intersection = m_topIntersection.pop(); + + QIntersectionPoint currentIntersectionPoint = intersection.intersectionPoint; + int currentVertex = intersection.vertex; + + QRBTree<int>::Node *leftmost = m_edges.at(intersection.leftEdge).node; + QRBTree<int>::Node *rightmost = m_edges.at(intersection.rightEdge).node; + + for (;;) { + QRBTree<int>::Node *previous = m_edgeList.previous(leftmost); + if (!previous) + break; + const Edge &edge = m_edges.at(previous->data); + const QPodPoint &u = m_parent->m_vertices.at(edge.from); + const QPodPoint &v = m_parent->m_vertices.at(edge.to); + if (!currentIntersectionPoint.isOnLine(u, v)) { + Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0); + break; + } + leftmost = previous; + } + + for (;;) { + QRBTree<int>::Node *next = m_edgeList.next(rightmost); + if (!next) + break; + const Edge &edge = m_edges.at(next->data); + const QPodPoint &u = m_parent->m_vertices.at(edge.from); + const QPodPoint &v = m_parent->m_vertices.at(edge.to); + if (!currentIntersectionPoint.isOnLine(u, v)) { + Q_ASSERT(!currentIntersectionPoint.isAccurate() || qCross(currentIntersectionPoint.upperLeft - u, v - u) != 0); + break; + } + rightmost = next; + } + + Q_ASSERT(leftmost && rightmost); + splitEdgeListRange(leftmost, rightmost, currentVertex, currentIntersectionPoint); + reorderEdgeListRange(leftmost, rightmost); + + while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= currentIntersectionPoint) + m_topIntersection.pop(); + +#ifdef Q_TRIANGULATOR_DEBUG + DebugDialog dialog(this, intersection.vertex); + dialog.exec(); +#endif + + } +} + +void QTriangulator::ComplexToSimple::fillPriorityQueue() +{ + m_events.reset(); + m_events.reserve(m_edges.size() * 2); + for (int i = 0; i < m_edges.size(); ++i) { + Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1); + Q_ASSERT(m_edges.at(i).node == 0); + Q_ASSERT(m_edges.at(i).pointingUp == m_edges.at(i).originallyPointingUp); + Q_ASSERT(m_edges.at(i).pointingUp == (m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from))); + // Ignore zero-length edges. + if (m_parent->m_vertices.at(m_edges.at(i).to) != m_parent->m_vertices.at(m_edges.at(i).from)) { + QPodPoint upper = m_parent->m_vertices.at(m_edges.at(i).upper()); + QPodPoint lower = m_parent->m_vertices.at(m_edges.at(i).lower()); + Event upperEvent = {{upper.x, upper.y}, Event::Upper, i}; + Event lowerEvent = {{lower.x, lower.y}, Event::Lower, i}; + m_events.add(upperEvent); + m_events.add(lowerEvent); + } + } + //qSort(m_events.data(), m_events.data() + m_events.size()); + sort(m_events.data(), m_events.size()); +} + +void QTriangulator::ComplexToSimple::calculateIntersections() +{ + fillPriorityQueue(); + + Q_ASSERT(m_topIntersection.empty()); + Q_ASSERT(m_edgeList.root == 0); + + // Find all intersection points. + while (!m_events.isEmpty()) { + Event event = m_events.last(); + sortEdgeList(event.point); + + // Find all edges in the edge list that contain the current vertex and mark them to be split later. + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> range = bounds(event.point); + QRBTree<int>::Node *leftNode = range.first ? m_edgeList.previous(range.first) : 0; + int vertex = (event.type == Event::Upper ? m_edges.at(event.edge).upper() : m_edges.at(event.edge).lower()); + QIntersectionPoint eventPoint = qIntersectionPoint(event.point); + + if (range.first != 0) { + splitEdgeListRange(range.first, range.second, vertex, eventPoint); + reorderEdgeListRange(range.first, range.second); + } + + // Handle the edges with start or end point in the current vertex. + while (!m_events.isEmpty() && m_events.last().point == event.point) { + event = m_events.last(); + m_events.pop_back(); + int i = event.edge; + + if (m_edges.at(i).node) { + // Remove edge from edge list. + Q_ASSERT(event.type == Event::Lower); + QRBTree<int>::Node *left = m_edgeList.previous(m_edges.at(i).node); + QRBTree<int>::Node *right = m_edgeList.next(m_edges.at(i).node); + m_edgeList.deleteNode(m_edges.at(i).node); + if (!left || !right) + continue; + calculateIntersection(left->data, right->data); + } else { + // Insert edge into edge list. + Q_ASSERT(event.type == Event::Upper); + QRBTree<int>::Node *left = searchEdgeLeftOf(i, leftNode); + m_edgeList.attachAfter(left, m_edges.at(i).node = m_edgeList.newNode()); + m_edges.at(i).node->data = i; + QRBTree<int>::Node *right = m_edgeList.next(m_edges.at(i).node); + if (left) + calculateIntersection(left->data, i); + if (right) + calculateIntersection(i, right->data); + } + } + while (!m_topIntersection.isEmpty() && m_topIntersection.top().intersectionPoint <= eventPoint) + m_topIntersection.pop(); +#ifdef Q_TRIANGULATOR_DEBUG + DebugDialog dialog(this, vertex); + dialog.exec(); +#endif + } + m_processedEdgePairs.clear(); +} + +// Split an edge into two pieces at the given point. +// The upper piece is pushed to the end of the 'm_edges' vector. +// The lower piece replaces the old edge. +// Return the edge whose 'from' is 'pointIndex'. +int QTriangulator::ComplexToSimple::splitEdge(int splitIndex) +{ + const Split &split = m_splits.at(splitIndex); + Edge &lowerEdge = m_edges.at(split.edge); + Q_ASSERT(lowerEdge.node == 0); + Q_ASSERT(lowerEdge.previous == -1 && lowerEdge.next == -1); + + if (lowerEdge.from == split.vertex) + return split.edge; + if (lowerEdge.to == split.vertex) + return lowerEdge.next; + + // Check that angle >= 90 degrees. + //Q_ASSERT(qDot(m_points.at(m_edges.at(edgeIndex).from) - m_points.at(pointIndex), + // m_points.at(m_edges.at(edgeIndex).to) - m_points.at(pointIndex)) <= 0); + + Edge upperEdge = lowerEdge; + upperEdge.mayIntersect |= !split.accurate; // The edge may have been split before at an inaccurate split point. + lowerEdge.mayIntersect = !split.accurate; + if (lowerEdge.pointingUp) { + lowerEdge.to = upperEdge.from = split.vertex; + m_edges.add(upperEdge); + return m_edges.size() - 1; + } else { + lowerEdge.from = upperEdge.to = split.vertex; + m_edges.add(upperEdge); + return split.edge; + } +} + +bool QTriangulator::ComplexToSimple::splitEdgesAtIntersections() +{ + for (int i = 0; i < m_edges.size(); ++i) + m_edges.at(i).mayIntersect = false; + bool checkForNewIntersections = false; + for (int i = 0; i < m_splits.size(); ++i) { + splitEdge(i); + checkForNewIntersections |= !m_splits.at(i).accurate; + } + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).originallyPointingUp = m_edges.at(i).pointingUp = + m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); + } + m_splits.reset(); + return checkForNewIntersections; +} + +void QTriangulator::ComplexToSimple::insertEdgeIntoVectorIfWanted(ShortArray &orderedEdges, int i) +{ + // Edges with zero length should not reach this part. + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(i).from) != m_parent->m_vertices.at(m_edges.at(i).to)); + + // Skip edges with unwanted winding number. + int windingNumber = m_edges.at(i).winding; + if (m_edges.at(i).originallyPointingUp) + ++windingNumber; + + // Make sure exactly one fill rule is specified. + Q_ASSERT(((m_parent->m_hint & QVectorPath::WindingFill) != 0) != ((m_parent->m_hint & QVectorPath::OddEvenFill) != 0)); + + if ((m_parent->m_hint & QVectorPath::WindingFill) && windingNumber != 0 && windingNumber != 1) + return; + + // Skip cancelling edges. + if (!orderedEdges.isEmpty()) { + int j = orderedEdges[orderedEdges.size() - 1]; + // If the last edge is already connected in one end, it should not be cancelled. + if (m_edges.at(j).next == -1 && m_edges.at(j).previous == -1 + && (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(j).to)) + && (m_parent->m_vertices.at(m_edges.at(i).to) == m_parent->m_vertices.at(m_edges.at(j).from))) { + orderedEdges.removeLast(); + return; + } + } + orderedEdges.append(i); +} + +void QTriangulator::ComplexToSimple::removeUnwantedEdgesAndConnect() +{ + Q_ASSERT(m_edgeList.root == 0); + // Initialize priority queue. + fillPriorityQueue(); + + ShortArray orderedEdges; + + while (!m_events.isEmpty()) { + Event event = m_events.last(); + int edgeIndex = event.edge; + + // Check that all the edges in the list crosses the current scanline + //if (m_edgeList.root) { + // for (QRBTree<int>::Node *node = m_edgeList.front(m_edgeList.root); node; node = m_edgeList.next(node)) { + // Q_ASSERT(event.point <= m_points.at(m_edges.at(node->data).lower())); + // } + //} + + orderedEdges.clear(); + QPair<QRBTree<int>::Node *, QRBTree<int>::Node *> b = outerBounds(event.point); + if (m_edgeList.root) { + QRBTree<int>::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root)); + // Process edges that are going to be removed from the edge list at the current event point. + while (current != b.second) { + Q_ASSERT(current); + Q_ASSERT(m_edges.at(current->data).node == current); + Q_ASSERT(qIntersectionPoint(event.point).isOnLine(m_parent->m_vertices.at(m_edges.at(current->data).from), m_parent->m_vertices.at(m_edges.at(current->data).to))); + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(current->data).from) == event.point || m_parent->m_vertices.at(m_edges.at(current->data).to) == event.point); + insertEdgeIntoVectorIfWanted(orderedEdges, current->data); + current = m_edgeList.next(current); + } + } + + // Remove edges above the event point, insert edges below the event point. + do { + event = m_events.last(); + m_events.pop_back(); + edgeIndex = event.edge; + + // Edges with zero length should not reach this part. + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(edgeIndex).from) != m_parent->m_vertices.at(m_edges.at(edgeIndex).to)); + + if (m_edges.at(edgeIndex).node) { + Q_ASSERT(event.type == Event::Lower); + Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).lower())); + m_edgeList.deleteNode(m_edges.at(edgeIndex).node); + } else { + Q_ASSERT(event.type == Event::Upper); + Q_ASSERT(event.point == m_parent->m_vertices.at(m_edges.at(event.edge).upper())); + QRBTree<int>::Node *left = searchEdgeLeftOf(edgeIndex, b.first); + m_edgeList.attachAfter(left, m_edges.at(edgeIndex).node = m_edgeList.newNode()); + m_edges.at(edgeIndex).node->data = edgeIndex; + } + } while (!m_events.isEmpty() && m_events.last().point == event.point); + + if (m_edgeList.root) { + QRBTree<int>::Node *current = (b.first ? m_edgeList.next(b.first) : m_edgeList.front(m_edgeList.root)); + + // Calculate winding number and turn counter-clockwise. + int currentWindingNumber = (b.first ? m_edges.at(b.first->data).winding : 0); + while (current != b.second) { + Q_ASSERT(current); + //Q_ASSERT(b.second == 0 || m_edgeList.order(current, b.second) < 0); + int i = current->data; + Q_ASSERT(m_edges.at(i).node == current); + + // Winding number. + int ccwWindingNumber = m_edges.at(i).winding = currentWindingNumber; + if (m_edges.at(i).originallyPointingUp) { + --m_edges.at(i).winding; + } else { + ++m_edges.at(i).winding; + ++ccwWindingNumber; + } + currentWindingNumber = m_edges.at(i).winding; + + // Turn counter-clockwise. + if ((ccwWindingNumber & 1) == 0) { + Q_ASSERT(m_edges.at(i).previous == -1 && m_edges.at(i).next == -1); + qSwap(m_edges.at(i).from, m_edges.at(i).to); + m_edges.at(i).pointingUp = !m_edges.at(i).pointingUp; + } + + current = m_edgeList.next(current); + } + + // Process edges that were inserted into the edge list at the current event point. + current = (b.second ? m_edgeList.previous(b.second) : m_edgeList.back(m_edgeList.root)); + while (current != b.first) { + Q_ASSERT(current); + Q_ASSERT(m_edges.at(current->data).node == current); + insertEdgeIntoVectorIfWanted(orderedEdges, current->data); + current = m_edgeList.previous(current); + } + } + if (orderedEdges.isEmpty()) + continue; + + Q_ASSERT((orderedEdges.size() & 1) == 0); + + // Connect edges. + // First make sure the first edge point towards the current point. + int i; + if (m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).from) == event.point) { + i = 1; + int copy = orderedEdges[0]; // Make copy in case the append() will cause a reallocation. + orderedEdges.append(copy); + } else { + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[0]).to) == event.point); + i = 0; + } + + // Remove references to duplicate points. First find the point with lowest index. + int pointIndex = INT_MAX; + for (int j = i; j < orderedEdges.size(); j += 2) { + Q_ASSERT(j + 1 < orderedEdges.size()); + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j]).to) == event.point); + Q_ASSERT(m_parent->m_vertices.at(m_edges.at(orderedEdges[j + 1]).from) == event.point); + if (m_edges.at(orderedEdges[j]).to < pointIndex) + pointIndex = m_edges.at(orderedEdges[j]).to; + if (m_edges.at(orderedEdges[j + 1]).from < pointIndex) + pointIndex = m_edges.at(orderedEdges[j + 1]).from; + } + + for (; i < orderedEdges.size(); i += 2) { + // Remove references to duplicate points by making all edges reference one common point. + m_edges.at(orderedEdges[i]).to = m_edges.at(orderedEdges[i + 1]).from = pointIndex; + + Q_ASSERT(m_edges.at(orderedEdges[i]).pointingUp || m_edges.at(orderedEdges[i]).previous != -1); + Q_ASSERT(!m_edges.at(orderedEdges[i + 1]).pointingUp || m_edges.at(orderedEdges[i + 1]).next != -1); + + m_edges.at(orderedEdges[i]).next = orderedEdges[i + 1]; + m_edges.at(orderedEdges[i + 1]).previous = orderedEdges[i]; + } + } // end while +} + +void QTriangulator::ComplexToSimple::removeUnusedPoints() { + QBitArray used(m_parent->m_vertices.size(), false); + for (int i = 0; i < m_edges.size(); ++i) { + Q_ASSERT((m_edges.at(i).previous == -1) == (m_edges.at(i).next == -1)); + if (m_edges.at(i).next != -1) + used.setBit(m_edges.at(i).from); + } + QDataBuffer<quint32> newMapping(m_parent->m_vertices.size()); + newMapping.resize(m_parent->m_vertices.size()); + int count = 0; + for (int i = 0; i < m_parent->m_vertices.size(); ++i) { + if (used.at(i)) { + m_parent->m_vertices.at(count) = m_parent->m_vertices.at(i); + newMapping.at(i) = count; + ++count; + } + } + m_parent->m_vertices.resize(count); + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).from = newMapping.at(m_edges.at(i).from); + m_edges.at(i).to = newMapping.at(m_edges.at(i).to); + } +} + +bool QTriangulator::ComplexToSimple::CompareEdges::operator () (int i, int j) const +{ + int cmp = comparePoints(m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).from), + m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).from)); + if (cmp == 0) { + cmp = comparePoints(m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).to), + m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).to)); + } + return cmp > 0; +} + +inline bool QTriangulator::ComplexToSimple::Event::operator < (const Event &other) const +{ + if (point == other.point) + return type < other.type; // 'Lower' has higher priority than 'Upper'. + return other.point < point; +} + +//============================================================================// +// QTriangulator::ComplexToSimple::DebugDialog // +//============================================================================// + +#ifdef Q_TRIANGULATOR_DEBUG + +QTriangulator::ComplexToSimple::DebugDialog::DebugDialog(ComplexToSimple *parent, int currentVertex) + : m_parent(parent), m_vertex(currentVertex) +{ + QDataBuffer<QPodPoint> &vertices = m_parent->m_parent->m_vertices; + if (vertices.isEmpty()) + return; + + int minX, maxX, minY, maxY; + minX = maxX = vertices.at(0).x; + minY = maxY = vertices.at(0).y; + for (int i = 1; i < vertices.size(); ++i) { + minX = qMin(minX, vertices.at(i).x); + maxX = qMax(maxX, vertices.at(i).x); + minY = qMin(minY, vertices.at(i).y); + maxY = qMax(maxY, vertices.at(i).y); + } + int w = maxX - minX; + int h = maxY - minY; + qreal border = qMin(w, h) / 10.0; + m_window = QRectF(minX - border, minY - border, (maxX - minX + 2 * border), (maxY - minY + 2 * border)); +} + +void QTriangulator::ComplexToSimple::DebugDialog::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing, true); + p.fillRect(rect(), Qt::black); + QDataBuffer<QPodPoint> &vertices = m_parent->m_parent->m_vertices; + if (vertices.isEmpty()) + return; + + qreal halfPointSize = qMin(m_window.width(), m_window.height()) / 300.0; + p.setWindow(m_window.toRect()); + + p.setPen(Qt::white); + + QDataBuffer<Edge> &edges = m_parent->m_edges; + for (int i = 0; i < edges.size(); ++i) { + QPodPoint u = vertices.at(edges.at(i).from); + QPodPoint v = vertices.at(edges.at(i).to); + p.drawLine(u.x, u.y, v.x, v.y); + } + + for (int i = 0; i < vertices.size(); ++i) { + QPodPoint q = vertices.at(i); + p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::red); + } + + Qt::GlobalColor colors[6] = {Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::yellow}; + p.setOpacity(0.5); + int count = 0; + if (m_parent->m_edgeList.root) { + QRBTree<int>::Node *current = m_parent->m_edgeList.front(m_parent->m_edgeList.root); + while (current) { + p.setPen(colors[count++ % 6]); + QPodPoint u = vertices.at(edges.at(current->data).from); + QPodPoint v = vertices.at(edges.at(current->data).to); + p.drawLine(u.x, u.y, v.x, v.y); + current = m_parent->m_edgeList.next(current); + } + } + + p.setOpacity(1.0); + QPodPoint q = vertices.at(m_vertex); + p.fillRect(QRectF(q.x - halfPointSize, q.y - halfPointSize, 2 * halfPointSize, 2 * halfPointSize), Qt::green); + + p.setPen(Qt::gray); + QDataBuffer<Split> &splits = m_parent->m_splits; + for (int i = 0; i < splits.size(); ++i) { + QPodPoint q = vertices.at(splits.at(i).vertex); + QPodPoint u = vertices.at(edges.at(splits.at(i).edge).from) - q; + QPodPoint v = vertices.at(edges.at(splits.at(i).edge).to) - q; + qreal uLen = sqrt(qreal(qDot(u, u))); + qreal vLen = sqrt(qreal(qDot(v, v))); + if (uLen) { + u.x *= 2 * halfPointSize / uLen; + u.y *= 2 * halfPointSize / uLen; + } + if (vLen) { + v.x *= 2 * halfPointSize / vLen; + v.y *= 2 * halfPointSize / vLen; + } + u += q; + v += q; + p.drawLine(u.x, u.y, v.x, v.y); + } +} + +void QTriangulator::ComplexToSimple::DebugDialog::wheelEvent(QWheelEvent *event) +{ + qreal scale = exp(-0.001 * event->delta()); + QPointF center = m_window.center(); + QPointF delta = scale * (m_window.bottomRight() - center); + m_window = QRectF(center - delta, center + delta); + event->accept(); + update(); +} + +void QTriangulator::ComplexToSimple::DebugDialog::mouseMoveEvent(QMouseEvent *event) +{ + if (event->buttons() & Qt::LeftButton) { + QPointF delta = event->pos() - m_lastMousePos; + delta.setX(delta.x() * m_window.width() / width()); + delta.setY(delta.y() * m_window.height() / height()); + m_window.translate(-delta.x(), -delta.y()); + m_lastMousePos = event->pos(); + event->accept(); + update(); + } +} + +void QTriangulator::ComplexToSimple::DebugDialog::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + m_lastMousePos = event->pos(); + event->accept(); +} + + +#endif + +//============================================================================// +// QTriangulator::SimpleToMonotone // +//============================================================================// + +void QTriangulator::SimpleToMonotone::decompose() +{ + setupDataStructures(); + removeZeroLengthEdges(); + monotoneDecomposition(); + + m_parent->m_indices.clear(); + QBitArray processed(m_edges.size(), false); + for (int first = 0; first < m_edges.size(); ++first) { + if (processed.at(first)) + continue; + int i = first; + do { + Q_ASSERT(!processed.at(i)); + Q_ASSERT(m_edges.at(m_edges.at(i).next).previous == i); + m_parent->m_indices.push_back(m_edges.at(i).from); + processed.setBit(i); + i = m_edges.at(i).next; + } while (i != first); + if (m_parent->m_indices.size() > 0 && m_parent->m_indices.back() != Q_TRIANGULATE_END_OF_POLYGON) + m_parent->m_indices.push_back(Q_TRIANGULATE_END_OF_POLYGON); + } +} + +void QTriangulator::SimpleToMonotone::setupDataStructures() +{ + int i = 0; + Edge e; + e.node = 0; + e.twin = -1; + + while (i + 3 <= m_parent->m_indices.size()) { + int start = m_edges.size(); + + do { + e.from = m_parent->m_indices.at(i); + e.type = RegularVertex; + e.next = m_edges.size() + 1; + e.previous = m_edges.size() - 1; + m_edges.add(e); + ++i; + Q_ASSERT(i < m_parent->m_indices.size()); + } while (m_parent->m_indices.at(i) != Q_TRIANGULATE_END_OF_POLYGON); + + m_edges.last().next = start; + m_edges.at(start).previous = m_edges.size() - 1; + ++i; // Skip Q_TRIANGULATE_END_OF_POLYGON. + } + + for (i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).to = m_edges.at(m_edges.at(i).next).from; + m_edges.at(i).pointingUp = m_parent->m_vertices.at(m_edges.at(i).to) < m_parent->m_vertices.at(m_edges.at(i).from); + m_edges.at(i).helper = -1; // Not initialized here. + } +} + +void QTriangulator::SimpleToMonotone::removeZeroLengthEdges() +{ + for (int i = 0; i < m_edges.size(); ++i) { + if (m_parent->m_vertices.at(m_edges.at(i).from) == m_parent->m_vertices.at(m_edges.at(i).to)) { + m_edges.at(m_edges.at(i).previous).next = m_edges.at(i).next; + m_edges.at(m_edges.at(i).next).previous = m_edges.at(i).previous; + m_edges.at(m_edges.at(i).next).from = m_edges.at(i).from; + m_edges.at(i).next = -1; // Mark as removed. + } + } + + QDataBuffer<int> newMapping(m_edges.size()); + newMapping.resize(m_edges.size()); + int count = 0; + for (int i = 0; i < m_edges.size(); ++i) { + if (m_edges.at(i).next != -1) { + m_edges.at(count) = m_edges.at(i); + newMapping.at(i) = count; + ++count; + } + } + m_edges.resize(count); + for (int i = 0; i < m_edges.size(); ++i) { + m_edges.at(i).next = newMapping.at(m_edges.at(i).next); + m_edges.at(i).previous = newMapping.at(m_edges.at(i).previous); + } +} + +void QTriangulator::SimpleToMonotone::fillPriorityQueue() +{ + m_upperVertex.reset(); + m_upperVertex.reserve(m_edges.size()); + for (int i = 0; i < m_edges.size(); ++i) + m_upperVertex.add(i); + CompareVertices cmp(this); + //qSort(m_upperVertex.data(), m_upperVertex.data() + m_upperVertex.size(), cmp); + sort(m_upperVertex.data(), m_upperVertex.size(), cmp); + //for (int i = 1; i < m_upperVertex.size(); ++i) { + // Q_ASSERT(!cmp(m_upperVertex.at(i), m_upperVertex.at(i - 1))); + //} +} + +bool QTriangulator::SimpleToMonotone::edgeIsLeftOfEdge(int leftEdgeIndex, int rightEdgeIndex) const +{ + const Edge &leftEdge = m_edges.at(leftEdgeIndex); + const Edge &rightEdge = m_edges.at(rightEdgeIndex); + const QPodPoint &u = m_parent->m_vertices.at(rightEdge.upper()); + const QPodPoint &l = m_parent->m_vertices.at(rightEdge.lower()); + qint64 d = qPointDistanceFromLine(m_parent->m_vertices.at(leftEdge.upper()), l, u); + // d < 0: left, d > 0: right, d == 0: on top + if (d == 0) + d = qPointDistanceFromLine(m_parent->m_vertices.at(leftEdge.lower()), l, u); + return d < 0; +} + +// Returns the rightmost edge not to the right of the given edge. +QRBTree<int>::Node *QTriangulator::SimpleToMonotone::searchEdgeLeftOfEdge(int edgeIndex) const +{ + QRBTree<int>::Node *current = m_edgeList.root; + QRBTree<int>::Node *result = 0; + while (current) { + if (edgeIsLeftOfEdge(edgeIndex, current->data)) { + current = current->left; + } else { + result = current; + current = current->right; + } + } + return result; +} + +// Returns the rightmost edge left of the given point. +QRBTree<int>::Node *QTriangulator::SimpleToMonotone::searchEdgeLeftOfPoint(int pointIndex) const +{ + QRBTree<int>::Node *current = m_edgeList.root; + QRBTree<int>::Node *result = 0; + while (current) { + const QPodPoint &p1 = m_parent->m_vertices.at(m_edges.at(current->data).lower()); + const QPodPoint &p2 = m_parent->m_vertices.at(m_edges.at(current->data).upper()); + qint64 d = qPointDistanceFromLine(m_parent->m_vertices.at(pointIndex), p1, p2); + if (d <= 0) { + current = current->left; + } else { + result = current; + current = current->right; + } + } + return result; +} + +void QTriangulator::SimpleToMonotone::classifyVertex(int i) +{ + Edge &e2 = m_edges.at(i); + const Edge &e1 = m_edges.at(e2.previous); + + bool startOrSplit = (e1.pointingUp && !e2.pointingUp); + bool endOrMerge = (!e1.pointingUp && e2.pointingUp); + + const QPodPoint &p1 = m_parent->m_vertices.at(e1.from); + const QPodPoint &p2 = m_parent->m_vertices.at(e2.from); + const QPodPoint &p3 = m_parent->m_vertices.at(e2.to); + qint64 d = qPointDistanceFromLine(p1, p2, p3); + Q_ASSERT(d != 0 || (!startOrSplit && !endOrMerge)); + + e2.type = RegularVertex; + + if (m_clockwiseOrder) { + if (startOrSplit) + e2.type = (d < 0 ? SplitVertex : StartVertex); + else if (endOrMerge) + e2.type = (d < 0 ? MergeVertex : EndVertex); + } else { + if (startOrSplit) + e2.type = (d > 0 ? SplitVertex : StartVertex); + else if (endOrMerge) + e2.type = (d > 0 ? MergeVertex : EndVertex); + } +} + +void QTriangulator::SimpleToMonotone::classifyVertices() +{ + for (int i = 0; i < m_edges.size(); ++i) + classifyVertex(i); +} + +bool QTriangulator::SimpleToMonotone::pointIsInSector(const QPodPoint &p, const QPodPoint &v1, const QPodPoint &v2, const QPodPoint &v3) +{ + bool leftOfPreviousEdge = !qPointIsLeftOfLine(p, v2, v1); + bool leftOfNextEdge = !qPointIsLeftOfLine(p, v3, v2); + + if (qPointIsLeftOfLine(v1, v2, v3)) + return leftOfPreviousEdge && leftOfNextEdge; + else + return leftOfPreviousEdge || leftOfNextEdge; +} + +bool QTriangulator::SimpleToMonotone::pointIsInSector(int vertex, int sector) +{ + const QPodPoint ¢er = m_parent->m_vertices.at(m_edges.at(sector).from); + // Handle degenerate edges. + while (m_parent->m_vertices.at(m_edges.at(vertex).from) == center) + vertex = m_edges.at(vertex).next; + int next = m_edges.at(sector).next; + while (m_parent->m_vertices.at(m_edges.at(next).from) == center) + next = m_edges.at(next).next; + int previous = m_edges.at(sector).previous; + while (m_parent->m_vertices.at(m_edges.at(previous).from) == center) + previous = m_edges.at(previous).previous; + + const QPodPoint &p = m_parent->m_vertices.at(m_edges.at(vertex).from); + const QPodPoint &v1 = m_parent->m_vertices.at(m_edges.at(previous).from); + const QPodPoint &v3 = m_parent->m_vertices.at(m_edges.at(next).from); + if (m_clockwiseOrder) + return pointIsInSector(p, v3, center, v1); + else + return pointIsInSector(p, v1, center, v3); +} + +int QTriangulator::SimpleToMonotone::findSector(int edge, int vertex) +{ + while (!pointIsInSector(vertex, edge)) { + edge = m_edges.at(m_edges.at(edge).previous).twin; + Q_ASSERT(edge != -1); + } + return edge; +} + +void QTriangulator::SimpleToMonotone::createDiagonal(int lower, int upper) +{ + lower = findSector(lower, upper); + upper = findSector(upper, lower); + + int prevLower = m_edges.at(lower).previous; + int prevUpper = m_edges.at(upper).previous; + + Edge e; + + e.twin = m_edges.size() + 1; + e.next = upper; + e.previous = prevLower; + e.from = m_edges.at(lower).from; + e.to = m_edges.at(upper).from; + m_edges.at(upper).previous = m_edges.at(prevLower).next = int(m_edges.size()); + m_edges.add(e); + + e.twin = m_edges.size() - 1; + e.next = lower; + e.previous = prevUpper; + e.from = m_edges.at(upper).from; + e.to = m_edges.at(lower).from; + m_edges.at(lower).previous = m_edges.at(prevUpper).next = int(m_edges.size()); + m_edges.add(e); +} + +void QTriangulator::SimpleToMonotone::monotoneDecomposition() +{ + if (m_edges.isEmpty()) + return; + + Q_ASSERT(!m_edgeList.root); + QDataBuffer<QPair<int, int> > diagonals; + + int i = 0; + for (int index = 1; index < m_edges.size(); ++index) { + if (m_parent->m_vertices.at(m_edges.at(index).from) < m_parent->m_vertices.at(m_edges.at(i).from)) + i = index; + } + Q_ASSERT(i < m_edges.size()); + int j = m_edges.at(i).previous; + Q_ASSERT(j < m_edges.size()); + m_clockwiseOrder = qPointIsLeftOfLine(m_parent->m_vertices.at(m_edges.at(i).from), + m_parent->m_vertices.at(m_edges.at(j).from), m_parent->m_vertices.at(m_edges.at(i).to)); + + classifyVertices(); + fillPriorityQueue(); + + // debug: set helpers explicitly (shouldn't be necessary) + //for (int i = 0; i < m_edges.size(); ++i) + // m_edges.at(i).helper = m_edges.at(i).upper(); + + while (!m_upperVertex.isEmpty()) { + i = m_upperVertex.last(); + Q_ASSERT(i < m_edges.size()); + m_upperVertex.pop_back(); + j = m_edges.at(i).previous; + Q_ASSERT(j < m_edges.size()); + + QRBTree<int>::Node *leftEdgeNode = 0; + + switch (m_edges.at(i).type) { + case RegularVertex: + // If polygon interior is to the right of the vertex... + if (m_edges.at(i).pointingUp == m_clockwiseOrder) { + if (m_edges.at(i).node) { + Q_ASSERT(!m_edges.at(j).node); + if (m_edges.at(m_edges.at(i).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(i).helper)); + m_edges.at(j).node = m_edges.at(i).node; + m_edges.at(i).node = 0; + m_edges.at(j).node->data = j; + m_edges.at(j).helper = i; + } else if (m_edges.at(j).node) { + Q_ASSERT(!m_edges.at(i).node); + if (m_edges.at(m_edges.at(j).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(j).helper)); + m_edges.at(i).node = m_edges.at(j).node; + m_edges.at(j).node = 0; + m_edges.at(i).node->data = i; + m_edges.at(i).helper = i; + } else { + qWarning("Inconsistent polygon. (#1)"); + } + } else { + leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); + if (leftEdgeNode) { + if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper)); + m_edges.at(leftEdgeNode->data).helper = i; + } else { + qWarning("Inconsistent polygon. (#2)"); + } + } + break; + case SplitVertex: + leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); + if (leftEdgeNode) { + diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper)); + m_edges.at(leftEdgeNode->data).helper = i; + } else { + qWarning("Inconsistent polygon. (#3)"); + } + // Fall through. + case StartVertex: + if (m_clockwiseOrder) { + leftEdgeNode = searchEdgeLeftOfEdge(j); + QRBTree<int>::Node *node = m_edgeList.newNode(); + node->data = j; + m_edges.at(j).node = node; + m_edges.at(j).helper = i; + m_edgeList.attachAfter(leftEdgeNode, node); + Q_ASSERT(m_edgeList.verify()); + } else { + leftEdgeNode = searchEdgeLeftOfEdge(i); + QRBTree<int>::Node *node = m_edgeList.newNode(); + node->data = i; + m_edges.at(i).node = node; + m_edges.at(i).helper = i; + m_edgeList.attachAfter(leftEdgeNode, node); + Q_ASSERT(m_edgeList.verify()); + } + break; + case MergeVertex: + leftEdgeNode = searchEdgeLeftOfPoint(m_edges.at(i).from); + if (leftEdgeNode) { + if (m_edges.at(m_edges.at(leftEdgeNode->data).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(leftEdgeNode->data).helper)); + m_edges.at(leftEdgeNode->data).helper = i; + } else { + qWarning("Inconsistent polygon. (#4)"); + } + // Fall through. + case EndVertex: + if (m_clockwiseOrder) { + if (m_edges.at(m_edges.at(i).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(i).helper)); + if (m_edges.at(i).node) { + m_edgeList.deleteNode(m_edges.at(i).node); + Q_ASSERT(m_edgeList.verify()); + } else { + qWarning("Inconsistent polygon. (#5)"); + } + } else { + if (m_edges.at(m_edges.at(j).helper).type == MergeVertex) + diagonals.add(QPair<int, int>(i, m_edges.at(j).helper)); + if (m_edges.at(j).node) { + m_edgeList.deleteNode(m_edges.at(j).node); + Q_ASSERT(m_edgeList.verify()); + } else { + qWarning("Inconsistent polygon. (#6)"); + } + } + break; + } + } + + for (int i = 0; i < diagonals.size(); ++i) + createDiagonal(diagonals.at(i).first, diagonals.at(i).second); +} + +bool QTriangulator::SimpleToMonotone::CompareVertices::operator () (int i, int j) const +{ + if (m_parent->m_edges.at(i).from == m_parent->m_edges.at(j).from) + return m_parent->m_edges.at(i).type > m_parent->m_edges.at(j).type; + return m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(i).from) > + m_parent->m_parent->m_vertices.at(m_parent->m_edges.at(j).from); +} + +//============================================================================// +// QTriangulator::MonotoneToTriangles // +//============================================================================// + +void QTriangulator::MonotoneToTriangles::decompose() +{ + QVector<quint32> result; + QDataBuffer<int> stack; + m_first = 0; + // Require at least three more indices. + while (m_first + 3 <= m_parent->m_indices.size()) { + m_length = 0; + while (m_parent->m_indices.at(m_first + m_length) != Q_TRIANGULATE_END_OF_POLYGON) { + ++m_length; + Q_ASSERT(m_first + m_length < m_parent->m_indices.size()); + } + if (m_length < 3) { + m_first += m_length + 1; + continue; + } + + int minimum = 0; + while (less(next(minimum), minimum)) + minimum = next(minimum); + while (less(previous(minimum), minimum)) + minimum = previous(minimum); + + stack.reset(); + stack.add(minimum); + int left = previous(minimum); + int right = next(minimum); + bool stackIsOnLeftSide; + bool clockwiseOrder = leftOfEdge(minimum, left, right); + + if (less(left, right)) { + stack.add(left); + left = previous(left); + stackIsOnLeftSide = true; + } else { + stack.add(right); + right = next(right); + stackIsOnLeftSide = false; + } + + for (int count = 0; count + 2 < m_length; ++count) + { + Q_ASSERT(stack.size() >= 2); + if (less(left, right)) { + if (stackIsOnLeftSide == false) { + for (int i = 0; i + 1 < stack.size(); ++i) { + result.push_back(indices(stack.at(i + 1))); + result.push_back(indices(left)); + result.push_back(indices(stack.at(i))); + } + stack.first() = stack.last(); + stack.resize(1); + } else { + while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(left, stack.at(stack.size() - 2), stack.last()))) { + result.push_back(indices(stack.at(stack.size() - 2))); + result.push_back(indices(left)); + result.push_back(indices(stack.last())); + stack.pop_back(); + } + } + stack.add(left); + left = previous(left); + stackIsOnLeftSide = true; + } else { + if (stackIsOnLeftSide == true) { + for (int i = 0; i + 1 < stack.size(); ++i) { + result.push_back(indices(stack.at(i))); + result.push_back(indices(right)); + result.push_back(indices(stack.at(i + 1))); + } + stack.first() = stack.last(); + stack.resize(1); + } else { + while (stack.size() >= 2 && (clockwiseOrder ^ !leftOfEdge(right, stack.last(), stack.at(stack.size() - 2)))) { + result.push_back(indices(stack.last())); + result.push_back(indices(right)); + result.push_back(indices(stack.at(stack.size() - 2))); + stack.pop_back(); + } + } + stack.add(right); + right = next(right); + stackIsOnLeftSide = false; + } + } + + m_first += m_length + 1; + } + m_parent->m_indices = result; +} + +//============================================================================// +// qTriangulate // +//============================================================================// + +QTriangleSet qTriangulate(const qreal *polygon, int count, uint hint, const QTransform &matrix) +{ + QTriangulator triangulator; + triangulator.initialize(polygon, count, hint, matrix); + return triangulator.triangulate(); +} + +QTriangleSet qTriangulate(const QVectorPath &path, const QTransform &matrix, qreal lod) +{ + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + return triangulator.triangulate(); +} + +QTriangleSet qTriangulate(const QPainterPath &path, const QTransform &matrix, qreal lod) +{ + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + return triangulator.triangulate(); +} + +QPolylineSet qPolyline(const QVectorPath &path, const QTransform &matrix, qreal lod) +{ + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + return triangulator.polyline(); +} + +QPolylineSet qPolyline(const QPainterPath &path, const QTransform &matrix, qreal lod) +{ + QTriangulator triangulator; + triangulator.initialize(path, matrix, lod); + return triangulator.polyline(); +} + +QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qtriangulator_p.h b/src/opengl/gl2paintengineex/qtriangulator_p.h new file mode 100644 index 0000000..da47666 --- /dev/null +++ b/src/opengl/gl2paintengineex/qtriangulator_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtOpenGL module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTRIANGULATOR_P_H +#define QTRIANGULATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qvector.h> +#include <QtGui/private/qvectorpath_p.h> + +QT_BEGIN_NAMESPACE + +#define Q_TRIANGULATE_END_OF_POLYGON quint32(-1) + +struct QTriangleSet +{ + inline QTriangleSet() { } + inline QTriangleSet(const QTriangleSet &other) : vertices(other.vertices), indices(other.indices) { } + QTriangleSet &operator = (const QTriangleSet &other) {vertices = other.vertices; indices = other.indices; return *this;} + + // The vertices of a triangle are given by: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ... + QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...] + QVector<quint32> indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...] +}; + +struct QPolylineSet +{ + inline QPolylineSet() { } + inline QPolylineSet(const QPolylineSet &other) : vertices(other.vertices), indices(other.indices) { } + QPolylineSet &operator = (const QPolylineSet &other) {vertices = other.vertices; indices = other.indices; return *this;} + + QVector<qreal> vertices; // [x[0], y[0], x[1], y[1], x[2], ...] + QVector<quint32> indices; + +}; + +// The vertex coordinates of the returned triangle set will be rounded to a grid with a mesh size +// of 1/32. The polygon is first transformed, then scaled by 32, the coordinates are rounded to +// integers, the polygon is triangulated, and then scaled back by 1/32. +// 'hint' should be a combination of QVectorPath::Hints. +// 'lod' is the level of detail. Default is 1. Curves are split into more lines when 'lod' is higher. +QTriangleSet qTriangulate(const qreal *polygon, int count, uint hint = QVectorPath::PolygonHint | QVectorPath::OddEvenFill, const QTransform &matrix = QTransform()); +QTriangleSet qTriangulate(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); +QTriangleSet qTriangulate(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); +QPolylineSet qPolyline(const QVectorPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); +QPolylineSet qPolyline(const QPainterPath &path, const QTransform &matrix = QTransform(), qreal lod = 1); + +QT_END_NAMESPACE + +#endif diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index 6076891..69cfd30 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -55,6 +55,7 @@ SOURCES += qgl.cpp \ gl2paintengineex/qglengineshadersource_p.h \ gl2paintengineex/qglcustomshaderstage_p.h \ gl2paintengineex/qtriangulatingstroker_p.h \ + gl2paintengineex/qtriangulator_p.h \ gl2paintengineex/qtextureglyphcache_gl_p.h SOURCES += qglshaderprogram.cpp \ @@ -69,6 +70,7 @@ SOURCES += qgl.cpp \ gl2paintengineex/qpaintengineex_opengl2.cpp \ gl2paintengineex/qglcustomshaderstage.cpp \ gl2paintengineex/qtriangulatingstroker.cpp \ + gl2paintengineex/qtriangulator.cpp \ gl2paintengineex/qtextureglyphcache_gl.cpp } diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 3f32cf3..b7139a2 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -222,6 +222,9 @@ bool qt_gl_preferGL2Engine() \value DirectRendering Specifies that the context is used for direct rendering to a display. \value HasOverlay Enables the use of an overlay. \value SampleBuffers Enables the use of sample buffers. + \value DeprecatedFunctions Enables the use of deprecated functionality for OpenGL 3.x + contexts. A context with deprecated functionality enabled is + called a full context in the OpenGL specification. \value SingleBuffer Specifies the use of a single buffer, as opposed to double buffers. \value NoDepthBuffer Disables the use of a depth buffer. \value ColorIndex Specifies that the context should use a color index as its pixel format. @@ -232,6 +235,9 @@ bool qt_gl_preferGL2Engine() \value IndirectRendering Specifies that the context is used for indirect rendering to a buffer. \value NoOverlay Disables the use of an overlay. \value NoSampleBuffers Disables the use of sample buffers. + \value NoDeprecatedFunctions Disables the use of deprecated functionality for OpenGL 3.x + contexts. A context with deprecated functionality disabled is + called a forward compatible context in the OpenGL specification. \sa {Sample Buffers Example} */ @@ -1077,6 +1083,88 @@ int QGLFormat::stencilBufferSize() const } /*! + \since 4.7 + + Set the OpenGL version. If a context compatible with the requested OpenGL version + cannot be created, a context compatible with version 1.x is created instead. + + \sa majorVersion(), minorVersion() +*/ +void QGLFormat::setVersion(int major, int minor) +{ + if (major < 1 || minor < 0) { + qWarning("QGLFormat::setVersion: Cannot set zero or negative version number %d.%d", major, minor); + return; + } + detach(); + d->majorVersion = major; + d->minorVersion = minor; +} + +/*! + \since 4.7 + + Returns the OpenGL major version. + + \sa setVersion(), minorVersion() +*/ +int QGLFormat::majorVersion() const +{ + return d->majorVersion; +} + +/*! + \since 4.7 + + Returns the OpenGL minor version. + + \sa setVersion(), majorVersion() +*/ +int QGLFormat::minorVersion() const +{ + return d->minorVersion; +} + +/*! + \enum QGLFormat::OpenGLContextProfile + \since 4.7 + + This enum describes the OpenGL context profiles that can be specified for contexts implementing + OpenGL version 3.2 or higher. These profiles are different from OpenGL ES profiles. + + \value NoProfile OpenGL version is lower than 3.2. + \value CoreProfile Functionality deprecated in OpenGL version 3.0 is not available. + \value CompatibilityProfile Functionality from earlier OpenGL versions is available. +*/ + +/*! + \since 4.7 + + Set the OpenGL context profile. The profile is ignored if the requested OpenGL + version is less than 3.2. + + \sa profile() +*/ +void QGLFormat::setProfile(OpenGLContextProfile profile) +{ + detach(); + d->profile = profile; +} + +/*! + \since 4.7 + + Returns the OpenGL context profile. + + \sa setProfile() +*/ +QGLFormat::OpenGLContextProfile QGLFormat::profile() const +{ + return d->profile; +} + + +/*! \fn bool QGLFormat::hasOpenGL() Returns true if the window system has any OpenGL support; @@ -1112,25 +1200,21 @@ QGLFormat::OpenGLVersionFlags Q_AUTOTEST_EXPORT qOpenGLVersionFlagsFromString(co if (parts[2].startsWith(QLatin1String("1.1"))) versionFlags |= QGLFormat::OpenGL_ES_Common_Version_1_1 | QGLFormat::OpenGL_ES_CommonLite_Version_1_1; - } - else { + } else { // Not -CM, must be CL, CommonLite versionFlags |= QGLFormat::OpenGL_ES_CommonLite_Version_1_0; if (parts[2].startsWith(QLatin1String("1.1"))) versionFlags |= QGLFormat::OpenGL_ES_CommonLite_Version_1_1; } - } - else { + } else { // OpenGL ES version 2.0 or higher versionFlags |= QGLFormat::OpenGL_ES_Version_2_0; } - } - else { + } else { // if < 3 parts to the name, it is an unrecognised OpenGL ES qWarning("Unrecognised OpenGL ES version"); } - } - else { + } else { // not ES, regular OpenGL, the version numbers are first in the string if (versionString.startsWith(QLatin1String("1."))) { switch (versionString[2].toAscii()) { @@ -1147,30 +1231,35 @@ QGLFormat::OpenGLVersionFlags Q_AUTOTEST_EXPORT qOpenGLVersionFlagsFromString(co default: break; } - } - else if (versionString.startsWith(QLatin1String("2."))) { + } else if (versionString.startsWith(QLatin1String("2."))) { versionFlags |= QGLFormat::OpenGL_Version_1_1 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_2_0; - QString minorVersion = versionString.section(QLatin1Char(' '), 0, 0).section(QLatin1Char('.'), 1, 1); - if (minorVersion == QChar(QLatin1Char('1'))) + if (versionString[2].toAscii() == '1') versionFlags |= QGLFormat::OpenGL_Version_2_1; - } - else if (versionString.startsWith(QLatin1String("3."))) { - versionFlags |= QGLFormat::OpenGL_Version_1_1 | - QGLFormat::OpenGL_Version_1_2 | - QGLFormat::OpenGL_Version_1_3 | - QGLFormat::OpenGL_Version_1_4 | - QGLFormat::OpenGL_Version_1_5 | - QGLFormat::OpenGL_Version_2_0 | - QGLFormat::OpenGL_Version_2_1 | - QGLFormat::OpenGL_Version_3_0; - } - else + } else if (versionString.startsWith(QLatin1String("3."))) { + versionFlags |= QGLFormat::OpenGL_Version_1_1 | + QGLFormat::OpenGL_Version_1_2 | + QGLFormat::OpenGL_Version_1_3 | + QGLFormat::OpenGL_Version_1_4 | + QGLFormat::OpenGL_Version_1_5 | + QGLFormat::OpenGL_Version_2_0 | + QGLFormat::OpenGL_Version_2_1 | + QGLFormat::OpenGL_Version_3_0; + switch (versionString[2].toAscii()) { + case '2': + versionFlags |= QGLFormat::OpenGL_Version_3_2; + case '1': + versionFlags |= QGLFormat::OpenGL_Version_3_1; + default: + break; + } + } else { qWarning("Unrecognised OpenGL version"); + } } return versionFlags; } @@ -1202,6 +1291,12 @@ QGLFormat::OpenGLVersionFlags Q_AUTOTEST_EXPORT qOpenGLVersionFlagsFromString(co \value OpenGL_Version_3_0 OpenGL version 3.0 or higher is present. + \value OpenGL_Version_3_1 OpenGL version 3.1 or higher is present. + Note that OpenGL version 3.1 or higher does not necessarily support all the features of + version 3.0 and lower. + + \value OpenGL_Version_3_2 OpenGL version 3.2 or higher is present. + \value OpenGL_ES_CommonLite_Version_1_0 OpenGL ES version 1.0 Common Lite or higher is present. \value OpenGL_ES_Common_Version_1_0 OpenGL ES version 1.0 Common or higher is present. @@ -1373,14 +1468,20 @@ void QGLFormat::setDefaultOverlayFormat(const QGLFormat &f) bool operator==(const QGLFormat& a, const QGLFormat& b) { - return (int) a.d->opts == (int) b.d->opts && a.d->pln == b.d->pln && a.d->alphaSize == b.d->alphaSize - && a.d->accumSize == b.d->accumSize && a.d->stencilSize == b.d->stencilSize + return (a.d == b.d) || ((int) a.d->opts == (int) b.d->opts + && a.d->pln == b.d->pln + && a.d->alphaSize == b.d->alphaSize + && a.d->accumSize == b.d->accumSize + && a.d->stencilSize == b.d->stencilSize && a.d->depthSize == b.d->depthSize && a.d->redSize == b.d->redSize && a.d->greenSize == b.d->greenSize && a.d->blueSize == b.d->blueSize && a.d->numSamples == b.d->numSamples - && a.d->swapInterval == b.d->swapInterval; + && a.d->swapInterval == b.d->swapInterval + && a.d->majorVersion == b.d->majorVersion + && a.d->minorVersion == b.d->minorVersion + && a.d->profile == b.d->profile); } diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h index 1a04ff9..8ada8ce 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -144,6 +144,7 @@ namespace QGL DirectRendering = 0x0080, HasOverlay = 0x0100, SampleBuffers = 0x0200, + DeprecatedFunctions = 0x0400, SingleBuffer = DoubleBuffer << 16, NoDepthBuffer = DepthBuffer << 16, ColorIndex = Rgba << 16, @@ -153,7 +154,8 @@ namespace QGL NoStereoBuffers = StereoBuffers << 16, IndirectRendering = DirectRendering << 16, NoOverlay = HasOverlay << 16, - NoSampleBuffers = SampleBuffers << 16 + NoSampleBuffers = SampleBuffers << 16, + NoDeprecatedFunctions = DeprecatedFunctions << 16 }; Q_DECLARE_FLAGS(FormatOptions, FormatOption) } @@ -235,7 +237,20 @@ public: static bool hasOpenGL(); static bool hasOpenGLOverlays(); - enum OpenGLVersionFlag { + void setVersion(int major, int minor); + int majorVersion() const; + int minorVersion() const; + + enum OpenGLContextProfile { + NoProfile, + CoreProfile, + CompatibilityProfile + }; + + void setProfile(OpenGLContextProfile profile); + OpenGLContextProfile profile() const; + + enum OpenGLVersionFlag { OpenGL_Version_None = 0x00000000, OpenGL_Version_1_1 = 0x00000001, OpenGL_Version_1_2 = 0x00000002, @@ -249,7 +264,9 @@ public: OpenGL_ES_Common_Version_1_1 = 0x00000200, OpenGL_ES_CommonLite_Version_1_1 = 0x00000400, OpenGL_ES_Version_2_0 = 0x00000800, - OpenGL_Version_3_0 = 0x00001000 + OpenGL_Version_3_0 = 0x00001000, + OpenGL_Version_3_1 = 0x00002000, + OpenGL_Version_3_2 = 0x00004000 }; Q_DECLARE_FLAGS(OpenGLVersionFlags, OpenGLVersionFlag) diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 0104f07..9e8e335 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -132,11 +132,15 @@ public: QGLFormatPrivate() : ref(1) { - opts = QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba | QGL::DirectRendering | QGL::StencilBuffer; + opts = QGL::DoubleBuffer | QGL::DepthBuffer | QGL::Rgba | QGL::DirectRendering + | QGL::StencilBuffer | QGL::DeprecatedFunctions; pln = 0; depthSize = accumSize = stencilSize = redSize = greenSize = blueSize = alphaSize = -1; numSamples = -1; swapInterval = -1; + majorVersion = 1; + minorVersion = 0; + profile = QGLFormat::NoProfile; } QGLFormatPrivate(const QGLFormatPrivate *other) : ref(1), @@ -150,7 +154,10 @@ public: blueSize(other->blueSize), alphaSize(other->alphaSize), numSamples(other->numSamples), - swapInterval(other->swapInterval) + swapInterval(other->swapInterval), + majorVersion(other->majorVersion), + minorVersion(other->minorVersion), + profile(other->profile) { } QAtomicInt ref; @@ -165,6 +172,9 @@ public: int alphaSize; int numSamples; int swapInterval; + int majorVersion; + int minorVersion; + QGLFormat::OpenGLContextProfile profile; }; class QGLWidgetPrivate : public QWidgetPrivate @@ -344,6 +354,10 @@ public: void syncGlState(); // Makes sure the GL context's state is what we think it is #if defined(Q_WS_WIN) + void updateFormatVersion(); +#endif + +#if defined(Q_WS_WIN) HGLRC rc; HDC dc; WId win; diff --git a/src/opengl/qgl_win.cpp b/src/opengl/qgl_win.cpp index ed4814f..b5f952e 100644 --- a/src/opengl/qgl_win.cpp +++ b/src/opengl/qgl_win.cpp @@ -122,6 +122,30 @@ typedef bool (APIENTRY *PFNWGLCHOOSEPIXELFORMATARB)(HDC hdc, #define WGL_TYPE_COLORINDEX_ARB 0x202C #endif +#ifndef WGL_ARB_create_context +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x0001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x0002 +// Error codes returned by GetLastError(). +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#endif + +#ifndef GL_VERSION_3_2 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001 +#endif + QT_BEGIN_NAMESPACE class QGLCmapPrivate @@ -682,8 +706,118 @@ QGLTemporaryContext::~QGLTemporaryContext() wglMakeCurrent(d->old_dc, d->old_context); } +static bool qgl_create_context(HDC hdc, QGLContextPrivate *d, QGLContextPrivate *shareContext) +{ + d->rc = 0; + + typedef HGLRC (APIENTRYP PFNWGLCREATECONTEXTATTRIBSARB)(HDC, HGLRC, const int *); + PFNWGLCREATECONTEXTATTRIBSARB wglCreateContextAttribsARB = + (PFNWGLCREATECONTEXTATTRIBSARB) wglGetProcAddress("wglCreateContextAttribsARB"); + if (wglCreateContextAttribsARB) { + int attributes[11]; + int attribIndex = 0; + const int major = d->reqFormat.majorVersion(); + const int minor = d->reqFormat.minorVersion(); + attributes[attribIndex++] = WGL_CONTEXT_MAJOR_VERSION_ARB; + attributes[attribIndex++] = major; + attributes[attribIndex++] = WGL_CONTEXT_MINOR_VERSION_ARB; + attributes[attribIndex++] = minor; + + if (major >= 3 && !d->reqFormat.testOption(QGL::DeprecatedFunctions)) { + attributes[attribIndex++] = WGL_CONTEXT_FLAGS_ARB; + attributes[attribIndex++] = WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + } + + if ((major == 3 && minor >= 2) || major > 3) { + switch (d->reqFormat.profile()) { + case QGLFormat::NoProfile: + break; + case QGLFormat::CoreProfile: + attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB; + attributes[attribIndex++] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + break; + case QGLFormat::CompatibilityProfile: + attributes[attribIndex++] = WGL_CONTEXT_PROFILE_MASK_ARB; + attributes[attribIndex++] = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + break; + default: + qWarning("QGLContext::chooseContext(): Context profile not supported."); + return false; + } + } + + if (d->reqFormat.plane() != 0) { + attributes[attribIndex++] = WGL_CONTEXT_LAYER_PLANE_ARB; + attributes[attribIndex++] = d->reqFormat.plane(); + } + + attributes[attribIndex++] = 0; // Terminate list. + d->rc = wglCreateContextAttribsARB(hdc, shareContext && shareContext->valid + ? shareContext->rc : 0, attributes); + if (d->rc) { + if (shareContext) + shareContext->sharing = d->sharing = true; + return true; + } + } + + d->rc = wglCreateLayerContext(hdc, d->reqFormat.plane()); + if (d->rc && shareContext && shareContext->valid) + shareContext->sharing = d->sharing = wglShareLists(shareContext->rc, d->rc); + return d->rc != 0; +} + +void QGLContextPrivate::updateFormatVersion() +{ + const GLubyte *s = glGetString(GL_VERSION); + + if (!(s && s[0] >= '0' && s[0] <= '9' && s[1] == '.' && s[2] >= '0' && s[2] <= '9')) { + if (!s) + qWarning("QGLContext::chooseContext(): OpenGL version string is null."); + else + qWarning("QGLContext::chooseContext(): Unexpected OpenGL version string format."); + glFormat.setVersion(0, 0); + glFormat.setProfile(QGLFormat::NoProfile); + glFormat.setOption(QGL::DeprecatedFunctions); + return; + } + + int major = s[0] - '0'; + int minor = s[2] - '0'; + glFormat.setVersion(major, minor); + + if (major < 3) { + glFormat.setProfile(QGLFormat::NoProfile); + glFormat.setOption(QGL::DeprecatedFunctions); + } else { + GLint value = 0; + if (major > 3 || minor >= 2) + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value); + + switch (value) { + case WGL_CONTEXT_CORE_PROFILE_BIT_ARB: + glFormat.setProfile(QGLFormat::CoreProfile); + break; + case WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB: + glFormat.setProfile(QGLFormat::CompatibilityProfile); + break; + default: + glFormat.setProfile(QGLFormat::NoProfile); + break; + } + + glGetIntegerv(GL_CONTEXT_FLAGS, &value); + if (value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) + glFormat.setOption(QGL::NoDeprecatedFunctions); + else + glFormat.setOption(QGL::DeprecatedFunctions); + } +} + bool QGLContext::chooseContext(const QGLContext* shareContext) { + QGLContextPrivate *share = shareContext ? const_cast<QGLContext *>(shareContext)->d_func() : 0; + Q_D(QGLContext); // workaround for matrox driver: // make a cheap call to opengl to force loading of DLL @@ -741,8 +875,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) goto end; } - d->rc = wglCreateLayerContext(myDc, d->glFormat.plane()); - if (!d->rc) { + if (!qgl_create_context(myDc, d, share)) { qwglError("QGLContext::chooseContext()", "CreateLayerContext"); result = false; goto end; @@ -792,16 +925,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) d->cmap = new QGLCmap(1 << lpfd.cColorBits); d->cmap->setEntry(lpfd.crTransparent, qRgb(1, 2, 3));//, QGLCmap::Reserved); } - - if (shareContext && shareContext->isValid()) { - QGLContext *share = const_cast<QGLContext *>(shareContext); - d->sharing = (wglShareLists(shareContext->d_func()->rc, d->rc) != 0); - share->d_func()->sharing = d->sharing; - } - - goto end; - } - { + } else { PIXELFORMATDESCRIPTOR pfd; PIXELFORMATDESCRIPTOR realPfd; d->pixelFormatId = choosePixelFormat(&pfd, myDc); @@ -840,17 +964,12 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) goto end; } - if (!(d->rc = wglCreateLayerContext(myDc, 0))) { + if (!qgl_create_context(myDc, d, share)) { qwglError("QGLContext::chooseContext()", "wglCreateContext"); result = false; goto end; } - if (shareContext && shareContext->isValid()) { - d->sharing = (wglShareLists(shareContext->d_func()->rc, d->rc) != 0); - const_cast<QGLContext *>(shareContext)->d_func()->sharing = d->sharing; - } - if(!deviceIsPixmap()) { QRgb* pal = qgl_create_rgb_palette(&realPfd); if (pal) { @@ -865,6 +984,9 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) end: // vblanking wglMakeCurrent(myDc, d->rc); + if (d->rc) + d->updateFormatVersion(); + typedef BOOL (APIENTRYP PFNWGLSWAPINTERVALEXT) (int interval); typedef int (APIENTRYP PFNWGLGETSWAPINTERVALEXT) (void); PFNWGLSWAPINTERVALEXT wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXT) wglGetProcAddress("wglSwapIntervalEXT"); diff --git a/src/opengl/qglextensions_p.h b/src/opengl/qglextensions_p.h index b0cb429..86096d2 100644 --- a/src/opengl/qglextensions_p.h +++ b/src/opengl/qglextensions_p.h @@ -599,6 +599,20 @@ struct QGLExtensionFuncs #define GL_DECR_WRAP 0x8508 #endif +#ifndef GL_VERSION_1_5 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#endif + #ifndef GL_VERSION_2_0 #define GL_FRAGMENT_SHADER 0x8B30 #define GL_VERTEX_SHADER 0x8B31 diff --git a/src/sql/drivers/odbc/qsql_odbc.cpp b/src/sql/drivers/odbc/qsql_odbc.cpp index 2049a76..18bc743 100644 --- a/src/sql/drivers/odbc/qsql_odbc.cpp +++ b/src/sql/drivers/odbc/qsql_odbc.cpp @@ -62,13 +62,6 @@ QT_BEGIN_NAMESPACE // undefine this to prevent initial check of the ODBC driver #define ODBC_CHECK_DRIVER -#if defined(Q_ODBC_VERSION_2) -//crude hack to get non-unicode capable driver managers to work -# undef UNICODE -# define SQLTCHAR SQLCHAR -# define SQL_C_WCHAR SQL_C_CHAR -#endif - // newer platform SDKs use SQLLEN instead of SQLINTEGER #if defined(WIN32) && (_MSC_VER < 1300) # define QSQLLEN SQLINTEGER @@ -298,13 +291,11 @@ static QVariant::Type qDecodeODBCType(SQLSMALLINT sqltype, const T* p, bool isSi case SQL_TYPE_TIMESTAMP: type = QVariant::DateTime; break; -#ifndef Q_ODBC_VERSION_2 case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: type = QVariant::String; break; -#endif case SQL_CHAR: case SQL_VARCHAR: case SQL_GUID: @@ -577,10 +568,8 @@ static QSqlField qMakeFieldInfo(const QODBCPrivate* p, int i ) static int qGetODBCVersion(const QString &connOpts) { -#ifndef Q_ODBC_VERSION_2 if (connOpts.contains(QLatin1String("SQL_ATTR_ODBC_VERSION=SQL_OV_ODBC3"), Qt::CaseInsensitive)) return SQL_OV_ODBC3; -#endif return SQL_OV_ODBC2; } @@ -679,7 +668,6 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts) continue; } r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACE, (SQLPOINTER) v, 0); -#ifndef Q_ODBC_VERSION_2 } else if (opt.toUpper() == QLatin1String("SQL_ATTR_CONNECTION_POOLING")) { if (val == QLatin1String("SQL_CP_OFF")) v = SQL_CP_OFF; @@ -706,7 +694,6 @@ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts) continue; } r = SQLSetConnectAttr(hDbc, SQL_ATTR_CP_MATCH, (SQLPOINTER)v, 0); -#endif } else if (opt.toUpper() == QLatin1String("SQL_ATTR_ODBC_VERSION")) { // Already handled in QODBCDriver::open() continue; @@ -1432,7 +1419,6 @@ bool QODBCResult::exec() *ind == SQL_NULL_DATA ? ind : NULL); break; case QVariant::String: -#ifndef Q_ODBC_VERSION_2 if (d->unicode) { QString str = val.toString(); str.utf16(); @@ -1469,7 +1455,6 @@ bool QODBCResult::exec() break; } else -#endif { QByteArray str = val.toString().toAscii(); if (*ind != SQL_NULL_DATA) @@ -1870,11 +1855,6 @@ void QODBCDriver::cleanup() // as two byte unicode characters void QODBCDriverPrivate::checkUnicode() { -#if defined(Q_ODBC_VERSION_2) - unicode = false; - return; -#endif - SQLRETURN r; SQLUINTEGER fFunc; diff --git a/src/sql/drivers/odbc/qsql_odbc.h b/src/sql/drivers/odbc/qsql_odbc.h index 145a902..836dbd6 100644 --- a/src/sql/drivers/odbc/qsql_odbc.h +++ b/src/sql/drivers/odbc/qsql_odbc.h @@ -49,13 +49,6 @@ #include <QtCore/qt_windows.h> #endif -#if defined (Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_3) -// assume we use iodbc on MACX -// comment next line out if you use a -// unicode compatible manager -# define Q_ODBC_VERSION_2 -#endif - #ifdef QT_PLUGIN #define Q_EXPORT_SQLDRIVER_ODBC #else @@ -75,10 +68,7 @@ # undef _MSC_VER #endif -#ifndef Q_ODBC_VERSION_2 #include <sqlucode.h> -#endif - #include <sqlext.h> QT_BEGIN_HEADER diff --git a/src/testlib/qbenchmark.cpp b/src/testlib/qbenchmark.cpp index 4ccbee6..95e2658 100644 --- a/src/testlib/qbenchmark.cpp +++ b/src/testlib/qbenchmark.cpp @@ -41,6 +41,7 @@ #include "QtTest/qbenchmark.h" #include "QtTest/private/qbenchmark_p.h" +#include "QtTest/private/qbenchmarkmetric_p.h" #ifdef QT_GUI_LIB #include <QtGui/qapplication.h> @@ -138,7 +139,7 @@ void QBenchmarkTestMethodData::endDataRun() int QBenchmarkTestMethodData::adjustIterationCount(int suggestion) { - // Let the -iteration-count option override the measurer. + // Let the -iterations option override the measurer. if (QBenchmarkGlobalData::current->iterationCount != -1) { iterationCount = QBenchmarkGlobalData::current->iterationCount; } else { @@ -148,12 +149,13 @@ int QBenchmarkTestMethodData::adjustIterationCount(int suggestion) return iterationCount; } -void QBenchmarkTestMethodData::setResult(qint64 value) +void QBenchmarkTestMethodData::setResult( + qreal value, QTest::QBenchmarkMetric metric, bool setByMacro) { bool accepted = false; // Always accept the result if the iteration count has been - // specified on the command line with -iteartion-count. + // specified on the command line with -iterations. if (QBenchmarkGlobalData::current->iterationCount != -1) accepted = true; @@ -161,7 +163,7 @@ void QBenchmarkTestMethodData::setResult(qint64 value) iterationCount = 1; accepted = true; } - + // Test the result directly without calling the measurer if the minimum time // has been specifed on the command line with -minimumvalue. else if (QBenchmarkGlobalData::current->walltimeMinimum != -1) @@ -175,8 +177,8 @@ void QBenchmarkTestMethodData::setResult(qint64 value) else iterationCount *= 2; - this->result = - QBenchmarkResult(QBenchmarkGlobalData::current->context, value, iterationCount); + this->result = QBenchmarkResult( + QBenchmarkGlobalData::current->context, value, iterationCount, metric, setByMacro); } /*! @@ -208,7 +210,8 @@ QTest::QBenchmarkIterationController::QBenchmarkIterationController() */ QTest::QBenchmarkIterationController::~QBenchmarkIterationController() { - QBenchmarkTestMethodData::current->setResult(QTest::endBenchmarkMeasurement()); + const qreal result = QTest::endBenchmarkMeasurement(); + QBenchmarkTestMethodData::current->setResult(result, QBenchmarkGlobalData::current->measurer->metricType()); } /*! \internal @@ -259,28 +262,30 @@ void QTest::beginBenchmarkMeasurement() /*! \internal */ -qint64 QTest::endBenchmarkMeasurement() +quint64 QTest::endBenchmarkMeasurement() { // the clock is ticking before the line below, don't add code here. return QBenchmarkGlobalData::current->measurer->stop(); } -/*! \internal -*/ -void QTest::setResult(qint64 result) -{ - QBenchmarkTestMethodData::current->setResult(result); -} - -/*! \internal +/*! + Sets the benchmark result for this test function to \a result. + + Use this function if you want to report benchmark results without + using the QBENCHMARK macro. Use \a metric to specify how QTestLib + should interpret the results. + + The context for the result will be the test function name and any + data tag from the _data function. This function can only be called + once in each test function, subsequent calls will replace the + earlier reported results. + + Note that the -iterations command line argument has no effect + on test functions without the QBENCHMARK macro. */ -void QTest::setResult(const QString &tag, qint64 result) +void QTest::setBenchmarkResult(qreal result, QTest::QBenchmarkMetric metric) { - QBenchmarkContext context = QBenchmarkGlobalData::current->context; - context.tag = tag; - QBenchmarkTestMethodData::current->result = - QBenchmarkResult( context, result, - QBenchmarkTestMethodData::current->iterationCount); + QBenchmarkTestMethodData::current->setResult(result, metric, false); } template <typename T> @@ -298,6 +303,4 @@ Q_TYPENAME T::value_type qAverage(const T &container) return acc / count; } - QT_END_NAMESPACE - diff --git a/src/testlib/qbenchmark.h b/src/testlib/qbenchmark.h index 0207b5b..38a0e52 100644 --- a/src/testlib/qbenchmark.h +++ b/src/testlib/qbenchmark.h @@ -43,6 +43,7 @@ #define QBENCHMARK_H #include <QtTest/qtest_global.h> +#include <QtTest/private/qbenchmarkmetric_p.h> QT_BEGIN_HEADER @@ -75,6 +76,8 @@ public: } +// --- BEGIN public API --- + #define QBENCHMARK \ for (QTest::QBenchmarkIterationController __iteration_controller; \ __iteration_controller.isDone() == false; __iteration_controller.next()) @@ -83,6 +86,13 @@ public: for (QTest::QBenchmarkIterationController __iteration_controller(QTest::QBenchmarkIterationController::RunOnce); \ __iteration_controller.isDone() == false; __iteration_controller.next()) +namespace QTest +{ + void Q_TESTLIB_EXPORT setBenchmarkResult(qreal result, QBenchmarkMetric metric); +} + +// --- END public API --- + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/testlib/qbenchmark_p.h b/src/testlib/qbenchmark_p.h index 0d6542f..50329e1 100644 --- a/src/testlib/qbenchmark_p.h +++ b/src/testlib/qbenchmark_p.h @@ -42,6 +42,8 @@ #ifndef QBENCHMARK_P_H #define QBENCHMARK_P_H +#include <stdlib.h> + // // W A R N I N G // ------------- @@ -68,6 +70,7 @@ #include "QtTest/private/qbenchmarkvalgrind_p.h" #endif #include "QtTest/private/qbenchmarkevent_p.h" +#include "QtTest/private/qbenchmarkmetric_p.h" QT_BEGIN_NAMESPACE @@ -92,23 +95,29 @@ class QBenchmarkResult { public: QBenchmarkContext context; - qint64 value; + qreal value; int iterations; + QTest::QBenchmarkMetric metric; + bool setByMacro; bool valid; QBenchmarkResult() : value(-1) , iterations(-1) + , setByMacro(true) , valid(false) - { } + { } - QBenchmarkResult(const QBenchmarkContext &context, const qint64 value, const int iterations) + QBenchmarkResult( + const QBenchmarkContext &context, const qreal value, const int iterations, + QTest::QBenchmarkMetric metric, bool setByMacro) : context(context) , value(value) , iterations(iterations) + , metric(metric) + , setByMacro(setByMacro) , valid(true) - { - } + { } bool operator<(const QBenchmarkResult &other) const { @@ -167,7 +176,7 @@ public: bool isBenchmark() const { return result.valid; } bool resultsAccepted() const { return resultAccepted; } int adjustIterationCount(int suggestion); - void setResult(qint64 value); + void setResult(qreal value, QTest::QBenchmarkMetric metric, bool setByMacro = true); QBenchmarkResult result; bool resultAccepted; @@ -183,10 +192,7 @@ namespace QTest void setIterationCount(int count); Q_TESTLIB_EXPORT void beginBenchmarkMeasurement(); - Q_TESTLIB_EXPORT qint64 endBenchmarkMeasurement(); - - void setResult(qint64 result); - void setResult(const QString &tag, qint64 result); + Q_TESTLIB_EXPORT quint64 endBenchmarkMeasurement(); } QT_END_NAMESPACE diff --git a/src/testlib/qbenchmarkevent.cpp b/src/testlib/qbenchmarkevent.cpp index 9c67096..39eb1c5 100644 --- a/src/testlib/qbenchmarkevent.cpp +++ b/src/testlib/qbenchmarkevent.cpp @@ -41,6 +41,7 @@ #include "QtTest/private/qbenchmarkevent_p.h" #include "QtTest/private/qbenchmark_p.h" +#include "QtTest/private/qbenchmarkmetric_p.h" #include <qdebug.h> QT_BEGIN_NAMESPACE @@ -91,14 +92,9 @@ int QBenchmarkEvent::adjustMedianCount(int suggestion) return 1; } -QString QBenchmarkEvent::unitText() +QTest::QBenchmarkMetric QBenchmarkEvent::metricType() { - return QLatin1String("events"); -} - -QString QBenchmarkEvent::metricText() -{ - return QLatin1String("events"); + return QTest::Events; } // This could be done in a much better way, this is just the beginning. diff --git a/src/testlib/qbenchmarkevent_p.h b/src/testlib/qbenchmarkevent_p.h index 1a48964..5e13b9f 100644 --- a/src/testlib/qbenchmarkevent_p.h +++ b/src/testlib/qbenchmarkevent_p.h @@ -70,8 +70,7 @@ public: int adjustIterationCount(int suggestion); int adjustMedianCount(int suggestion); bool repeatCount() { return 1; } - QString unitText(); - QString metricText(); + QTest::QBenchmarkMetric metricType(); static bool eventCountingMechanism(void *message); static qint64 eventCounter; }; diff --git a/src/testlib/qbenchmarkmeasurement.cpp b/src/testlib/qbenchmarkmeasurement.cpp index 836d85f..c03cbff 100644 --- a/src/testlib/qbenchmarkmeasurement.cpp +++ b/src/testlib/qbenchmarkmeasurement.cpp @@ -41,6 +41,8 @@ #include "QtTest/private/qbenchmarkmeasurement_p.h" #include "QtTest/private/qbenchmark_p.h" +#include "QtTest/private/qbenchmarkmetric_p.h" +#include "qbenchmark.h" #include <qdebug.h> QT_BEGIN_NAMESPACE @@ -77,14 +79,9 @@ int QBenchmarkTimeMeasurer::adjustMedianCount(int) return 1; } -QString QBenchmarkTimeMeasurer::unitText() +QTest::QBenchmarkMetric QBenchmarkTimeMeasurer::metricType() { - return QLatin1String("msec"); -} - -QString QBenchmarkTimeMeasurer::metricText() -{ - return QLatin1String("walltime"); + return QTest::WalltimeMilliseconds; } #ifdef HAVE_TICK_COUNTER // defined in 3rdparty/cycle_p.h @@ -126,14 +123,9 @@ bool QBenchmarkTickMeasurer::needsWarmupIteration() return true; } -QString QBenchmarkTickMeasurer::unitText() -{ - return QLatin1String("ticks"); -} - -QString QBenchmarkTickMeasurer::metricText() +QTest::QBenchmarkMetric QBenchmarkTickMeasurer::metricType() { - return QLatin1String("cputicks"); + return QTest::CPUTicks; } #endif diff --git a/src/testlib/qbenchmarkmeasurement_p.h b/src/testlib/qbenchmarkmeasurement_p.h index 6f3c034..8ad3613 100644 --- a/src/testlib/qbenchmarkmeasurement_p.h +++ b/src/testlib/qbenchmarkmeasurement_p.h @@ -55,6 +55,7 @@ #include <QtCore/qdatetime.h> #include "3rdparty/cycle_p.h" +#include "qbenchmark.h" QT_BEGIN_NAMESPACE @@ -71,8 +72,7 @@ public: virtual int adjustMedianCount(int suggestion) = 0; virtual bool repeatCount() { return 1; } virtual bool needsWarmupIteration() { return false; } - virtual QString unitText() = 0; - virtual QString metricText() = 0; + virtual QTest::QBenchmarkMetric metricType() = 0; }; class QBenchmarkTimeMeasurer : public QBenchmarkMeasurerBase @@ -84,8 +84,7 @@ public: bool isMeasurementAccepted(qint64 measurement); int adjustIterationCount(int sugestion); int adjustMedianCount(int suggestion); - QString unitText(); - QString metricText(); + QTest::QBenchmarkMetric metricType(); private: QTime time; }; @@ -102,8 +101,7 @@ public: int adjustIterationCount(int); int adjustMedianCount(int suggestion); bool needsWarmupIteration(); - QString unitText(); - QString metricText(); + QTest::QBenchmarkMetric metricType(); private: CycleCounterTicks startTicks; }; diff --git a/src/testlib/qbenchmarkmetric.cpp b/src/testlib/qbenchmarkmetric.cpp new file mode 100644 index 0000000..75297c4 --- /dev/null +++ b/src/testlib/qbenchmarkmetric.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/private/qbenchmarkmetric_p.h> + +const char * QTest::benchmarkMetricName(QBenchmarkMetric metric) +{ + switch (metric) { + case FramesPerSecond: + return "FramesPerSecond"; + case BitsPerSecond: + return "BitsPerSecond"; + case BytesPerSecond: + return "BytesPerSecond"; + case WalltimeMilliseconds: + return "WalltimeMilliseconds"; + case CPUTicks: + return "CPUTicks"; + case InstructionReads: + return "InstructionReads"; + case Events: + return "Events"; + default: + return ""; + } +}; + +const char * QTest::benchmarkMetricUnit(QBenchmarkMetric metric) +{ + switch (metric) { + case FramesPerSecond: + return "fps"; + case BitsPerSecond: + return "bits/s"; + case BytesPerSecond: + return "bytes/s"; + case WalltimeMilliseconds: + return "msecs"; + case CPUTicks: + return "CPU ticks"; + case InstructionReads: + return "instruction reads"; + case Events: + return "events"; + default: + return ""; + } +} + diff --git a/src/testlib/qbenchmarkmetric.h b/src/testlib/qbenchmarkmetric.h new file mode 100644 index 0000000..302c1aa --- /dev/null +++ b/src/testlib/qbenchmarkmetric.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBENCHMARKMETRIC_H +#define QBENCHMARKMETRIC_H + +#include <QtTest/qtest_global.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +namespace QTest { + +enum QBenchmarkMetric { + FramesPerSecond, + BitsPerSecond, + BytesPerSecond, + WalltimeMilliseconds, + CPUTicks, + InstructionReads, + Events +}; + +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QBENCHMARK_H diff --git a/src/testlib/qbenchmarkmetric_p.h b/src/testlib/qbenchmarkmetric_p.h new file mode 100644 index 0000000..c919d2e --- /dev/null +++ b/src/testlib/qbenchmarkmetric_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBENCHMARKMETRIC_P_H +#define QBENCHMARKMETRIC_P_H + +#include <QtTest/qtest_global.h> +#include <QtTest/qbenchmarkmetric.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Test) + +namespace QTest { + const char * benchmarkMetricName(QBenchmarkMetric metric); + const char * benchmarkMetricUnit(QBenchmarkMetric metric); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QBENCHMARK_H diff --git a/src/testlib/qbenchmarkvalgrind.cpp b/src/testlib/qbenchmarkvalgrind.cpp index 264774e..8c64c0b 100644 --- a/src/testlib/qbenchmarkvalgrind.cpp +++ b/src/testlib/qbenchmarkvalgrind.cpp @@ -263,14 +263,9 @@ bool QBenchmarkCallgrindMeasurer::needsWarmupIteration() return true; } -QString QBenchmarkCallgrindMeasurer::unitText() +QTest::QBenchmarkMetric QBenchmarkCallgrindMeasurer::metricType() { - return QLatin1String("instr. loads"); -} - -QString QBenchmarkCallgrindMeasurer::metricText() -{ - return QLatin1String("callgrind"); + return QTest::InstructionReads; } QT_END_NAMESPACE diff --git a/src/testlib/qbenchmarkvalgrind_p.h b/src/testlib/qbenchmarkvalgrind_p.h index 754fe3d..7b6893b 100644 --- a/src/testlib/qbenchmarkvalgrind_p.h +++ b/src/testlib/qbenchmarkvalgrind_p.h @@ -54,6 +54,7 @@ // #include "QtTest/private/qbenchmarkmeasurement_p.h" +#include "QtTest/private/qbenchmarkmetric_p.h" #include <QtCore/qmap.h> #include <QtCore/qstring.h> @@ -84,8 +85,7 @@ public: int adjustIterationCount(int); int adjustMedianCount(int); bool needsWarmupIteration(); - QString unitText(); - QString metricText(); + QTest::QBenchmarkMetric metricType(); }; QT_END_NAMESPACE diff --git a/src/testlib/qplaintestlogger.cpp b/src/testlib/qplaintestlogger.cpp index 0888cfc..46431a8 100644 --- a/src/testlib/qplaintestlogger.cpp +++ b/src/testlib/qplaintestlogger.cpp @@ -44,6 +44,7 @@ #include "QtTest/private/qtestlog_p.h" #include "QtTest/private/qplaintestlogger_p.h" #include "QtTest/private/qbenchmark_p.h" +#include "QtTest/private/qbenchmarkmetric_p.h" #include <stdarg.h> #include <stdio.h> @@ -324,7 +325,6 @@ namespace QTest { QTestResult::currentTestObjectName(), result.context.slotName.toAscii().data()); - char bufTag[1024]; bufTag[0] = 0; QByteArray tag = result.context.tag.toAscii(); @@ -340,32 +340,43 @@ namespace QTest { char fill[1024]; QTest::qt_snprintf(fill, sizeof(fill), fillFormat, ""); - - QByteArray unitText = QBenchmarkGlobalData::current->measurer->unitText().toAscii(); + const char * unitText = QTest::benchmarkMetricUnit(result.metric); qreal valuePerIteration = qreal(result.value) / qreal(result.iterations); char resultBuffer[100] = ""; formatResult(resultBuffer, 100, valuePerIteration, countSignificantDigits(result.value)); - QByteArray iterationText = "per iteration"; - char buf2[1024]; - Q_ASSERT(result.iterations > 0); QTest::qt_snprintf( - buf2, sizeof(buf2), "%s %s %s", + buf2, sizeof(buf2), "%s %s", resultBuffer, - unitText.data(), + unitText); + + char buf2_[1024]; + QByteArray iterationText = " per iteration"; + Q_ASSERT(result.iterations > 0); + QTest::qt_snprintf( + buf2_, + sizeof(buf2_), "%s", iterationText.data()); char buf3[1024]; Q_ASSERT(result.iterations > 0); + formatResult(resultBuffer, 100, result.value, countSignificantDigits(result.value)); QTest::qt_snprintf( - buf3, sizeof(buf3), " (total: %s, iterations: %d)\n", - QByteArray::number(result.value).constData(), // no 64-bit qt_snprintf support + buf3, sizeof(buf3), " (total: %s, iterations: %d)", + resultBuffer, result.iterations); char buf[1024]; - QTest::qt_snprintf(buf, sizeof(buf), "%s%s%s%s%s", buf1, bufTag, fill, buf2, buf3); + + if (result.setByMacro) { + QTest::qt_snprintf( + buf, sizeof(buf), "%s%s%s%s%s%s\n", buf1, bufTag, fill, buf2, buf2_, buf3); + } else { + QTest::qt_snprintf(buf, sizeof(buf), "%s%s%s%s\n", buf1, bufTag, fill, buf2); + } + memcpy(buf, bmtag, strlen(bmtag)); outputMessage(buf); } diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index ecdcca8..40daecb 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -846,6 +846,9 @@ namespace QTest static int mouseDelay = -1; static int eventDelay = -1; static int keyVerbose = -1; +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) + static bool noCrashHandler = false; +#endif void filter_unprintable(char *str) { @@ -976,6 +979,9 @@ static void qParseArgs(int argc, char *argv[]) " -keyevent-verbose : Turn on verbose messages for keyboard simulation\n" " -maxwarnings n : Sets the maximum amount of messages to output.\n" " 0 means unlimited, default: 2000\n" +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) + " -nocrashhandler : Disables the crash handler\n" +#endif "\n" " Benchmark related options:\n" #ifdef QTESTLIB_USE_VALGRIND @@ -1056,6 +1062,10 @@ static void qParseArgs(int argc, char *argv[]) } else { QTestLog::setMaxWarnings(qToInt(argv[++i])); } +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) + } else if (strcmp(argv[i], "-nocrashhandler") == 0) { + QTest::noCrashHandler = true; +#endif } else if (strcmp(argv[i], "-keyevent-verbose") == 0) { QTest::keyVerbose = 1; #ifdef QTESTLIB_USE_VALGRIND @@ -1661,7 +1671,9 @@ int QTest::qExec(QObject *testObject, int argc, char **argv) #endif { #if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) - FatalSignalHandler handler; + QScopedPointer<FatalSignalHandler> handler; + if (!noCrashHandler) + handler.reset(new FatalSignalHandler); #endif qInvokeTestMethods(testObject); } diff --git a/src/testlib/qtestcoreelement.h b/src/testlib/qtestcoreelement.h index e1ad44a..4738f7d 100644 --- a/src/testlib/qtestcoreelement.h +++ b/src/testlib/qtestcoreelement.h @@ -45,6 +45,8 @@ #include <QtTest/qtestcorelist.h> #include <QtTest/qtestelementattribute.h> +#include <cstdlib> + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -93,6 +95,9 @@ void QTestCoreElement<ElementType>::addAttribute(const QTest::AttributeIndex att if (attribute(attributeIndex)) return; + // if (attributeIndex == QTest::AI_Metric) + // abort(); + QTestElementAttribute *testAttribute = new QTestElementAttribute; testAttribute->setPair(attributeIndex, value); testAttribute->addToList(&listOfAttributes); diff --git a/src/testlib/qtestlogger.cpp b/src/testlib/qtestlogger.cpp index c0bc26c..6c76388 100644 --- a/src/testlib/qtestlogger.cpp +++ b/src/testlib/qtestlogger.cpp @@ -267,7 +267,9 @@ void QTestLogger::addBenchmarkResult(const QBenchmarkResult &result) QTestElement *benchmarkElement = new QTestElement(QTest::LET_Benchmark); // printf("element %i", benchmarkElement->elementType()); - benchmarkElement->addAttribute(QTest::AI_Metric, QBenchmarkGlobalData::current->measurer->metricText().toAscii().data()); + benchmarkElement->addAttribute( + QTest::AI_Metric, + QTest::benchmarkMetricName(QBenchmarkTestMethodData::current->result.metric)); benchmarkElement->addAttribute(QTest::AI_Tag, result.context.tag.toAscii().data()); benchmarkElement->addAttribute(QTest::AI_Value, QByteArray::number(result.value).constData()); diff --git a/src/testlib/qxmltestlogger.cpp b/src/testlib/qxmltestlogger.cpp index 13103f3..2236666 100644 --- a/src/testlib/qxmltestlogger.cpp +++ b/src/testlib/qxmltestlogger.cpp @@ -46,6 +46,7 @@ #include "QtTest/private/qxmltestlogger_p.h" #include "QtTest/private/qtestresult_p.h" #include "QtTest/private/qbenchmark_p.h" +#include "QtTest/private/qbenchmarkmetric_p.h" #include "QtTest/qtestcase.h" QT_BEGIN_NAMESPACE @@ -243,7 +244,7 @@ void QXmlTestLogger::addBenchmarkResult(const QBenchmarkResult &result) QTestCharBuffer quotedTag; xmlQuote("edMetric, - QBenchmarkGlobalData::current->measurer->metricText().toAscii().constData()); + benchmarkMetricUnit(result.metric)); xmlQuote("edTag, result.context.tag.toAscii().constData()); QTest::qt_asprintf( diff --git a/src/testlib/testlib.pro b/src/testlib/testlib.pro index f68ff35..a8186d8 100644 --- a/src/testlib/testlib.pro +++ b/src/testlib/testlib.pro @@ -47,6 +47,7 @@ SOURCES = qtestcase.cpp \ qbenchmarkmeasurement.cpp \ qbenchmarkvalgrind.cpp \ qbenchmarkevent.cpp \ + qbenchmarkmetric.cpp \ qtestelement.cpp \ qtestelementattribute.cpp \ qtestbasicstreamer.cpp \ diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 982d95f5..6729c62 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -173,7 +173,7 @@ void Generator::generateCode() int index = 14; fprintf(out, "static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData()); fprintf(out, "\n // content:\n"); - fprintf(out, " %4d, // revision\n", 4); + fprintf(out, " %4d, // revision\n", 5); fprintf(out, " %4d, // classname\n", strreg(cdef->qualified)); fprintf(out, " %4d, %4d, // classinfo\n", cdef->classInfoList.count(), cdef->classInfoList.count() ? index : 0); index += cdef->classInfoList.count() * 2; diff --git a/src/xmlpatterns/type/qprimitives_p.h b/src/xmlpatterns/type/qprimitives_p.h index 1090f7f..c378a66 100644 --- a/src/xmlpatterns/type/qprimitives_p.h +++ b/src/xmlpatterns/type/qprimitives_p.h @@ -79,18 +79,6 @@ class QString; */ namespace QPatternist { - - /** - * @internal - * - * A method to allow a QHash or QSet with QUrl - * as key type. - */ - inline uint qHash(const QUrl &uri) - { - return qHash(uri.toString()); - } - /** * @defgroup Patternist_cppWXSTypes C++ Primitives for W3C XML Schema Number Types * @@ -208,8 +196,6 @@ namespace QPatternist QString Q_AUTOTEST_EXPORT escape(const QString &input); } -using QPatternist::qHash; - QT_END_NAMESPACE QT_END_HEADER |