From 38be0d13830efd2d98281c645c3a60afe05ffece Mon Sep 17 00:00:00 2001 From: Qt by Nokia Date: Wed, 27 Apr 2011 12:05:43 +0200 Subject: Initial import from the monolithic Qt. This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12 --- src/corelib/kernel/kernel.pri | 164 + src/corelib/kernel/qabstracteventdispatcher.cpp | 556 +++ src/corelib/kernel/qabstracteventdispatcher.h | 107 + src/corelib/kernel/qabstracteventdispatcher_p.h | 79 + src/corelib/kernel/qabstractitemmodel.cpp | 3454 ++++++++++++++++++ src/corelib/kernel/qabstractitemmodel.h | 410 +++ src/corelib/kernel/qabstractitemmodel_p.h | 176 + src/corelib/kernel/qbasictimer.cpp | 138 + src/corelib/kernel/qbasictimer.h | 74 + src/corelib/kernel/qcore_mac.cpp | 82 + src/corelib/kernel/qcore_mac_p.h | 163 + src/corelib/kernel/qcore_symbian_p.cpp | 317 ++ src/corelib/kernel/qcore_symbian_p.h | 282 ++ src/corelib/kernel/qcore_unix.cpp | 106 + src/corelib/kernel/qcore_unix_p.h | 333 ++ src/corelib/kernel/qcoreapplication.cpp | 2730 ++++++++++++++ src/corelib/kernel/qcoreapplication.h | 297 ++ src/corelib/kernel/qcoreapplication_mac.cpp | 66 + src/corelib/kernel/qcoreapplication_p.h | 146 + src/corelib/kernel/qcoreapplication_win.cpp | 1060 ++++++ src/corelib/kernel/qcorecmdlineargs_p.h | 171 + src/corelib/kernel/qcoreevent.cpp | 612 ++++ src/corelib/kernel/qcoreevent.h | 396 +++ src/corelib/kernel/qcoreglobaldata.cpp | 53 + src/corelib/kernel/qcoreglobaldata_p.h | 72 + src/corelib/kernel/qcrashhandler.cpp | 423 +++ src/corelib/kernel/qcrashhandler_p.h | 81 + src/corelib/kernel/qeventdispatcher_glib.cpp | 601 ++++ src/corelib/kernel/qeventdispatcher_glib_p.h | 119 + src/corelib/kernel/qeventdispatcher_symbian.cpp | 1310 +++++++ src/corelib/kernel/qeventdispatcher_symbian_p.h | 327 ++ src/corelib/kernel/qeventdispatcher_unix.cpp | 979 +++++ src/corelib/kernel/qeventdispatcher_unix_p.h | 208 ++ src/corelib/kernel/qeventdispatcher_win.cpp | 1158 ++++++ src/corelib/kernel/qeventdispatcher_win_p.h | 110 + src/corelib/kernel/qeventloop.cpp | 329 ++ src/corelib/kernel/qeventloop.h | 101 + src/corelib/kernel/qfunctions_nacl.cpp | 156 + src/corelib/kernel/qfunctions_nacl.h | 97 + src/corelib/kernel/qfunctions_p.h | 79 + src/corelib/kernel/qfunctions_vxworks.cpp | 202 ++ src/corelib/kernel/qfunctions_vxworks.h | 153 + src/corelib/kernel/qfunctions_wince.cpp | 451 +++ src/corelib/kernel/qfunctions_wince.h | 397 +++ src/corelib/kernel/qmath.cpp | 305 ++ src/corelib/kernel/qmath.h | 288 ++ src/corelib/kernel/qmath.qdoc | 155 + src/corelib/kernel/qmetaobject.cpp | 2778 +++++++++++++++ src/corelib/kernel/qmetaobject.h | 242 ++ src/corelib/kernel/qmetaobject_p.h | 326 ++ src/corelib/kernel/qmetatype.cpp | 1509 ++++++++ src/corelib/kernel/qmetatype.h | 416 +++ src/corelib/kernel/qmimedata.cpp | 627 ++++ src/corelib/kernel/qmimedata.h | 104 + src/corelib/kernel/qobject.cpp | 4310 +++++++++++++++++++++++ src/corelib/kernel/qobject.h | 411 +++ src/corelib/kernel/qobject_p.h | 305 ++ src/corelib/kernel/qobjectcleanuphandler.cpp | 148 + src/corelib/kernel/qobjectcleanuphandler.h | 78 + src/corelib/kernel/qobjectdefs.h | 506 +++ src/corelib/kernel/qpointer.cpp | 270 ++ src/corelib/kernel/qpointer.h | 168 + src/corelib/kernel/qsharedmemory.cpp | 612 ++++ src/corelib/kernel/qsharedmemory.h | 121 + src/corelib/kernel/qsharedmemory_p.h | 179 + src/corelib/kernel/qsharedmemory_symbian.cpp | 173 + src/corelib/kernel/qsharedmemory_unix.cpp | 302 ++ src/corelib/kernel/qsharedmemory_win.cpp | 195 + src/corelib/kernel/qsignalmapper.cpp | 321 ++ src/corelib/kernel/qsignalmapper.h | 100 + src/corelib/kernel/qsocketnotifier.cpp | 323 ++ src/corelib/kernel/qsocketnotifier.h | 93 + src/corelib/kernel/qsystemerror.cpp | 220 ++ src/corelib/kernel/qsystemerror_p.h | 107 + src/corelib/kernel/qsystemsemaphore.cpp | 363 ++ src/corelib/kernel/qsystemsemaphore.h | 103 + src/corelib/kernel/qsystemsemaphore_p.h | 118 + src/corelib/kernel/qsystemsemaphore_symbian.cpp | 138 + src/corelib/kernel/qsystemsemaphore_unix.cpp | 238 ++ src/corelib/kernel/qsystemsemaphore_win.cpp | 135 + src/corelib/kernel/qtcore_eval.cpp | 571 +++ src/corelib/kernel/qtimer.cpp | 394 +++ src/corelib/kernel/qtimer.h | 116 + src/corelib/kernel/qtranslator.cpp | 979 +++++ src/corelib/kernel/qtranslator.h | 104 + src/corelib/kernel/qtranslator_p.h | 79 + src/corelib/kernel/qvariant.cpp | 3237 +++++++++++++++++ src/corelib/kernel/qvariant.h | 618 ++++ src/corelib/kernel/qvariant_p.h | 153 + src/corelib/kernel/qwineventnotifier_p.cpp | 134 + src/corelib/kernel/qwineventnotifier_p.h | 94 + 91 files changed, 42591 insertions(+) create mode 100644 src/corelib/kernel/kernel.pri create mode 100644 src/corelib/kernel/qabstracteventdispatcher.cpp create mode 100644 src/corelib/kernel/qabstracteventdispatcher.h create mode 100644 src/corelib/kernel/qabstracteventdispatcher_p.h create mode 100644 src/corelib/kernel/qabstractitemmodel.cpp create mode 100644 src/corelib/kernel/qabstractitemmodel.h create mode 100644 src/corelib/kernel/qabstractitemmodel_p.h create mode 100644 src/corelib/kernel/qbasictimer.cpp create mode 100644 src/corelib/kernel/qbasictimer.h create mode 100644 src/corelib/kernel/qcore_mac.cpp create mode 100644 src/corelib/kernel/qcore_mac_p.h create mode 100644 src/corelib/kernel/qcore_symbian_p.cpp create mode 100644 src/corelib/kernel/qcore_symbian_p.h create mode 100644 src/corelib/kernel/qcore_unix.cpp create mode 100644 src/corelib/kernel/qcore_unix_p.h create mode 100644 src/corelib/kernel/qcoreapplication.cpp create mode 100644 src/corelib/kernel/qcoreapplication.h create mode 100644 src/corelib/kernel/qcoreapplication_mac.cpp create mode 100644 src/corelib/kernel/qcoreapplication_p.h create mode 100644 src/corelib/kernel/qcoreapplication_win.cpp create mode 100644 src/corelib/kernel/qcorecmdlineargs_p.h create mode 100644 src/corelib/kernel/qcoreevent.cpp create mode 100644 src/corelib/kernel/qcoreevent.h create mode 100644 src/corelib/kernel/qcoreglobaldata.cpp create mode 100644 src/corelib/kernel/qcoreglobaldata_p.h create mode 100644 src/corelib/kernel/qcrashhandler.cpp create mode 100644 src/corelib/kernel/qcrashhandler_p.h create mode 100644 src/corelib/kernel/qeventdispatcher_glib.cpp create mode 100644 src/corelib/kernel/qeventdispatcher_glib_p.h create mode 100644 src/corelib/kernel/qeventdispatcher_symbian.cpp create mode 100644 src/corelib/kernel/qeventdispatcher_symbian_p.h create mode 100644 src/corelib/kernel/qeventdispatcher_unix.cpp create mode 100644 src/corelib/kernel/qeventdispatcher_unix_p.h create mode 100644 src/corelib/kernel/qeventdispatcher_win.cpp create mode 100644 src/corelib/kernel/qeventdispatcher_win_p.h create mode 100644 src/corelib/kernel/qeventloop.cpp create mode 100644 src/corelib/kernel/qeventloop.h create mode 100644 src/corelib/kernel/qfunctions_nacl.cpp create mode 100644 src/corelib/kernel/qfunctions_nacl.h create mode 100644 src/corelib/kernel/qfunctions_p.h create mode 100644 src/corelib/kernel/qfunctions_vxworks.cpp create mode 100644 src/corelib/kernel/qfunctions_vxworks.h create mode 100644 src/corelib/kernel/qfunctions_wince.cpp create mode 100644 src/corelib/kernel/qfunctions_wince.h create mode 100644 src/corelib/kernel/qmath.cpp create mode 100644 src/corelib/kernel/qmath.h create mode 100644 src/corelib/kernel/qmath.qdoc create mode 100644 src/corelib/kernel/qmetaobject.cpp create mode 100644 src/corelib/kernel/qmetaobject.h create mode 100644 src/corelib/kernel/qmetaobject_p.h create mode 100644 src/corelib/kernel/qmetatype.cpp create mode 100644 src/corelib/kernel/qmetatype.h create mode 100644 src/corelib/kernel/qmimedata.cpp create mode 100644 src/corelib/kernel/qmimedata.h create mode 100644 src/corelib/kernel/qobject.cpp create mode 100644 src/corelib/kernel/qobject.h create mode 100644 src/corelib/kernel/qobject_p.h create mode 100644 src/corelib/kernel/qobjectcleanuphandler.cpp create mode 100644 src/corelib/kernel/qobjectcleanuphandler.h create mode 100644 src/corelib/kernel/qobjectdefs.h create mode 100644 src/corelib/kernel/qpointer.cpp create mode 100644 src/corelib/kernel/qpointer.h create mode 100644 src/corelib/kernel/qsharedmemory.cpp create mode 100644 src/corelib/kernel/qsharedmemory.h create mode 100644 src/corelib/kernel/qsharedmemory_p.h create mode 100644 src/corelib/kernel/qsharedmemory_symbian.cpp create mode 100644 src/corelib/kernel/qsharedmemory_unix.cpp create mode 100644 src/corelib/kernel/qsharedmemory_win.cpp create mode 100644 src/corelib/kernel/qsignalmapper.cpp create mode 100644 src/corelib/kernel/qsignalmapper.h create mode 100644 src/corelib/kernel/qsocketnotifier.cpp create mode 100644 src/corelib/kernel/qsocketnotifier.h create mode 100644 src/corelib/kernel/qsystemerror.cpp create mode 100644 src/corelib/kernel/qsystemerror_p.h create mode 100644 src/corelib/kernel/qsystemsemaphore.cpp create mode 100644 src/corelib/kernel/qsystemsemaphore.h create mode 100644 src/corelib/kernel/qsystemsemaphore_p.h create mode 100644 src/corelib/kernel/qsystemsemaphore_symbian.cpp create mode 100644 src/corelib/kernel/qsystemsemaphore_unix.cpp create mode 100644 src/corelib/kernel/qsystemsemaphore_win.cpp create mode 100644 src/corelib/kernel/qtcore_eval.cpp create mode 100644 src/corelib/kernel/qtimer.cpp create mode 100644 src/corelib/kernel/qtimer.h create mode 100644 src/corelib/kernel/qtranslator.cpp create mode 100644 src/corelib/kernel/qtranslator.h create mode 100644 src/corelib/kernel/qtranslator_p.h create mode 100644 src/corelib/kernel/qvariant.cpp create mode 100644 src/corelib/kernel/qvariant.h create mode 100644 src/corelib/kernel/qvariant_p.h create mode 100644 src/corelib/kernel/qwineventnotifier_p.cpp create mode 100644 src/corelib/kernel/qwineventnotifier_p.h (limited to 'src/corelib/kernel') diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri new file mode 100644 index 0000000000..a3628b1be5 --- /dev/null +++ b/src/corelib/kernel/kernel.pri @@ -0,0 +1,164 @@ +# Qt core object module + +HEADERS += \ + kernel/qabstracteventdispatcher.h \ + kernel/qabstractitemmodel.h \ + kernel/qabstractitemmodel_p.h \ + kernel/qbasictimer.h \ + kernel/qeventloop.h\ + kernel/qpointer.h \ + kernel/qcorecmdlineargs_p.h \ + kernel/qcoreapplication.h \ + kernel/qcoreevent.h \ + kernel/qmetaobject.h \ + kernel/qmetatype.h \ + kernel/qmimedata.h \ + kernel/qobject.h \ + kernel/qobjectdefs.h \ + kernel/qsignalmapper.h \ + kernel/qsocketnotifier.h \ + kernel/qtimer.h \ + kernel/qtranslator.h \ + kernel/qtranslator_p.h \ + kernel/qvariant.h \ + kernel/qabstracteventdispatcher_p.h \ + kernel/qcoreapplication_p.h \ + kernel/qobjectcleanuphandler.h \ + kernel/qvariant_p.h \ + kernel/qmetaobject_p.h \ + kernel/qobject_p.h \ + kernel/qcoreglobaldata_p.h \ + kernel/qsharedmemory.h \ + kernel/qsharedmemory_p.h \ + kernel/qsystemsemaphore.h \ + kernel/qsystemsemaphore_p.h \ + kernel/qfunctions_p.h \ + kernel/qmath.h \ + kernel/qsystemerror_p.h + +SOURCES += \ + kernel/qabstracteventdispatcher.cpp \ + kernel/qabstractitemmodel.cpp \ + kernel/qbasictimer.cpp \ + kernel/qeventloop.cpp \ + kernel/qcoreapplication.cpp \ + kernel/qcoreevent.cpp \ + kernel/qmetaobject.cpp \ + kernel/qmetatype.cpp \ + kernel/qmimedata.cpp \ + kernel/qobject.cpp \ + kernel/qobjectcleanuphandler.cpp \ + kernel/qsignalmapper.cpp \ + kernel/qsocketnotifier.cpp \ + kernel/qtimer.cpp \ + kernel/qtranslator.cpp \ + kernel/qvariant.cpp \ + kernel/qcoreglobaldata.cpp \ + kernel/qsharedmemory.cpp \ + kernel/qsystemsemaphore.cpp \ + kernel/qpointer.cpp \ + kernel/qmath.cpp \ + kernel/qsystemerror.cpp + +win32 { + SOURCES += \ + kernel/qeventdispatcher_win.cpp \ + kernel/qcoreapplication_win.cpp \ + kernel/qwineventnotifier_p.cpp \ + kernel/qsharedmemory_win.cpp \ + kernel/qsystemsemaphore_win.cpp + HEADERS += \ + kernel/qeventdispatcher_win_p.h \ + kernel/qwineventnotifier_p.h +} + + +wince*: { + SOURCES += \ + kernel/qfunctions_wince.cpp + HEADERS += \ + kernel/qfunctions_wince.h +} + +mac:!embedded:!qpa{ + SOURCES += \ + kernel/qcoreapplication_mac.cpp +} + +mac:!nacl { + SOURCES += \ + kernel/qcore_mac.cpp +} + +nacl { + SOURCES += \ + kernel/qfunctions_nacl.cpp + HEADERS += \ + kernel/qfunctions_nacl.h +} + +unix:!symbian { + SOURCES += \ + kernel/qcore_unix.cpp \ + kernel/qcrashhandler.cpp \ + kernel/qsharedmemory_unix.cpp \ + kernel/qsystemsemaphore_unix.cpp + HEADERS += \ + kernel/qcore_unix_p.h \ + kernel/qcrashhandler_p.h + + contains(QT_CONFIG, glib) { + SOURCES += \ + kernel/qeventdispatcher_glib.cpp + HEADERS += \ + kernel/qeventdispatcher_glib_p.h + QMAKE_CXXFLAGS += $$QT_CFLAGS_GLIB + LIBS_PRIVATE +=$$QT_LIBS_GLIB + } + SOURCES += \ + kernel/qeventdispatcher_unix.cpp + HEADERS += \ + kernel/qeventdispatcher_unix_p.h + + contains(QT_CONFIG, clock-gettime):include($$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri) +} + +symbian { + SOURCES += \ + kernel/qcore_unix.cpp \ + kernel/qcrashhandler.cpp \ + kernel/qeventdispatcher_symbian.cpp \ + kernel/qcore_symbian_p.cpp \ + kernel/qsharedmemory_symbian.cpp \ + kernel/qsystemsemaphore_symbian.cpp + + HEADERS += \ + kernel/qcore_unix_p.h \ + kernel/qcrashhandler_p.h \ + kernel/qeventdispatcher_symbian_p.h \ + kernel/qcore_symbian_p.h +} + +vxworks { + SOURCES += \ + kernel/qfunctions_vxworks.cpp + HEADERS += \ + kernel/qfunctions_vxworks.h +} + + +integrity { + SOURCES += \ + kernel/qcore_unix.cpp \ + kernel/qcrashhandler.cpp \ + kernel/qsharedmemory_unix.cpp \ + kernel/qsystemsemaphore_unix.cpp \ + kernel/qeventdispatcher_unix.cpp + HEADERS += \ + kernel/qcore_unix_p.h \ + kernel/qcrashhandler_p.h \ + kernel/qeventdispatcher_unix_p.h + + contains(QT_CONFIG, clock-gettime):include($$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri) +} + diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp new file mode 100644 index 0000000000..2949cd0d3a --- /dev/null +++ b/src/corelib/kernel/qabstracteventdispatcher.cpp @@ -0,0 +1,556 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qabstracteventdispatcher.h" +#include "qabstracteventdispatcher_p.h" + +#include "qthread.h" +#include +#include + +QT_BEGIN_NAMESPACE + +// we allow for 2^24 = 8^8 = 16777216 simultaneously running timers +static const int TimerIdMask = 0x00ffffff; +static const int TimerSerialMask = ~TimerIdMask & ~0x80000000; +static const int TimerSerialCounter = TimerIdMask + 1; +static const int MaxTimerId = TimerIdMask; + +static int FirstBucket[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 +}; + +enum { + FirstBucketOffset = 0, + SecondBucketOffset = sizeof(FirstBucket) / sizeof(FirstBucket[0]), + ThirdBucketOffset = 0x100, + FourthBucketOffset = 0x1000, + FifthBucketOffset = 0x10000, + SixthBucketOffset = 0x100000 +}; + +enum { + FirstBucketSize = SecondBucketOffset, + SecondBucketSize = ThirdBucketOffset - SecondBucketOffset, + ThirdBucketSize = FourthBucketOffset - ThirdBucketOffset, + FourthBucketSize = FifthBucketOffset - FourthBucketOffset, + FifthBucketSize = SixthBucketOffset - FifthBucketOffset, + SixthBucketSize = MaxTimerId - SixthBucketOffset +}; + +static const int BucketSize[] = { + FirstBucketSize, SecondBucketSize, ThirdBucketSize, + FourthBucketSize, FifthBucketSize, SixthBucketSize +}; +enum { NumberOfBuckets = sizeof(BucketSize) / sizeof(BucketSize[0]) }; + +static const int BucketOffset[] = { + FirstBucketOffset, SecondBucketOffset, ThirdBucketOffset, + FourthBucketOffset, FifthBucketOffset, SixthBucketOffset +}; + +static QBasicAtomicPointer timerIds[] = + { Q_BASIC_ATOMIC_INITIALIZER(FirstBucket), + Q_BASIC_ATOMIC_INITIALIZER(0), + Q_BASIC_ATOMIC_INITIALIZER(0), + Q_BASIC_ATOMIC_INITIALIZER(0), + Q_BASIC_ATOMIC_INITIALIZER(0), + Q_BASIC_ATOMIC_INITIALIZER(0) }; + +static void timerIdsDestructorFunction() +{ + // start at one, the first bucket is pre-allocated + for (int i = 1; i < NumberOfBuckets; ++i) + delete [] static_cast(timerIds[i]); +} +Q_DESTRUCTOR_FUNCTION(timerIdsDestructorFunction) + +static QBasicAtomicInt nextFreeTimerId = Q_BASIC_ATOMIC_INITIALIZER(1); + +// avoid the ABA-problem by using 7 of the top 8 bits of the timerId as a serial number +static inline int prepareNewValueWithSerialNumber(int oldId, int newId) +{ + return (newId & TimerIdMask) | ((oldId + TimerSerialCounter) & TimerSerialMask); +} + +namespace { + template struct QStaticAssertType; + template<> struct QStaticAssertType { enum { Value = 1 }; }; +} +#define q_static_assert(expr) (void)QStaticAssertType::Value + +static inline int bucketOffset(int timerId) +{ + q_static_assert(sizeof BucketSize == sizeof BucketOffset); + q_static_assert(sizeof(timerIds) / sizeof(timerIds[0]) == NumberOfBuckets); + + for (int i = 0; i < NumberOfBuckets; ++i) { + if (timerId < BucketSize[i]) + return i; + timerId -= BucketSize[i]; + } + qFatal("QAbstractEventDispatcher: INTERNAL ERROR, timer ID %d is too large", timerId); + return -1; +} + +static inline int bucketIndex(int bucket, int timerId) +{ + return timerId - BucketOffset[bucket]; +} + +static inline int *allocateBucket(int bucket) +{ + // allocate a new bucket + const int size = BucketSize[bucket]; + const int offset = BucketOffset[bucket]; + int *b = new int[size]; + for (int i = 0; i != size; ++i) + b[i] = offset + i + 1; + return b; +} + +void QAbstractEventDispatcherPrivate::init() +{ + Q_Q(QAbstractEventDispatcher); + if (threadData->eventDispatcher != 0) { + qWarning("QAbstractEventDispatcher: An event dispatcher has already been created for this thread"); + } else { + threadData->eventDispatcher = q; + } +} + +// Timer IDs are implemented using a free-list; +// there's a vector initialized with: +// X[i] = i + 1 +// and nextFreeTimerId starts with 1. +// +// Allocating a timer ID involves taking the ID from +// X[nextFreeTimerId] +// updating nextFreeTimerId to this value and returning the old value +// +// When the timer ID is allocated, its cell in the vector is unused (it's a +// free list). As an added protection, we use the cell to store an invalid +// (negative) value that we can later check for integrity. +// +// (continues below). +int QAbstractEventDispatcherPrivate::allocateTimerId() +{ + int timerId, newTimerId; + int at, *b; + do { + timerId = nextFreeTimerId; //.loadAcquire(); // ### FIXME Proper memory ordering semantics + + // which bucket are we looking in? + int which = timerId & TimerIdMask; + int bucket = bucketOffset(which); + at = bucketIndex(bucket, which); + b = timerIds[bucket]; + + if (!b) { + // allocate a new bucket + b = allocateBucket(bucket); + if (!timerIds[bucket].testAndSetRelease(0, b)) { + // another thread won the race to allocate the bucket + delete [] b; + b = timerIds[bucket]; + } + } + + newTimerId = prepareNewValueWithSerialNumber(timerId, b[at]); + } while (!nextFreeTimerId.testAndSetRelaxed(timerId, newTimerId)); + + b[at] = -timerId; + + return timerId; +} + +// Releasing a timer ID requires putting the current ID back in the vector; +// we do it by setting: +// X[timerId] = nextFreeTimerId; +// then we update nextFreeTimerId to the timer we've just released +// +// The extra code in allocateTimerId and releaseTimerId are ABA prevention +// and bucket memory. The buckets are simply to make sure we allocate only +// the necessary number of timers. See above. +// +// ABA prevention simply adds a value to 7 of the top 8 bits when resetting +// nextFreeTimerId. +void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId) +{ + int which = timerId & TimerIdMask; + int bucket = bucketOffset(which); + int at = bucketIndex(bucket, which); + int *b = timerIds[bucket]; + + Q_ASSERT_X(timerId == -b[at], "QAbstractEventDispatcher::releaseTimerId", + "Internal error: timer ID not found"); + + int freeId, newTimerId; + do { + freeId = nextFreeTimerId;//.loadAcquire(); // ### FIXME Proper memory ordering semantics + b[at] = freeId & TimerIdMask; + + newTimerId = prepareNewValueWithSerialNumber(freeId, timerId); + } while (!nextFreeTimerId.testAndSetRelease(freeId, newTimerId)); +} + +/*! + \class QAbstractEventDispatcher + \brief The QAbstractEventDispatcher class provides an interface to manage Qt's event queue. + + \ingroup events + + An event dispatcher receives events from the window system and other + sources. It then sends them to the QCoreApplication or QApplication + instance for processing and delivery. QAbstractEventDispatcher provides + fine-grained control over event delivery. + + For simple control of event processing use + QCoreApplication::processEvents(). + + For finer control of the application's event loop, call + instance() and call functions on the QAbstractEventDispatcher + object that is returned. If you want to use your own instance of + QAbstractEventDispatcher or of a QAbstractEventDispatcher + subclass, you must create your instance \e before you create the + QApplication object. + + The main event loop is started by calling + QCoreApplication::exec(), and stopped by calling + QCoreApplication::exit(). Local event loops can be created using + QEventLoop. + + Programs that perform long operations can call processEvents() + with a bitwise OR combination of various QEventLoop::ProcessEventsFlag + values to control which events should be delivered. + + QAbstractEventDispatcher also allows the integration of an + external event loop with the Qt event loop. For example, the + \l{Qt Solutions}{Motif Extension Qt Solution} includes a + reimplementation of QAbstractEventDispatcher that merges Qt and + Motif events together. + + \sa QEventLoop, QCoreApplication +*/ + +/*! + Constructs a new event dispatcher with the given \a parent. +*/ +QAbstractEventDispatcher::QAbstractEventDispatcher(QObject *parent) + : QObject(*new QAbstractEventDispatcherPrivate, parent) +{ + Q_D(QAbstractEventDispatcher); + d->init(); +} + +/*! + \internal +*/ +QAbstractEventDispatcher::QAbstractEventDispatcher(QAbstractEventDispatcherPrivate &dd, + QObject *parent) + : QObject(dd, parent) +{ + Q_D(QAbstractEventDispatcher); + d->init(); +} + +/*! + Destroys the event dispatcher. +*/ +QAbstractEventDispatcher::~QAbstractEventDispatcher() +{ } + +/*! + Returns a pointer to the event dispatcher object for the specified + \a thread. If \a thread is zero, the current thread is used. If no + event dispatcher exists for the specified thread, this function + returns 0. + + \bold{Note:} If Qt is built without thread support, the \a thread + argument is ignored. + */ +QAbstractEventDispatcher *QAbstractEventDispatcher::instance(QThread *thread) +{ + QThreadData *data = thread ? QThreadData::get2(thread) : QThreadData::current(); + return data->eventDispatcher; +} + +/*! + \fn bool QAbstractEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) + + Processes pending events that match \a flags until there are no + more events to process. Returns true if an event was processed; + otherwise returns false. + + This function is especially useful if you have a long running + operation and want to show its progress without allowing user + input; i.e. by using the QEventLoop::ExcludeUserInputEvents flag. + + If the QEventLoop::WaitForMoreEvents flag is set in \a flags, the + behavior of this function is as follows: + + \list + + \i If events are available, this function returns after processing + them. + + \i If no events are available, this function will wait until more + are available and return after processing newly available events. + + \endlist + + If the QEventLoop::WaitForMoreEvents flag is not set in \a flags, + and no events are available, this function will return + immediately. + + \bold{Note:} This function does not process events continuously; it + returns after all available events are processed. + + \sa hasPendingEvents() +*/ + +/*! \fn bool QAbstractEventDispatcher::hasPendingEvents() + + Returns true if there is an event waiting; otherwise returns + false. +*/ + +/*! + \fn void QAbstractEventDispatcher::registerSocketNotifier(QSocketNotifier *notifier) + + Registers \a notifier with the event loop. Subclasses must + implement this method to tie a socket notifier into another + event loop. +*/ + +/*! \fn void QAbstractEventDispatcher::unregisterSocketNotifier(QSocketNotifier *notifier) + + Unregisters \a notifier from the event dispatcher. Subclasses must + reimplement this method to tie a socket notifier into another + event loop. Reimplementations must call the base + implementation. +*/ + +/*! + \fn int QAbstractEventDispatcher::registerTimer(int interval, QObject *object) + + Registers a timer with the specified \a interval for the given \a object. +*/ +int QAbstractEventDispatcher::registerTimer(int interval, QObject *object) +{ + int id = QAbstractEventDispatcherPrivate::allocateTimerId(); + registerTimer(id, interval, object); + return id; +} + +/*! + \fn void QAbstractEventDispatcher::registerTimer(int timerId, int interval, QObject *object) + + Register a timer with the specified \a timerId and \a interval for + the given \a object. +*/ + +/*! + \fn bool QAbstractEventDispatcher::unregisterTimer(int timerId) + + Unregisters the timer with the given \a timerId. + Returns true if successful; otherwise returns false. + + \sa registerTimer(), unregisterTimers() +*/ + +/*! + \fn bool QAbstractEventDispatcher::unregisterTimers(QObject *object) + + Unregisters all the timers associated with the given \a object. + Returns true if all timers were successful removed; otherwise returns false. + + \sa unregisterTimer(), registeredTimers() +*/ + +/*! + \fn QList QAbstractEventDispatcher::registeredTimers(QObject *object) const + + Returns a list of registered timers for \a object. The timer ID + is the first member in each pair; the interval is the second. +*/ + +/*! \fn void QAbstractEventDispatcher::wakeUp() + \threadsafe + + Wakes up the event loop. + + \sa awake() +*/ + +/*! + \fn void QAbstractEventDispatcher::interrupt() + + Interrupts event dispatching; i.e. the event dispatcher will + return from processEvents() as soon as possible. +*/ + +/*! \fn void QAbstractEventDispatcher::flush() + + Flushes the event queue. This normally returns almost + immediately. Does nothing on platforms other than X11. +*/ + +// ### DOC: Are these called when the _application_ starts/stops or just +// when the current _event loop_ starts/stops? +/*! \internal */ +void QAbstractEventDispatcher::startingUp() +{ } + +/*! \internal */ +void QAbstractEventDispatcher::closingDown() +{ } + +/*! + \typedef QAbstractEventDispatcher::TimerInfo + + Typedef for QPair. The first component of + the pair is the timer ID; the second component is + the interval. + + \sa registeredTimers() +*/ + +/*! + \typedef QAbstractEventDispatcher::EventFilter + + Typedef for a function with the signature + + \snippet doc/src/snippets/code/src_corelib_kernel_qabstracteventdispatcher.cpp 0 + + Note that the type of the \a message is platform dependent. The + following table shows the \a {message}'s type on Windows, Mac, and + X11. You can do a static cast to these types. + + \table + \header + \o Platform + \o type + \row + \o Windows + \o MSG + \row + \o X11 + \o XEvent + \row + \o Mac + \o NSEvent + \endtable + + + + \sa setEventFilter(), filterEvent() +*/ + +/*! + Replaces the event filter function for this + QAbstractEventDispatcher with \a filter and returns the replaced + event filter function. Only the current event filter function is + called. If you want to use both filter functions, save the + replaced EventFilter in a place where yours can call it. + + The event filter function set here is called for all messages + taken from the system event loop before the event is dispatched to + the respective target, including the messages not meant for Qt + objects. + + The event filter function should return true if the message should + be filtered, (i.e. stopped). It should return false to allow + processing the message to continue. + + By default, no event filter function is set (i.e., this function + returns a null EventFilter the first time it is called). +*/ +QAbstractEventDispatcher::EventFilter QAbstractEventDispatcher::setEventFilter(EventFilter filter) +{ + Q_D(QAbstractEventDispatcher); + EventFilter oldFilter = d->event_filter; + d->event_filter = filter; + return oldFilter; +} + +/*! + Sends \a message through the event filter that was set by + setEventFilter(). If no event filter has been set, this function + returns false; otherwise, this function returns the result of the + event filter function. + + Subclasses of QAbstractEventDispatcher \e must call this function + for \e all messages received from the system to ensure + compatibility with any extensions that may be used in the + application. + + Note that the type of \a message is platform dependent. See + QAbstractEventDispatcher::EventFilter for details. + + \sa setEventFilter() +*/ +bool QAbstractEventDispatcher::filterEvent(void *message) +{ + Q_D(QAbstractEventDispatcher); + if (d->event_filter) + return d->event_filter(message); + return false; +} + +/*! \fn void QAbstractEventDispatcher::awake() + + This signal is emitted after the event loop returns from a + function that could block. + + \sa wakeUp() aboutToBlock() +*/ + +/*! \fn void QAbstractEventDispatcher::aboutToBlock() + + This signal is emitted before the event loop calls a function that + could block. + + \sa awake() +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qabstracteventdispatcher.h b/src/corelib/kernel/qabstracteventdispatcher.h new file mode 100644 index 0000000000..212b832ae2 --- /dev/null +++ b/src/corelib/kernel/qabstracteventdispatcher.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QABSTRACTEVENTDISPATCHER_H +#define QABSTRACTEVENTDISPATCHER_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QAbstractEventDispatcherPrivate; +class QSocketNotifier; +template struct QPair; + +class Q_CORE_EXPORT QAbstractEventDispatcher : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QAbstractEventDispatcher) + +public: + typedef QPair TimerInfo; + + explicit QAbstractEventDispatcher(QObject *parent = 0); + ~QAbstractEventDispatcher(); + + static QAbstractEventDispatcher *instance(QThread *thread = 0); + + virtual bool processEvents(QEventLoop::ProcessEventsFlags flags) = 0; + virtual bool hasPendingEvents() = 0; + + virtual void registerSocketNotifier(QSocketNotifier *notifier) = 0; + virtual void unregisterSocketNotifier(QSocketNotifier *notifier) = 0; + + int registerTimer(int interval, QObject *object); + virtual void registerTimer(int timerId, int interval, QObject *object) = 0; + virtual bool unregisterTimer(int timerId) = 0; + virtual bool unregisterTimers(QObject *object) = 0; + virtual QList registeredTimers(QObject *object) const = 0; + + virtual void wakeUp() = 0; + virtual void interrupt() = 0; + virtual void flush() = 0; + + virtual void startingUp(); + virtual void closingDown(); + + typedef bool(*EventFilter)(void *message); + EventFilter setEventFilter(EventFilter filter); + bool filterEvent(void *message); + +Q_SIGNALS: + void aboutToBlock(); + void awake(); + +protected: + QAbstractEventDispatcher(QAbstractEventDispatcherPrivate &, + QObject *parent); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QABSTRACTEVENTDISPATCHER_H diff --git a/src/corelib/kernel/qabstracteventdispatcher_p.h b/src/corelib/kernel/qabstracteventdispatcher_p.h new file mode 100644 index 0000000000..4a55dfba09 --- /dev/null +++ b/src/corelib/kernel/qabstracteventdispatcher_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QABSTRACTEVENTDISPATCHER_P_H +#define QABSTRACTEVENTDISPATCHER_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/qabstracteventdispatcher.h" +#include "private/qobject_p.h" + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT uint qGlobalPostedEventsCount(); + +class Q_CORE_EXPORT QAbstractEventDispatcherPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QAbstractEventDispatcher) +public: + inline QAbstractEventDispatcherPrivate() + : event_filter(0) + { } + void init(); + QAbstractEventDispatcher::EventFilter event_filter; + + static int allocateTimerId(); + static void releaseTimerId(int id); +}; + +QT_END_NAMESPACE + +#endif // QABSTRACTEVENTDISPATCHER_P_H diff --git a/src/corelib/kernel/qabstractitemmodel.cpp b/src/corelib/kernel/qabstractitemmodel.cpp new file mode 100644 index 0000000000..9dd22dc39b --- /dev/null +++ b/src/corelib/kernel/qabstractitemmodel.cpp @@ -0,0 +1,3454 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qabstractitemmodel.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QPersistentModelIndexData *QPersistentModelIndexData::create(const QModelIndex &index) +{ + Q_ASSERT(index.isValid()); // we will _never_ insert an invalid index in the list + QPersistentModelIndexData *d = 0; + QAbstractItemModel *model = const_cast(index.model()); + QHash &indexes = model->d_func()->persistent.indexes; + const QHash::iterator it = indexes.find(index); + if (it != indexes.end()) { + d = (*it); + } else { + d = new QPersistentModelIndexData(index); + indexes.insert(index, d); + } + Q_ASSERT(d); + return d; +} + +void QPersistentModelIndexData::destroy(QPersistentModelIndexData *data) +{ + Q_ASSERT(data); + Q_ASSERT(data->ref == 0); + QAbstractItemModel *model = const_cast(data->model); + // a valid persistent model index with a null model pointer can only happen if the model was destroyed + if (model) { + QAbstractItemModelPrivate *p = model->d_func(); + Q_ASSERT(p); + p->removePersistentIndexData(data); + } + delete data; +} + +/*! + \class QPersistentModelIndex + + \brief The QPersistentModelIndex class is used to locate data in a data model. + + \ingroup model-view + + A QPersistentModelIndex is a model index that can be stored by an + application, and later used to access information in a model. + Unlike the QModelIndex class, it is safe to store a + QPersistentModelIndex since the model will ensure that references + to items will continue to be valid as long as they can be accessed + by the model. + + It is good practice to check that persistent model indexes are valid + before using them. + + \sa {Model/View Programming}, QModelIndex, QAbstractItemModel +*/ + + +/*! + \fn QPersistentModelIndex::QPersistentModelIndex() + + \internal +*/ + +QPersistentModelIndex::QPersistentModelIndex() + : d(0) +{ +} + +/*! + \fn QPersistentModelIndex::QPersistentModelIndex(const QPersistentModelIndex &other) + + Creates a new QPersistentModelIndex that is a copy of the \a other persistent + model index. +*/ + +QPersistentModelIndex::QPersistentModelIndex(const QPersistentModelIndex &other) + : d(other.d) +{ + if (d) d->ref.ref(); +} + +/*! + Creates a new QPersistentModelIndex that is a copy of the model \a index. +*/ + +QPersistentModelIndex::QPersistentModelIndex(const QModelIndex &index) + : d(0) +{ + if (index.isValid()) { + d = QPersistentModelIndexData::create(index); + d->ref.ref(); + } +} + +/*! + \fn QPersistentModelIndex::~QPersistentModelIndex() + + \internal +*/ + +QPersistentModelIndex::~QPersistentModelIndex() +{ + if (d && !d->ref.deref()) { + QPersistentModelIndexData::destroy(d); + d = 0; + } +} + +/*! + Returns true if this persistent model index is equal to the \a other + persistent model index; otherwise returns false. + + All values in the persistent model index are used when comparing + with another persistent model index. +*/ + +bool QPersistentModelIndex::operator==(const QPersistentModelIndex &other) const +{ + if (d && other.d) + return d->index == other.d->index; + return d == other.d; +} + +/*! + \since 4.1 + + Returns true if this persistent model index is smaller than the \a other + persistent model index; otherwise returns false. + + All values in the persistent model index are used when comparing + with another persistent model index. +*/ + +bool QPersistentModelIndex::operator<(const QPersistentModelIndex &other) const +{ + if (d && other.d) + return d->index < other.d->index; + + return d < other.d; +} + +/*! + \fn bool QPersistentModelIndex::operator!=(const QPersistentModelIndex &other) const + \since 4.2 + + Returns true if this persistent model index is not equal to the \a + other persistent model index; otherwise returns false. +*/ + +/*! + Sets the persistent model index to refer to the same item in a model + as the \a other persistent model index. +*/ + +QPersistentModelIndex &QPersistentModelIndex::operator=(const QPersistentModelIndex &other) +{ + if (d == other.d) + return *this; + if (d && !d->ref.deref()) + QPersistentModelIndexData::destroy(d); + d = other.d; + if (d) d->ref.ref(); + return *this; +} + +/*! + Sets the persistent model index to refer to the same item in a model + as the \a other model index. +*/ + +QPersistentModelIndex &QPersistentModelIndex::operator=(const QModelIndex &other) +{ + if (d && !d->ref.deref()) + QPersistentModelIndexData::destroy(d); + if (other.isValid()) { + d = QPersistentModelIndexData::create(other); + if (d) d->ref.ref(); + } else { + d = 0; + } + return *this; +} + +/*! + \fn QPersistentModelIndex::operator const QModelIndex&() const + + Cast operator that returns a const QModelIndex&. +*/ + +QPersistentModelIndex::operator const QModelIndex&() const +{ + static const QModelIndex invalid; + if (d) + return d->index; + return invalid; +} + +/*! + \fn bool QPersistentModelIndex::operator==(const QModelIndex &other) const + + Returns true if this persistent model index refers to the same location as + the \a other model index; otherwise returns false. + + All values in the persistent model index are used when comparing with + another model index. +*/ + +bool QPersistentModelIndex::operator==(const QModelIndex &other) const +{ + if (d) + return d->index == other; + return !other.isValid(); +} + +/*! + \fn bool QPersistentModelIndex::operator!=(const QModelIndex &other) const + + Returns true if this persistent model index does not refer to the same + location as the \a other model index; otherwise returns false. +*/ + +bool QPersistentModelIndex::operator!=(const QModelIndex &other) const +{ + if (d) + return d->index != other; + return other.isValid(); +} + +/*! + \fn int QPersistentModelIndex::row() const + + Returns the row this persistent model index refers to. +*/ + +int QPersistentModelIndex::row() const +{ + if (d) + return d->index.row(); + return -1; +} + +/*! + \fn int QPersistentModelIndex::column() const + + Returns the column this persistent model index refers to. +*/ + +int QPersistentModelIndex::column() const +{ + if (d) + return d->index.column(); + return -1; +} + +/*! + \fn void *QPersistentModelIndex::internalPointer() const + + \internal + + Returns a \c{void} \c{*} pointer used by the model to associate the index with + the internal data structure. +*/ + +void *QPersistentModelIndex::internalPointer() const +{ + if (d) + return d->index.internalPointer(); + return 0; +} + +/*! + \fn void *QPersistentModelIndex::internalId() const + + \internal + + Returns a \c{qint64} used by the model to associate the index with + the internal data structure. +*/ + +qint64 QPersistentModelIndex::internalId() const +{ + if (d) + return d->index.internalId(); + return 0; +} + +/*! + Returns the parent QModelIndex for this persistent index, or an invalid + QModelIndex if it has no parent. + + \sa child() sibling() model() +*/ +QModelIndex QPersistentModelIndex::parent() const +{ + if (d) + return d->index.parent(); + return QModelIndex(); +} + +/*! + Returns the sibling at \a row and \a column or an invalid QModelIndex if + there is no sibling at this position. + + \sa parent() child() +*/ + +QModelIndex QPersistentModelIndex::sibling(int row, int column) const +{ + if (d) + return d->index.sibling(row, column); + return QModelIndex(); +} + +/*! + Returns the child of the model index that is stored in the given \a row + and \a column. + + \sa parent() sibling() +*/ + +QModelIndex QPersistentModelIndex::child(int row, int column) const +{ + if (d) + return d->index.child(row, column); + return QModelIndex(); +} + +/*! + Returns the data for the given \a role for the item referred to by the + index. + + \sa Qt::ItemDataRole, QAbstractItemModel::setData() +*/ +QVariant QPersistentModelIndex::data(int role) const +{ + if (d) + return d->index.data(role); + return QVariant(); +} + +/*! + \since 4.2 + + Returns the flags for the item referred to by the index. +*/ +Qt::ItemFlags QPersistentModelIndex::flags() const +{ + if (d) + return d->index.flags(); + return 0; +} + +/*! + Returns the model that the index belongs to. +*/ +const QAbstractItemModel *QPersistentModelIndex::model() const +{ + if (d) + return d->index.model(); + return 0; +} + +/*! + \fn bool QPersistentModelIndex::isValid() const + + Returns true if this persistent model index is valid; otherwise returns + false. + + A valid index belongs to a model, and has non-negative row and column + numbers. + + \sa model(), row(), column() +*/ + +bool QPersistentModelIndex::isValid() const +{ + return d && d->index.isValid(); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QModelIndex &idx) +{ +#ifndef Q_BROKEN_DEBUG_STREAM + dbg.nospace() << "QModelIndex(" << idx.row() << ',' << idx.column() + << ',' << idx.internalPointer() << ',' << idx.model() << ')'; + return dbg.space(); +#else + qWarning("This compiler doesn't support streaming QModelIndex to QDebug"); + return dbg; + Q_UNUSED(idx); +#endif +} + +QDebug operator<<(QDebug dbg, const QPersistentModelIndex &idx) +{ + if (idx.d) + dbg << idx.d->index; + else + dbg << QModelIndex(); + return dbg; +} +#endif + +class QEmptyItemModel : public QAbstractItemModel +{ +public: + explicit QEmptyItemModel(QObject *parent = 0) : QAbstractItemModel(parent) {} + QModelIndex index(int, int, const QModelIndex &) const { return QModelIndex(); } + QModelIndex parent(const QModelIndex &) const { return QModelIndex(); } + int rowCount(const QModelIndex &) const { return 0; } + int columnCount(const QModelIndex &) const { return 0; } + bool hasChildren(const QModelIndex &) const { return false; } + QVariant data(const QModelIndex &, int) const { return QVariant(); } +}; + +Q_GLOBAL_STATIC(QEmptyItemModel, qEmptyModel) + +QAbstractItemModel *QAbstractItemModelPrivate::staticEmptyModel() +{ + return qEmptyModel(); +} + +namespace { + struct DefaultRoleNames : public QHash + { + DefaultRoleNames() { + (*this)[Qt::DisplayRole] = "display"; + (*this)[Qt::DecorationRole] = "decoration"; + (*this)[Qt::EditRole] = "edit"; + (*this)[Qt::ToolTipRole] = "toolTip"; + (*this)[Qt::StatusTipRole] = "statusTip"; + (*this)[Qt::WhatsThisRole] = "whatsThis"; + } + }; +} + +Q_GLOBAL_STATIC(DefaultRoleNames, qDefaultRoleNames) + +const QHash &QAbstractItemModelPrivate::defaultRoleNames() +{ + return *qDefaultRoleNames(); +} + + +static uint typeOfVariant(const QVariant &value) +{ + //return 0 for integer, 1 for floating point and 2 for other + switch (value.userType()) { + case QVariant::Bool: + case QVariant::Int: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Char: + case QMetaType::Short: + case QMetaType::UShort: + case QMetaType::UChar: + case QMetaType::ULong: + case QMetaType::Long: + return 0; + case QVariant::Double: + case QMetaType::Float: + return 1; + default: + return 2; + } +} + +/*! + \internal + return true if \a value contains a numerical type + + This function is used by our Q{Tree,Widget,Table}WidgetModel classes to sort. +*/ +bool QAbstractItemModelPrivate::variantLessThan(const QVariant &v1, const QVariant &v2) +{ + switch(qMax(typeOfVariant(v1), typeOfVariant(v2))) + { + case 0: //integer type + return v1.toLongLong() < v2.toLongLong(); + case 1: //floating point + return v1.toReal() < v2.toReal(); + default: + return v1.toString().localeAwareCompare(v2.toString()) < 0; + } +} + +void QAbstractItemModelPrivate::removePersistentIndexData(QPersistentModelIndexData *data) +{ + if (data->index.isValid()) { + int removed = persistent.indexes.remove(data->index); + Q_ASSERT_X(removed == 1, "QPersistentModelIndex::~QPersistentModelIndex", + "persistent model indexes corrupted"); //maybe the index was somewhat invalid? + // This assert may happen if the model use changePersistentIndex in a way that could result on two + // QPersistentModelIndex pointing to the same index. + Q_UNUSED(removed); + } + // make sure our optimization still works + for (int i = persistent.moved.count() - 1; i >= 0; --i) { + int idx = persistent.moved[i].indexOf(data); + if (idx >= 0) + persistent.moved[i].remove(idx); + } + // update the references to invalidated persistent indexes + for (int i = persistent.invalidated.count() - 1; i >= 0; --i) { + int idx = persistent.invalidated[i].indexOf(data); + if (idx >= 0) + persistent.invalidated[i].remove(idx); + } + +} + +void QAbstractItemModelPrivate::rowsAboutToBeInserted(const QModelIndex &parent, + int first, int last) +{ + Q_Q(QAbstractItemModel); + Q_UNUSED(last); + QVector persistent_moved; + if (first < q->rowCount(parent)) { + for (QHash::const_iterator it = persistent.indexes.constBegin(); + it != persistent.indexes.constEnd(); ++it) { + QPersistentModelIndexData *data = *it; + const QModelIndex &index = data->index; + if (index.row() >= first && index.isValid() && index.parent() == parent) { + persistent_moved.append(data); + } + } + } + persistent.moved.push(persistent_moved); +} + +void QAbstractItemModelPrivate::rowsInserted(const QModelIndex &parent, + int first, int last) +{ + QVector persistent_moved = persistent.moved.pop(); + int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested + for (QVector::const_iterator it = persistent_moved.constBegin(); + it != persistent_moved.constEnd(); ++it) { + QPersistentModelIndexData *data = *it; + QModelIndex old = data->index; + persistent.indexes.erase(persistent.indexes.find(old)); + data->index = q_func()->index(old.row() + count, old.column(), parent); + if (data->index.isValid()) { + persistent.insertMultiAtEnd(data->index, data); + } else { + qWarning() << "QAbstractItemModel::endInsertRows: Invalid index (" << old.row() + count << ',' << old.column() << ") in model" << q_func(); + } + } +} + +void QAbstractItemModelPrivate::itemsAboutToBeMoved(const QModelIndex &srcParent, int srcFirst, int srcLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation) +{ + QVector persistent_moved_explicitly; + QVector persistent_moved_in_source; + QVector persistent_moved_in_destination; + + QHash::const_iterator it; + const QHash::const_iterator begin = persistent.indexes.constBegin(); + const QHash::const_iterator end = persistent.indexes.constEnd(); + + const bool sameParent = (srcParent == destinationParent); + const bool movingUp = (srcFirst > destinationChild); + + for ( it = begin; it != end; ++it) { + QPersistentModelIndexData *data = *it; + const QModelIndex &index = data->index; + const QModelIndex &parent = index.parent(); + const bool isSourceIndex = (parent == srcParent); + const bool isDestinationIndex = (parent == destinationParent); + + int childPosition; + if (orientation == Qt::Vertical) + childPosition = index.row(); + else + childPosition = index.column(); + + if (!index.isValid() || !(isSourceIndex || isDestinationIndex ) ) + continue; + + if (!sameParent && isDestinationIndex) { + if (childPosition >= destinationChild) + persistent_moved_in_destination.append(data); + continue; + } + + if (sameParent && movingUp && childPosition < destinationChild) + continue; + + if (sameParent && !movingUp && childPosition < srcFirst ) + continue; + + if (!sameParent && childPosition < srcFirst) + continue; + + if (sameParent && (childPosition > srcLast) && (childPosition >= destinationChild )) + continue; + + if ((childPosition <= srcLast) && (childPosition >= srcFirst)) { + persistent_moved_explicitly.append(data); + } else { + persistent_moved_in_source.append(data); + } + } + persistent.moved.push(persistent_moved_explicitly); + persistent.moved.push(persistent_moved_in_source); + persistent.moved.push(persistent_moved_in_destination); +} + +/*! + \internal + + Moves persistent indexes \a indexes by amount \a change. The change will be either a change in row value or a change in + column value depending on the value of \a orientation. The indexes may also be moved to a different parent if \a parent + differs from the existing parent for the index. +*/ +void QAbstractItemModelPrivate::movePersistentIndexes(QVector indexes, int change, const QModelIndex &parent, Qt::Orientation orientation) +{ + QVector::const_iterator it; + const QVector::const_iterator begin = indexes.constBegin(); + const QVector::const_iterator end = indexes.constEnd(); + + for (it = begin; it != end; ++it) + { + QPersistentModelIndexData *data = *it; + + int row = data->index.row(); + int column = data->index.column(); + + if (Qt::Vertical == orientation) + row += change; + else + column += change; + + persistent.indexes.erase(persistent.indexes.find(data->index)); + data->index = q_func()->index(row, column, parent); + if (data->index.isValid()) { + persistent.insertMultiAtEnd(data->index, data); + } else { + qWarning() << "QAbstractItemModel::endMoveRows: Invalid index (" << row << "," << column << ") in model" << q_func(); + } + } +} + +void QAbstractItemModelPrivate::itemsMoved(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation) +{ + QVector moved_in_destination = persistent.moved.pop(); + QVector moved_in_source = persistent.moved.pop(); + QVector moved_explicitly = persistent.moved.pop(); + + const bool sameParent = (sourceParent == destinationParent); + const bool movingUp = (sourceFirst > destinationChild); + + const int explicit_change = (!sameParent || movingUp) ? destinationChild - sourceFirst : destinationChild - sourceLast - 1 ; + const int source_change = (!sameParent || !movingUp) ? -1*(sourceLast - sourceFirst + 1) : sourceLast - sourceFirst + 1 ; + const int destination_change = sourceLast - sourceFirst + 1; + + movePersistentIndexes(moved_explicitly, explicit_change, destinationParent, orientation); + movePersistentIndexes(moved_in_source, source_change, sourceParent, orientation); + movePersistentIndexes(moved_in_destination, destination_change, destinationParent, orientation); +} + +void QAbstractItemModelPrivate::rowsAboutToBeRemoved(const QModelIndex &parent, + int first, int last) +{ + QVector persistent_moved; + QVector persistent_invalidated; + // find the persistent indexes that are affected by the change, either by being in the removed subtree + // or by being on the same level and below the removed rows + for (QHash::const_iterator it = persistent.indexes.constBegin(); + it != persistent.indexes.constEnd(); ++it) { + QPersistentModelIndexData *data = *it; + bool level_changed = false; + QModelIndex current = data->index; + while (current.isValid()) { + QModelIndex current_parent = current.parent(); + if (current_parent == parent) { // on the same level as the change + if (!level_changed && current.row() > last) // below the removed rows + persistent_moved.append(data); + else if (current.row() <= last && current.row() >= first) // in the removed subtree + persistent_invalidated.append(data); + break; + } + current = current_parent; + level_changed = true; + } + } + + persistent.moved.push(persistent_moved); + persistent.invalidated.push(persistent_invalidated); +} + +void QAbstractItemModelPrivate::rowsRemoved(const QModelIndex &parent, + int first, int last) +{ + QVector persistent_moved = persistent.moved.pop(); + int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested + for (QVector::const_iterator it = persistent_moved.constBegin(); + it != persistent_moved.constEnd(); ++it) { + QPersistentModelIndexData *data = *it; + QModelIndex old = data->index; + persistent.indexes.erase(persistent.indexes.find(old)); + data->index = q_func()->index(old.row() - count, old.column(), parent); + if (data->index.isValid()) { + persistent.insertMultiAtEnd(data->index, data); + } else { + qWarning() << "QAbstractItemModel::endRemoveRows: Invalid index (" << old.row() - count << ',' << old.column() << ") in model" << q_func(); + } + } + QVector persistent_invalidated = persistent.invalidated.pop(); + for (QVector::const_iterator it = persistent_invalidated.constBegin(); + it != persistent_invalidated.constEnd(); ++it) { + QPersistentModelIndexData *data = *it; + persistent.indexes.erase(persistent.indexes.find(data->index)); + data->index = QModelIndex(); + data->model = 0; + } +} + +void QAbstractItemModelPrivate::columnsAboutToBeInserted(const QModelIndex &parent, + int first, int last) +{ + Q_Q(QAbstractItemModel); + Q_UNUSED(last); + QVector persistent_moved; + if (first < q->columnCount(parent)) { + for (QHash::const_iterator it = persistent.indexes.constBegin(); + it != persistent.indexes.constEnd(); ++it) { + QPersistentModelIndexData *data = *it; + const QModelIndex &index = data->index; + if (index.column() >= first && index.isValid() && index.parent() == parent) + persistent_moved.append(data); + } + } + persistent.moved.push(persistent_moved); +} + +void QAbstractItemModelPrivate::columnsInserted(const QModelIndex &parent, + int first, int last) +{ + QVector persistent_moved = persistent.moved.pop(); + int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested + for (QVector::const_iterator it = persistent_moved.constBegin(); + it != persistent_moved.constEnd(); ++it) { + QPersistentModelIndexData *data = *it; + QModelIndex old = data->index; + persistent.indexes.erase(persistent.indexes.find(old)); + data->index = q_func()->index(old.row(), old.column() + count, parent); + if (data->index.isValid()) { + persistent.insertMultiAtEnd(data->index, data); + } else { + qWarning() << "QAbstractItemModel::endInsertColumns: Invalid index (" << old.row() << ',' << old.column() + count << ") in model" << q_func(); + } + } +} + +void QAbstractItemModelPrivate::columnsAboutToBeRemoved(const QModelIndex &parent, + int first, int last) +{ + QVector persistent_moved; + QVector persistent_invalidated; + // find the persistent indexes that are affected by the change, either by being in the removed subtree + // or by being on the same level and to the right of the removed columns + for (QHash::const_iterator it = persistent.indexes.constBegin(); + it != persistent.indexes.constEnd(); ++it) { + QPersistentModelIndexData *data = *it; + bool level_changed = false; + QModelIndex current = data->index; + while (current.isValid()) { + QModelIndex current_parent = current.parent(); + if (current_parent == parent) { // on the same level as the change + if (!level_changed && current.column() > last) // right of the removed columns + persistent_moved.append(data); + else if (current.column() <= last && current.column() >= first) // in the removed subtree + persistent_invalidated.append(data); + break; + } + current = current_parent; + level_changed = true; + } + } + + persistent.moved.push(persistent_moved); + persistent.invalidated.push(persistent_invalidated); + +} + +void QAbstractItemModelPrivate::columnsRemoved(const QModelIndex &parent, + int first, int last) +{ + QVector persistent_moved = persistent.moved.pop(); + int count = (last - first) + 1; // it is important to only use the delta, because the change could be nested + for (QVector::const_iterator it = persistent_moved.constBegin(); + it != persistent_moved.constEnd(); ++it) { + QPersistentModelIndexData *data = *it; + QModelIndex old = data->index; + persistent.indexes.erase(persistent.indexes.find(old)); + data->index = q_func()->index(old.row(), old.column() - count, parent); + if (data->index.isValid()) { + persistent.insertMultiAtEnd(data->index, data); + } else { + qWarning() << "QAbstractItemModel::endRemoveColumns: Invalid index (" << old.row() << ',' << old.column() - count << ") in model" << q_func(); + } + } + QVector persistent_invalidated = persistent.invalidated.pop(); + for (QVector::const_iterator it = persistent_invalidated.constBegin(); + it != persistent_invalidated.constEnd(); ++it) { + QPersistentModelIndexData *data = *it; + persistent.indexes.erase(persistent.indexes.find(data->index)); + data->index = QModelIndex(); + data->model = 0; + } +} + +/*! + \class QModelIndex + + \brief The QModelIndex class is used to locate data in a data model. + + \ingroup model-view + + + This class is used as an index into item models derived from + QAbstractItemModel. The index is used by item views, delegates, and + selection models to locate an item in the model. + + New QModelIndex objects are created by the model using the + QAbstractItemModel::createIndex() function. An \e invalid model index can + be constructed with the QModelIndex constructor. Invalid indexes are often + used as parent indexes when referring to top-level items in a model. + + Model indexes refer to items in models, and contain all the information + required to specify their locations in those models. Each index is located + in a given row and column, and may have a parent index; use row(), + column(), and parent() to obtain this information. Each top-level item in a + model is represented by a model index that does not have a parent index - + in this case, parent() will return an invalid model index, equivalent to an + index constructed with the zero argument form of the QModelIndex() + constructor. + + To obtain a model index that refers to an existing item in a model, call + QAbstractItemModel::index() with the required row and column values, and + the model index of the parent. When referring to top-level items in a + model, supply QModelIndex() as the parent index. + + The model() function returns the model that the index references as a + QAbstractItemModel. The child() function is used to examine items held + under the index in the model. The sibling() function allows you to traverse + items in the model on the same level as the index. + + \note Model indexes should be used immediately and then discarded. You + should not rely on indexes to remain valid after calling model functions + that change the structure of the model or delete items. If you need to + keep a model index over time use a QPersistentModelIndex. + + \sa {Model/View Programming}, QPersistentModelIndex, QAbstractItemModel +*/ + +/*! + \fn QModelIndex::QModelIndex() + + Creates a new empty model index. This type of model index is used to + indicate that the position in the model is invalid. + + \sa isValid() QAbstractItemModel +*/ + +/*! + \fn QModelIndex::QModelIndex(int row, int column, void *data, const QAbstractItemModel *model) + + \internal + + Creates a new model index at the given \a row and \a column, + pointing to some \a data. +*/ + +/*! + \fn QModelIndex::QModelIndex(const QModelIndex &other) + + Creates a new model index that is a copy of the \a other model + index. +*/ + +/*! + \fn QModelIndex::~QModelIndex() + + Destroys the model index. +*/ + +/*! + \fn int QModelIndex::row() const + + Returns the row this model index refers to. +*/ + + +/*! + \fn int QModelIndex::column() const + + Returns the column this model index refers to. +*/ + + +/*! + \fn void *QModelIndex::internalPointer() const + + Returns a \c{void} \c{*} pointer used by the model to associate + the index with the internal data structure. + + \sa QAbstractItemModel::createIndex() +*/ + +/*! + \fn void *QModelIndex::internalId() const + + Returns a \c{qint64} used by the model to associate + the index with the internal data structure. + + \sa QAbstractItemModel::createIndex() +*/ + +/*! + \fn bool QModelIndex::isValid() const + + Returns true if this model index is valid; otherwise returns false. + + A valid index belongs to a model, and has non-negative row and column + numbers. + + \sa model(), row(), column() +*/ + +/*! + \fn const QAbstractItemModel *QModelIndex::model() const + + Returns a pointer to the model containing the item that this index + refers to. + + A const pointer to the model is returned because calls to non-const + functions of the model might invalidate the model index and possibly + crash your application. +*/ + +/*! + \fn QModelIndex QModelIndex::sibling(int row, int column) const + + Returns the sibling at \a row and \a column. If there is no sibling at this + position, an invalid QModelIndex is returned. + + \sa parent(), child() +*/ + +/*! + \fn QModelIndex QModelIndex::child(int row, int column) const + + Returns the child of the model index that is stored in the given \a row and + \a column. + + \note This function does not work for an invalid model index which is often + used as the root index. + + \sa parent(), sibling() +*/ + +/*! + \fn QVariant QModelIndex::data(int role) const + + Returns the data for the given \a role for the item referred to by the + index. +*/ + +/*! + \fn Qt::ItemFlags QModelIndex::flags() const + \since 4.2 + + Returns the flags for the item referred to by the index. +*/ + +/*! + \fn bool QModelIndex::operator==(const QModelIndex &other) const + + Returns true if this model index refers to the same location as the + \a other model index; otherwise returns false. + + All values in the model index are used when comparing with another model + index. +*/ + + +/*! + \fn bool QModelIndex::operator!=(const QModelIndex &other) const + + Returns true if this model index does not refer to the same location as + the \a other model index; otherwise returns false. +*/ + + +/*! + \fn QModelIndex QModelIndex::parent() const + + Returns the parent of the model index, or QModelIndex() if it has no + parent. + + \sa child(), sibling(), model() +*/ + +/*! + \class QAbstractItemModel + + \brief The QAbstractItemModel class provides the abstract interface for + item model classes. + + \ingroup model-view + + + The QAbstractItemModel class defines the standard interface that item + models must use to be able to interoperate with other components in the + model/view architecture. It is not supposed to be instantiated directly. + Instead, you should subclass it to create new models. + + The QAbstractItemModel class is one of the \l{Model/View Classes} + and is part of Qt's \l{Model/View Programming}{model/view framework}. + + If you need a model to use with a QListView or a QTableView, you should + consider subclassing QAbstractListModel or QAbstractTableModel instead of + this class. + + The underlying data model is exposed to views and delegates as a hierarchy + of tables. If you do not make use of the hierarchy, then the model is a + simple table of rows and columns. Each item has a unique index specified by + a QModelIndex. + + \image modelindex-no-parent.png + + Every item of data that can be accessed via a model has an associated model + index. You can obtain this model index using the index() function. Each + index may have a sibling() index; child items have a parent() index. + + Each item has a number of data elements associated with it and they can be + retrieved by specifying a role (see \l Qt::ItemDataRole) to the model's + data() function. Data for all available roles can be obtained at the same + time using the itemData() function. + + Data for each role is set using a particular \l Qt::ItemDataRole. Data for + individual roles are set individually with setData(), or they can be set + for all roles with setItemData(). + + Items can be queried with flags() (see \l Qt::ItemFlag) to see if they can + be selected, dragged, or manipulated in other ways. + + If an item has child objects, hasChildren() returns true for the + corresponding index. + + The model has a rowCount() and a columnCount() for each level of the + hierarchy. Rows and columns can be inserted and removed with insertRows(), + insertColumns(), removeRows(), and removeColumns(). + + The model emits signals to indicate changes. For example, dataChanged() is + emitted whenever items of data made available by the model are changed. + Changes to the headers supplied by the model cause headerDataChanged() to + be emitted. If the structure of the underlying data changes, the model can + emit layoutChanged() to indicate to any attached views that they should + redisplay any items shown, taking the new structure into account. + + The items available through the model can be searched for particular data + using the match() function. + + To sort the model, you can use sort(). + + + \section1 Subclassing + + \note Some general guidelines for subclassing models are available in the + \l{Model Subclassing Reference}. + + When subclassing QAbstractItemModel, at the very least you must implement + index(), parent(), rowCount(), columnCount(), and data(). These functions + are used in all read-only models, and form the basis of editable models. + + You can also reimplement hasChildren() to provide special behavior for + models where the implementation of rowCount() is expensive. This makes it + possible for models to restrict the amount of data requested by views, and + can be used as a way to implement lazy population of model data. + + To enable editing in your model, you must also implement setData(), and + reimplement flags() to ensure that \c ItemIsEditable is returned. You can + also reimplement headerData() and setHeaderData() to control the way the + headers for your model are presented. + + The dataChanged() and headerDataChanged() signals must be emitted + explicitly when reimplementing the setData() and setHeaderData() functions, + respectively. + + Custom models need to create model indexes for other components to use. To + do this, call createIndex() with suitable row and column numbers for the + item, and an identifier for it, either as a pointer or as an integer value. + The combination of these values must be unique for each item. Custom models + typically use these unique identifiers in other reimplemented functions to + retrieve item data and access information about the item's parents and + children. See the \l{Simple Tree Model Example} for more information about + unique identifiers. + + It is not necessary to support every role defined in Qt::ItemDataRole. + Depending on the type of data contained within a model, it may only be + useful to implement the data() function to return valid information for + some of the more common roles. Most models provide at least a textual + representation of item data for the Qt::DisplayRole, and well-behaved + models should also provide valid information for the Qt::ToolTipRole and + Qt::WhatsThisRole. Supporting these roles enables models to be used with + standard Qt views. However, for some models that handle highly-specialized + data, it may be appropriate to provide data only for user-defined roles. + + Models that provide interfaces to resizable data structures can provide + implementations of insertRows(), removeRows(), insertColumns(),and + removeColumns(). When implementing these functions, it is important to + notify any connected views about changes to the model's dimensions both + \e before and \e after they occur: + + \list + \o An insertRows() implementation must call beginInsertRows() \e before + inserting new rows into the data structure, and endInsertRows() + \e{immediately afterwards}. + \o An insertColumns() implementation must call beginInsertColumns() + \e before inserting new columns into the data structure, and + endInsertColumns() \e{immediately afterwards}. + \o A removeRows() implementation must call beginRemoveRows() \e before + the rows are removed from the data structure, and endRemoveRows() + \e{immediately afterwards}. + \o A removeColumns() implementation must call beginRemoveColumns() + \e before the columns are removed from the data structure, and + endRemoveColumns() \e{immediately afterwards}. + \endlist + + The \e private signals that these functions emit give attached components + the chance to take action before any data becomes unavailable. The + encapsulation of the insert and remove operations with these begin and end + functions also enables the model to manage \l{QPersistentModelIndex} + {persistent model indexes} correctly. \bold{If you want selections to be + handled properly, you must ensure that you call these functions.} If you + insert or remove an item with children, you do not need to call these + functions for the child items. In other words, the parent item will take + care of its child items. + + To create models that populate incrementally, you can reimplement + fetchMore() and canFetchMore(). If the reimplementation of fetchMore() adds + rows to the model, \l{QAbstractItemModel::}{beginInsertRows()} and + \l{QAbstractItemModel::}{endInsertRows()} must be called. + + \sa {Model Classes}, {Model Subclassing Reference}, QModelIndex, + QAbstractItemView, {Using drag and drop with item views}, + {Simple DOM Model Example}, {Simple Tree Model Example}, + {Editable Tree Model Example}, {Fetch More Example} +*/ + +/*! + \fn QModelIndex QAbstractItemModel::index(int row, int column, const QModelIndex &parent) const = 0 + + Returns the index of the item in the model specified by the given \a row, + \a column and \a parent index. + + When reimplementing this function in a subclass, call createIndex() to + generate model indexes that other components can use to refer to items in + your model. + + \sa createIndex() +*/ + +/*! + \fn bool QAbstractItemModel::insertColumn(int column, const QModelIndex &parent) + + Inserts a single column before the given \a column in the child items of + the \a parent specified. + + Returns true if the column is inserted; otherwise returns false. + + \sa insertColumns() insertRow() removeColumn() +*/ + +/*! + \fn bool QAbstractItemModel::insertRow(int row, const QModelIndex &parent) + + \note The base class implementation of this function does nothing and + returns false. + + Inserts a single row before the given \a row in the child items of the + \a parent specified. + + Returns true if the row is inserted; otherwise returns false. + + \sa insertRows() insertColumn() removeRow() +*/ + +/*! + \fn QObject *QAbstractItemModel::parent() const + \internal +*/ + +/*! + \fn QModelIndex QAbstractItemModel::parent(const QModelIndex &index) const = 0 + + Returns the parent of the model item with the given \a index. If the item + has no parent, an invalid QModelIndex is returned. + + A common convention used in models that expose tree data structures is that + only items in the first column have children. For that case, when + reimplementing this function in a subclass the column of the returned + QModelIndex would be 0. + + When reimplementing this function in a subclass, be careful to avoid + calling QModelIndex member functions, such as QModelIndex::parent(), since + indexes belonging to your model will simply call your implementation, + leading to infinite recursion. + + \sa createIndex() +*/ + +/*! + \fn bool QAbstractItemModel::removeColumn(int column, const QModelIndex &parent) + + Removes the given \a column from the child items of the \a parent + specified. + + Returns true if the column is removed; otherwise returns false. + + \sa removeColumns(), removeRow(), insertColumn() +*/ + +/*! + \fn bool QAbstractItemModel::removeRow(int row, const QModelIndex &parent) + + Removes the given \a row from the child items of the \a parent specified. + + Returns true if the row is removed; otherwise returns false. + + This is a convenience function that calls removeRows(). The + QAbstractItemModel implementation of removeRows() does nothing. + + \sa removeRows(), removeColumn(), insertRow() +*/ + +/*! + \fn void QAbstractItemModel::headerDataChanged(Qt::Orientation orientation, int first, int last) + + This signal is emitted whenever a header is changed. The \a orientation + indicates whether the horizontal or vertical header has changed. The + sections in the header from the \a first to the \a last need to be updated. + + When reimplementing the setHeaderData() function, this signal must be + emitted explicitly. + + If you are changing the number of columns or rows you do not need to emit + this signal, but use the begin/end functions (refer to the section on + subclassing in the QAbstractItemModel class description for details). + + \sa headerData(), setHeaderData(), dataChanged() +*/ + +/*! + \fn void QAbstractItemModel::layoutAboutToBeChanged() + \since 4.2 + + This signal is emitted just before the layout of a model is changed. + Components connected to this signal use it to adapt to changes in the + model's layout. + + Subclasses should update any persistent model indexes after emitting + layoutAboutToBeChanged(). + + \sa layoutChanged(), changePersistentIndex() +*/ + +/*! + \fn void QAbstractItemModel::layoutChanged() + + This signal is emitted whenever the layout of items exposed by the model + has changed; for example, when the model has been sorted. When this signal + is received by a view, it should update the layout of items to reflect this + change. + + When subclassing QAbstractItemModel or QAbstractProxyModel, ensure that you + emit layoutAboutToBeChanged() before changing the order of items or + altering the structure of the data you expose to views, and emit + layoutChanged() after changing the layout. + + Subclasses should update any persistent model indexes before emitting + layoutChanged(). In other words, when the structure changes: + + \list + \o emit layoutAboutToBeChanged + \o Remember the QModelIndex that will change + \o Update your internal data + \o Call changePersistentIndex() + \o emit layoutChanged + \endlist + + \sa layoutAboutToBeChanged(), dataChanged(), headerDataChanged(), modelReset(), + changePersistentIndex() +*/ + +/*! + Constructs an abstract item model with the given \a parent. +*/ +QAbstractItemModel::QAbstractItemModel(QObject *parent) + : QObject(*new QAbstractItemModelPrivate, parent) +{ +} + +/*! + \internal +*/ +QAbstractItemModel::QAbstractItemModel(QAbstractItemModelPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + Destroys the abstract item model. +*/ +QAbstractItemModel::~QAbstractItemModel() +{ + d_func()->invalidatePersistentIndexes(); +} + +/*! + \fn QModelIndex QAbstractItemModel::sibling(int row, int column, const QModelIndex &index) const + + Returns the sibling at \a row and \a column for the item at \a index, or an + invalid QModelIndex if there is no sibling at that location. + + sibling() is just a convenience function that finds the item's parent, and + uses it to retrieve the index of the child item in the specified \a row and + \a column. + + \sa index(), QModelIndex::row(), QModelIndex::column() +*/ + + +/*! + \fn int QAbstractItemModel::rowCount(const QModelIndex &parent) const + + Returns the number of rows under the given \a parent. When the parent is + valid it means that rowCount is returning the number of children of parent. + + \note When implementing a table based model, rowCount() should return 0 + when the parent is valid. + + \sa columnCount() +*/ + +/*! + \fn int QAbstractItemModel::columnCount(const QModelIndex &parent) const + + Returns the number of columns for the children of the given \a parent. + + In most subclasses, the number of columns is independent of the \a parent. + + For example: + + \snippet examples/itemviews/simpledommodel/dommodel.cpp 2 + + \note When implementing a table based model, columnCount() should return 0 + when the parent is valid. + + \sa rowCount() +*/ + +/*! + \fn void QAbstractItemModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) + + This signal is emitted whenever the data in an existing item changes. + + If the items are of the same parent, the affected ones are those between + \a topLeft and \a bottomRight inclusive. If the items do not have the same + parent, the behavior is undefined. + + When reimplementing the setData() function, this signal must be emitted + explicitly. + + \sa headerDataChanged(), setData(), layoutChanged() +*/ + +/*! + \fn void QAbstractItemModel::rowsInserted(const QModelIndex &parent, int start, int end) + + This signal is emitted after rows have been inserted into the + model. The new items are those between \a start and \a end + inclusive, under the given \a parent item. + + \note Components connected to this signal use it to adapt to changes in the + model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa insertRows(), beginInsertRows() +*/ + +/*! + \fn void QAbstractItemModel::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) + + This signal is emitted just before rows are inserted into the model. The + new items will be positioned between \a start and \a end inclusive, under + the given \a parent item. + + \note Components connected to this signal use it to adapt to changes + in the model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa insertRows(), beginInsertRows() +*/ + +/*! + \fn void QAbstractItemModel::rowsRemoved(const QModelIndex &parent, int start, int end) + + This signal is emitted after rows have been removed from the model. The + removed items are those between \a start and \a end inclusive, under the + given \a parent item. + + \note Components connected to this signal use it to adapt to changes + in the model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa removeRows(), beginRemoveRows() +*/ + +/*! + \fn void QAbstractItemModel::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) + + This signal is emitted just before rows are removed from the model. The + items that will be removed are those between \a start and \a end inclusive, + under the given \a parent item. + + \note Components connected to this signal use it to adapt to changes + in the model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa removeRows(), beginRemoveRows() +*/ + +/*! + \fn void QAbstractItemModel::rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) + \since 4.6 + + This signal is emitted after rows have been moved within the + model. The items between \a sourceStart and \a sourceEnd + inclusive, under the given \a sourceParent item have been moved to \a destinationParent + starting at the row \a destinationRow. + + \bold{Note:} Components connected to this signal use it to adapt to changes + in the model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa beginMoveRows() +*/ + +/*! + \fn void QAbstractItemModel::rowsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) + \since 4.6 + + This signal is emitted just before rows are moved within the + model. The items that will be moved are those between \a sourceStart and \a sourceEnd + inclusive, under the given \a sourceParent item. They will be moved to \a destinationParent + starting at the row \a destinationRow. + + \bold{Note:} Components connected to this signal use it to adapt to changes + in the model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa beginMoveRows() +*/ + +/*! + \fn void QAbstractItemModel::columnsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn) + \since 4.6 + + This signal is emitted after columns have been moved within the + model. The items between \a sourceStart and \a sourceEnd + inclusive, under the given \a sourceParent item have been moved to \a destinationParent + starting at the column \a destinationColumn. + + \bold{Note:} Components connected to this signal use it to adapt to changes + in the model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa beginMoveRows() +*/ + +/*! + \fn void QAbstractItemModel::columnsAboutToBeMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn) + \since 4.6 + + This signal is emitted just before columns are moved within the + model. The items that will be moved are those between \a sourceStart and \a sourceEnd + inclusive, under the given \a sourceParent item. They will be moved to \a destinationParent + starting at the column \a destinationColumn. + + \bold{Note:} Components connected to this signal use it to adapt to changes + in the model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa beginMoveRows() +*/ + +/*! + \fn void QAbstractItemModel::columnsInserted(const QModelIndex &parent, int start, int end) + + This signal is emitted after columns have been inserted into the model. The + new items are those between \a start and \a end inclusive, under the given + \a parent item. + + \note Components connected to this signal use it to adapt to changes in the + model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa insertColumns(), beginInsertColumns() +*/ + +/*! + \fn void QAbstractItemModel::columnsAboutToBeInserted(const QModelIndex &parent, int start, int end) + + This signal is emitted just before columns are inserted into the model. The + new items will be positioned between \a start and \a end inclusive, under + the given \a parent item. + + \note Components connected to this signal use it to adapt to changes in the + model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa insertColumns(), beginInsertColumns() +*/ + +/*! + \fn void QAbstractItemModel::columnsRemoved(const QModelIndex &parent, int start, int end) + + This signal is emitted after columns have been removed from the model. + The removed items are those between \a start and \a end inclusive, + under the given \a parent item. + + \note Components connected to this signal use it to adapt to changes in + the model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa removeColumns(), beginRemoveColumns() +*/ + +/*! + \fn void QAbstractItemModel::columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end) + + This signal is emitted just before columns are removed from the model. The + items to be removed are those between \a start and \a end inclusive, under + the given \a parent item. + + \note Components connected to this signal use it to adapt to changes in the + model's dimensions. It can only be emitted by the QAbstractItemModel + implementation, and cannot be explicitly emitted in subclass code. + + \sa removeColumns(), beginRemoveColumns() +*/ + +/*! + Returns true if the model returns a valid QModelIndex for \a row and + \a column with \a parent, otherwise returns false. +*/ +bool QAbstractItemModel::hasIndex(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 || column < 0) + return false; + return row < rowCount(parent) && column < columnCount(parent); +} + + +/*! + Returns true if \a parent has any children; otherwise returns false. + + Use rowCount() on the parent to find out the number of children. + + \sa parent() index() +*/ +bool QAbstractItemModel::hasChildren(const QModelIndex &parent) const +{ + return (rowCount(parent) > 0) && (columnCount(parent) > 0); +} + + +/*! + Returns a map with values for all predefined roles in the model for the + item at the given \a index. + + Reimplement this function if you want to extend the default behavior of + this function to include custom roles in the map. + + \sa Qt::ItemDataRole, data() +*/ +QMap QAbstractItemModel::itemData(const QModelIndex &index) const +{ + QMap roles; + for (int i = 0; i < Qt::UserRole; ++i) { + QVariant variantData = data(index, i); + if (variantData.isValid()) + roles.insert(i, variantData); + } + return roles; +} + +/*! + Sets the \a role data for the item at \a index to \a value. + + Returns true if successful; otherwise returns false. + + The dataChanged() signal should be emitted if the data was successfully + set. + + The base class implementation returns false. This function and data() must + be reimplemented for editable models. + + \sa Qt::ItemDataRole, data(), itemData() +*/ +bool QAbstractItemModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + Q_UNUSED(index); + Q_UNUSED(value); + Q_UNUSED(role); + return false; +} + +/*! + \fn QVariant QAbstractItemModel::data(const QModelIndex &index, int role) const = 0 + + Returns the data stored under the given \a role for the item referred to + by the \a index. + + \note If you do not have a value to return, return an \bold invalid + QVariant instead of returning 0. + + \sa Qt::ItemDataRole, setData(), headerData() +*/ + +/*! + Sets the role data for the item at \a index to the associated value in + \a roles, for every Qt::ItemDataRole. + + Returns true if successful; otherwise returns false. + + Roles that are not in \a roles will not be modified. + + \sa setData() data() itemData() +*/ +bool QAbstractItemModel::setItemData(const QModelIndex &index, const QMap &roles) +{ + bool b = true; + for (QMap::ConstIterator it = roles.begin(); it != roles.end(); ++it) + b = b && setData(index, it.value(), it.key()); + return b; +} + +/*! + Returns a list of MIME types that can be used to describe a list of model + indexes. + + \sa mimeData() +*/ +QStringList QAbstractItemModel::mimeTypes() const +{ + QStringList types; + types << QLatin1String("application/x-qabstractitemmodeldatalist"); + return types; +} + +/*! + Returns an object that contains serialized items of data corresponding to + the list of \a indexes specified. The formats used to describe the encoded + data is obtained from the mimeTypes() function. + + If the list of indexes is empty, or there are no supported MIME types, 0 is + returned rather than a serialized empty list. + + \sa mimeTypes(), dropMimeData() +*/ +QMimeData *QAbstractItemModel::mimeData(const QModelIndexList &indexes) const +{ + if (indexes.count() <= 0) + return 0; + QStringList types = mimeTypes(); + if (types.isEmpty()) + return 0; + QMimeData *data = new QMimeData(); + QString format = types.at(0); + QByteArray encoded; + QDataStream stream(&encoded, QIODevice::WriteOnly); + encodeData(indexes, stream); + data->setData(format, encoded); + return data; +} + +/*! + Handles the \a data supplied by a drag and drop operation that ended with + the given \a action. + + Returns true if the data and action can be handled by the model; otherwise + returns false. + + Although the specified \a row, \a column and \a parent indicate the + location of an item in the model where the operation ended, it is the + responsibility of the view to provide a suitable location for where the + data should be inserted. + + For instance, a drop action on an item in a QTreeView can result in new + items either being inserted as children of the item specified by \a row, + \a column, and \a parent, or as siblings of the item. + + When row and column are -1 it means that it is up to the model to decide + where to place the data. This can occur in a tree when data is dropped on + a parent. Models will usually append the data to the parent in this case. + + \sa supportedDropActions(), {Using drag and drop with item views} +*/ +bool QAbstractItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + // check if the action is supported + if (!data || !(action == Qt::CopyAction || action == Qt::MoveAction)) + return false; + // check if the format is supported + QStringList types = mimeTypes(); + if (types.isEmpty()) + return false; + QString format = types.at(0); + if (!data->hasFormat(format)) + return false; + if (row > rowCount(parent)) + row = rowCount(parent); + if (row == -1) + row = rowCount(parent); + if (column == -1) + column = 0; + // decode and insert + QByteArray encoded = data->data(format); + QDataStream stream(&encoded, QIODevice::ReadOnly); + return decodeData(row, column, parent, stream); +} + +/*! + \since 4.2 + + Returns the drop actions supported by this model. + + The default implementation returns Qt::CopyAction. Reimplement this + function if you wish to support additional actions. You must also + reimplement the dropMimeData() function to handle the additional + operations. + + \sa dropMimeData(), Qt::DropActions, {Using drag and drop with item + views} +*/ +Qt::DropActions QAbstractItemModel::supportedDropActions() const +{ + return Qt::CopyAction; +} + +/*! + Returns the actions supported by the data in this model. + + The default implementation returns supportedDropActions() unless specific + values have been set with setSupportedDragActions(). + + supportedDragActions() is used by QAbstractItemView::startDrag() as the + default values when a drag occurs. + + \sa Qt::DropActions, {Using drag and drop with item views} +*/ +Qt::DropActions QAbstractItemModel::supportedDragActions() const +{ + // ### Qt 5: make this virtual or these properties + Q_D(const QAbstractItemModel); + if (d->supportedDragActions != -1) + return d->supportedDragActions; + return supportedDropActions(); +} + +/*! + \since 4.2 + + Sets the supported drag \a actions for the items in the model. + + \sa supportedDragActions(), {Using drag and drop with item views} +*/ +void QAbstractItemModel::setSupportedDragActions(Qt::DropActions actions) +{ + Q_D(QAbstractItemModel); + d->supportedDragActions = actions; +} + +/*! + \note The base class implementation of this function does nothing and + returns false. + + On models that support this, inserts \a count rows into the model before + the given \a row. Items in the new row will be children of the item + represented by the \a parent model index. + + If \a row is 0, the rows are prepended to any existing rows in the parent. + + If \a row is rowCount(), the rows are appended to any existing rows in the + parent. + + If \a parent has no children, a single column with \a count rows is + inserted. + + Returns true if the rows were successfully inserted; otherwise returns + false. + + If you implement your own model, you can reimplement this function if you + want to support insertions. Alternatively, you can provide your own API for + altering the data. In either case, you will need to call + beginInsertRows() and endInsertRows() to notify other components that the + model has changed. + + \sa insertColumns(), removeRows(), beginInsertRows(), endInsertRows() +*/ +bool QAbstractItemModel::insertRows(int, int, const QModelIndex &) +{ + return false; +} + +/*! + On models that support this, inserts \a count new columns into the model + before the given \a column. The items in each new column will be children + of the item represented by the \a parent model index. + + If \a column is 0, the columns are prepended to any existing columns. + + If \a column is columnCount(), the columns are appended to any existing + columns. + + If \a parent has no children, a single row with \a count columns is + inserted. + + Returns true if the columns were successfully inserted; otherwise returns + false. + + The base class implementation does nothing and returns false. + + If you implement your own model, you can reimplement this function if you + want to support insertions. Alternatively, you can provide your own API for + altering the data. + + \sa insertRows(), removeColumns(), beginInsertColumns(), endInsertColumns() +*/ +bool QAbstractItemModel::insertColumns(int, int, const QModelIndex &) +{ + return false; +} + +/*! + On models that support this, removes \a count rows starting with the given + \a row under parent \a parent from the model. + + Returns true if the rows were successfully removed; otherwise returns + false. + + The base class implementation does nothing and returns false. + + If you implement your own model, you can reimplement this function if you + want to support removing. Alternatively, you can provide your own API for + altering the data. + + \sa removeRow(), removeColumns(), insertColumns(), beginRemoveRows(), + endRemoveRows() +*/ +bool QAbstractItemModel::removeRows(int, int, const QModelIndex &) +{ + return false; +} + +/*! + On models that support this, removes \a count columns starting with the + given \a column under parent \a parent from the model. + + Returns true if the columns were successfully removed; otherwise returns + false. + + The base class implementation does nothing and returns false. + + If you implement your own model, you can reimplement this function if you + want to support removing. Alternatively, you can provide your own API for + altering the data. + + \sa removeColumn(), removeRows(), insertColumns(), beginRemoveColumns(), + endRemoveColumns() +*/ +bool QAbstractItemModel::removeColumns(int, int, const QModelIndex &) +{ + return false; +} + +/*! + Fetches any available data for the items with the parent specified by the + \a parent index. + + Reimplement this if you are populating your model incrementally. + + The default implementation does nothing. + + \sa canFetchMore() +*/ +void QAbstractItemModel::fetchMore(const QModelIndex &) +{ + // do nothing +} + +/*! + Returns true if there is more data available for \a parent; otherwise + returns false. + + The default implementation always returns false. + + If canFetchMore() returns true, QAbstractItemView will call fetchMore(). + However, the fetchMore() function is only called when the model is being + populated incrementally. + + \sa fetchMore() +*/ +bool QAbstractItemModel::canFetchMore(const QModelIndex &) const +{ + return false; +} + +/*! + Returns the item flags for the given \a index. + + The base class implementation returns a combination of flags that enables + the item (\c ItemIsEnabled) and allows it to be selected + (\c ItemIsSelectable). + + \sa Qt::ItemFlags +*/ +Qt::ItemFlags QAbstractItemModel::flags(const QModelIndex &index) const +{ + Q_D(const QAbstractItemModel); + if (!d->indexValid(index)) + return 0; + + return Qt::ItemIsSelectable|Qt::ItemIsEnabled; +} + +/*! + Sorts the model by \a column in the given \a order. + + The base class implementation does nothing. +*/ +void QAbstractItemModel::sort(int column, Qt::SortOrder order) +{ + Q_UNUSED(column); + Q_UNUSED(order); + // do nothing +} + +/*! + Returns a model index for the buddy of the item represented by \a index. + When the user wants to edit an item, the view will call this function to + check whether another item in the model should be edited instead. Then, the + view will construct a delegate using the model index returned by the buddy + item. + + The default implementation of this function has each item as its own buddy. +*/ +QModelIndex QAbstractItemModel::buddy(const QModelIndex &index) const +{ + return index; +} + +/*! + Returns a list of indexes for the items in the column of the \a start index + where data stored under the given \a role matches the specified \a value. + The way the search is performed is defined by the \a flags given. The list + that is returned may be empty. + + The search begins from the \a start index, and continues until the number + of matching data items equals \a hits, the search reaches the last row, or + the search reaches \a start again - depending on whether \c MatchWrap is + specified in \a flags. If you want to search for all matching items, use + \a hits = -1. + + By default, this function will perform a wrapping, string-based comparison + on all items, searching for items that begin with the search term specified + by \a value. + + \note The default implementation of this function only searches columns. + Reimplement this function to include a different search behavior. +*/ +QModelIndexList QAbstractItemModel::match(const QModelIndex &start, int role, + const QVariant &value, int hits, + Qt::MatchFlags flags) const +{ + QModelIndexList result; + uint matchType = flags & 0x0F; + Qt::CaseSensitivity cs = flags & Qt::MatchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; + bool recurse = flags & Qt::MatchRecursive; + bool wrap = flags & Qt::MatchWrap; + bool allHits = (hits == -1); + QString text; // only convert to a string if it is needed + QModelIndex p = parent(start); + int from = start.row(); + int to = rowCount(p); + + // iterates twice if wrapping + for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) { + for (int r = from; (r < to) && (allHits || result.count() < hits); ++r) { + QModelIndex idx = index(r, start.column(), p); + if (!idx.isValid()) + continue; + QVariant v = data(idx, role); + // QVariant based matching + if (matchType == Qt::MatchExactly) { + if (value == v) + result.append(idx); + } else { // QString based matching + if (text.isEmpty()) // lazy conversion + text = value.toString(); + QString t = v.toString(); + switch (matchType) { + case Qt::MatchRegExp: + if (QRegExp(text, cs).exactMatch(t)) + result.append(idx); + break; + case Qt::MatchWildcard: + if (QRegExp(text, cs, QRegExp::Wildcard).exactMatch(t)) + result.append(idx); + break; + case Qt::MatchStartsWith: + if (t.startsWith(text, cs)) + result.append(idx); + break; + case Qt::MatchEndsWith: + if (t.endsWith(text, cs)) + result.append(idx); + break; + case Qt::MatchFixedString: + if (t.compare(text, cs) == 0) + result.append(idx); + break; + case Qt::MatchContains: + default: + if (t.contains(text, cs)) + result.append(idx); + } + } + if (recurse && hasChildren(idx)) { // search the hierarchy + result += match(index(0, idx.column(), idx), role, + (text.isEmpty() ? value : text), + (allHits ? -1 : hits - result.count()), flags); + } + } + // prepare for the next iteration + from = 0; + to = start.row(); + } + return result; +} + +/*! + Returns the row and column span of the item represented by \a index. + + \note Currently, span is not used. +*/ + +QSize QAbstractItemModel::span(const QModelIndex &) const +{ + return QSize(1, 1); +} + +/*! + \since 4.6 + + Sets the model's role names to \a roleNames. + + This function allows mapping of role identifiers to role property names in + Declarative UI. This function must be called before the model is used. + Modifying the role names after the model has been set may result in + undefined behaviour. + + \sa roleNames() +*/ +void QAbstractItemModel::setRoleNames(const QHash &roleNames) +{ + Q_D(QAbstractItemModel); + d->roleNames = roleNames; +} + +/*! + \since 4.6 + + Returns the model's role names. + + \sa setRoleNames() +*/ +const QHash &QAbstractItemModel::roleNames() const +{ + Q_D(const QAbstractItemModel); + return d->roleNames; +} + +/*! + Lets the model know that it should submit cached information to permanent + storage. This function is typically used for row editing. + + Returns true if there is no error; otherwise returns false. + + \sa revert() +*/ + +bool QAbstractItemModel::submit() +{ + return true; +} + +/*! + Lets the model know that it should discard cached information. This + function is typically used for row editing. + + \sa submit() +*/ + +void QAbstractItemModel::revert() +{ + // do nothing +} + +/*! + Returns the data for the given \a role and \a section in the header with + the specified \a orientation. + + For horizontal headers, the section number corresponds to the column + number. Similarly, for vertical headers, the section number corresponds to + the row number. + + \sa Qt::ItemDataRole, setHeaderData(), QHeaderView +*/ + +QVariant QAbstractItemModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(orientation); + if (role == Qt::DisplayRole) + return section + 1; + return QVariant(); +} + +/*! + Sets the data for the given \a role and \a section in the header with the + specified \a orientation to the \a value supplied. + + Returns true if the header's data was updated; otherwise returns false. + + When reimplementing this function, the headerDataChanged() signal must be + emitted explicitly. + + \sa Qt::ItemDataRole, headerData() +*/ + +bool QAbstractItemModel::setHeaderData(int section, Qt::Orientation orientation, + const QVariant &value, int role) +{ + Q_UNUSED(section); + Q_UNUSED(orientation); + Q_UNUSED(value); + Q_UNUSED(role); + return false; +} + +/*! + \fn QModelIndex QAbstractItemModel::createIndex(int row, int column, void *ptr) const + + Creates a model index for the given \a row and \a column with the internal + pointer \a ptr. + + When using a QSortFilterProxyModel, its indexes have their own internal + pointer. It is not advisable to access this internal pointer outside of the + model. Use the data() function instead. + + This function provides a consistent interface that model subclasses must + use to create model indexes. +*/ + +/*! + \fn QModelIndex QAbstractItemModel::createIndex(int row, int column, int id) const + \obsolete + + Use QModelIndex + QAbstractItemModel::createIndex(int row, int column, quint32 id) instead. +*/ + +/*! + \fn QModelIndex QAbstractItemModel::createIndex(int row, int column, quint32 id) const + + Creates a model index for the given \a row and \a column with the internal + identifier, \a id. + + This function provides a consistent interface that model subclasses must + use to create model indexes. + + \sa QModelIndex::internalId() +*/ + +/*! + \internal +*/ +void QAbstractItemModel::encodeData(const QModelIndexList &indexes, QDataStream &stream) const +{ + QModelIndexList::ConstIterator it = indexes.begin(); + for (; it != indexes.end(); ++it) + stream << (*it).row() << (*it).column() << itemData(*it); +} + +/*! + \internal + */ +bool QAbstractItemModel::decodeData(int row, int column, const QModelIndex &parent, + QDataStream &stream) +{ + int top = INT_MAX; + int left = INT_MAX; + int bottom = 0; + int right = 0; + QVector rows, columns; + QVector > data; + + while (!stream.atEnd()) { + int r, c; + QMap v; + stream >> r >> c >> v; + rows.append(r); + columns.append(c); + data.append(v); + top = qMin(r, top); + left = qMin(c, left); + bottom = qMax(r, bottom); + right = qMax(c, right); + } + + // insert the dragged items into the table, use a bit array to avoid overwriting items, + // since items from different tables can have the same row and column + int dragRowCount = 0; + int dragColumnCount = right - left + 1; + + // Compute the number of continuous rows upon insertion and modify the rows to match + QVector rowsToInsert(bottom + 1); + for (int i = 0; i < rows.count(); ++i) + rowsToInsert[rows.at(i)] = 1; + for (int i = 0; i < rowsToInsert.count(); ++i) { + if (rowsToInsert[i] == 1){ + rowsToInsert[i] = dragRowCount; + ++dragRowCount; + } + } + for (int i = 0; i < rows.count(); ++i) + rows[i] = top + rowsToInsert[rows[i]]; + + QBitArray isWrittenTo(dragRowCount * dragColumnCount); + + // make space in the table for the dropped data + int colCount = columnCount(parent); + if (colCount == 0) { + insertColumns(colCount, dragColumnCount - colCount, parent); + colCount = columnCount(parent); + } + insertRows(row, dragRowCount, parent); + + row = qMax(0, row); + column = qMax(0, column); + + QVector newIndexes(data.size()); + // set the data in the table + for (int j = 0; j < data.size(); ++j) { + int relativeRow = rows.at(j) - top; + int relativeColumn = columns.at(j) - left; + int destinationRow = relativeRow + row; + int destinationColumn = relativeColumn + column; + int flat = (relativeRow * dragColumnCount) + relativeColumn; + // if the item was already written to, or we just can't fit it in the table, create a new row + if (destinationColumn >= colCount || isWrittenTo.testBit(flat)) { + destinationColumn = qBound(column, destinationColumn, colCount - 1); + destinationRow = row + dragRowCount; + insertRows(row + dragRowCount, 1, parent); + flat = (dragRowCount * dragColumnCount) + relativeColumn; + isWrittenTo.resize(++dragRowCount * dragColumnCount); + } + if (!isWrittenTo.testBit(flat)) { + newIndexes[j] = index(destinationRow, destinationColumn, parent); + isWrittenTo.setBit(flat); + } + } + + for(int k = 0; k < newIndexes.size(); k++) { + if (newIndexes.at(k).isValid()) + setItemData(newIndexes.at(k), data.at(k)); + } + + return true; +} + +/*! + Begins a row insertion operation. + + When reimplementing insertRows() in a subclass, you must call this function + \e before inserting data into the model's underlying data store. + + The \a parent index corresponds to the parent into which the new rows are + inserted; \a first and \a last are the row numbers that the new rows will + have after they have been inserted. + + \table 80% + \row + \o \inlineimage modelview-begin-insert-rows.png Inserting rows + \o Specify the first and last row numbers for the span of rows you + want to insert into an item in a model. + + For example, as shown in the diagram, we insert three rows before + row 2, so \a first is 2 and \a last is 4: + + \snippet doc/src/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp 0 + + This inserts the three new rows as rows 2, 3, and 4. + \row + \o \inlineimage modelview-begin-append-rows.png Appending rows + \o To append rows, insert them after the last row. + + For example, as shown in the diagram, we append two rows to a + collection of 4 existing rows (ending in row 3), so \a first is 4 + and \a last is 5: + + \snippet doc/src/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp 1 + + This appends the two new rows as rows 4 and 5. + \endtable + + \note This function emits the rowsAboutToBeInserted() signal which + connected views (or proxies) must handle before the data is inserted. + Otherwise, the views may end up in an invalid state. + \sa endInsertRows() +*/ +void QAbstractItemModel::beginInsertRows(const QModelIndex &parent, int first, int last) +{ + Q_ASSERT(first >= 0); + Q_ASSERT(last >= first); + Q_D(QAbstractItemModel); + d->changes.push(QAbstractItemModelPrivate::Change(parent, first, last)); + emit rowsAboutToBeInserted(parent, first, last); + d->rowsAboutToBeInserted(parent, first, last); +} + +/*! + Ends a row insertion operation. + + When reimplementing insertRows() in a subclass, you must call this function + \e after inserting data into the model's underlying data store. + + \sa beginInsertRows() +*/ +void QAbstractItemModel::endInsertRows() +{ + Q_D(QAbstractItemModel); + QAbstractItemModelPrivate::Change change = d->changes.pop(); + d->rowsInserted(change.parent, change.first, change.last); + emit rowsInserted(change.parent, change.first, change.last); +} + +/*! + Begins a row removal operation. + + When reimplementing removeRows() in a subclass, you must call this + function \e before removing data from the model's underlying data store. + + The \a parent index corresponds to the parent from which the new rows are + removed; \a first and \a last are the row numbers of the rows to be + removed. + + \table 80% + \row + \o \inlineimage modelview-begin-remove-rows.png Removing rows + \o Specify the first and last row numbers for the span of rows you + want to remove from an item in a model. + + For example, as shown in the diagram, we remove the two rows from + row 2 to row 3, so \a first is 2 and \a last is 3: + + \snippet doc/src/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp 2 + \endtable + + \note This function emits the rowsAboutToBeRemoved() signal which connected + views (or proxies) must handle before the data is removed. Otherwise, the + views may end up in an invalid state. + + \sa endRemoveRows() +*/ +void QAbstractItemModel::beginRemoveRows(const QModelIndex &parent, int first, int last) +{ + Q_ASSERT(first >= 0); + Q_ASSERT(last >= first); + Q_D(QAbstractItemModel); + d->changes.push(QAbstractItemModelPrivate::Change(parent, first, last)); + emit rowsAboutToBeRemoved(parent, first, last); + d->rowsAboutToBeRemoved(parent, first, last); +} + +/*! + Ends a row removal operation. + + When reimplementing removeRows() in a subclass, you must call this function + \e after removing data from the model's underlying data store. + + \sa beginRemoveRows() +*/ +void QAbstractItemModel::endRemoveRows() +{ + Q_D(QAbstractItemModel); + QAbstractItemModelPrivate::Change change = d->changes.pop(); + d->rowsRemoved(change.parent, change.first, change.last); + emit rowsRemoved(change.parent, change.first, change.last); +} + +/*! + Returns whether a move operation is valid. + + A move operation is not allowed if it moves a continuous range of rows to a destination within + itself, or if it attempts to move a row to one of its own descendants. + + \internal +*/ +bool QAbstractItemModelPrivate::allowMove(const QModelIndex &srcParent, int start, int end, const QModelIndex &destinationParent, int destinationStart, Qt::Orientation orientation) +{ + // Don't move the range within itself. + if (destinationParent == srcParent) + return !(destinationStart >= start && destinationStart <= end + 1); + + QModelIndex destinationAncestor = destinationParent; + int pos = (Qt::Vertical == orientation) ? destinationAncestor.row() : destinationAncestor.column(); + forever { + if (destinationAncestor == srcParent) { + if (pos >= start && pos <= end) + return false; + break; + } + + if (!destinationAncestor.isValid()) + break; + + pos = (Qt::Vertical == orientation) ? destinationAncestor.row() : destinationAncestor.column(); + destinationAncestor = destinationAncestor.parent(); + } + + return true; +} + +/*! + \since 4.6 + + Begins a row move operation. + + When reimplementing a subclass, this method simplifies moving + entities in your model. This method is responsible for moving + persistent indexes in the model, which you would otherwise be + required to do yourself. Using beginMoveRows and endMoveRows + is an alternative to emitting layoutAboutToBeChanged and + layoutChanged directly along with changePersistentIndexes. + layoutAboutToBeChanged is emitted by this method for compatibility + reasons. + + The \a sourceParent index corresponds to the parent from which the + rows are moved; \a sourceFirst and \a sourceLast are the first and last + row numbers of the rows to be moved. The \a destinationParent index + corresponds to the parent into which those rows are moved. The \a + destinationChild is the row to which the rows will be moved. That + is, the index at row \a sourceFirst in \a sourceParent will become + row \a destinationChild in \a destinationParent, followed by all other + rows up to \a sourceLast. + + However, when moving rows down in the same parent (\a sourceParent + and \a destinationParent are equal), the rows will be placed before the + \a destinationChild index. That is, if you wish to move rows 0 and 1 so + they will become rows 1 and 2, \a destinationChild should be 3. In this + case, the new index for the source row \c i (which is between + \a sourceFirst and \a sourceLast) is equal to + \c {(destinationChild-sourceLast-1+i)}. + + Note that if \a sourceParent and \a destinationParent are the same, + you must ensure that the \a destinationChild is not within the range + of \a sourceFirst and \a sourceLast + 1. You must also ensure that you + do not attempt to move a row to one of its own children or ancestors. + This method returns false if either condition is true, in which case you + should abort your move operation. + + \table 80% + \row + \o \inlineimage modelview-move-rows-1.png Moving rows to another parent + \o Specify the first and last row numbers for the span of rows in + the source parent you want to move in the model. Also specify + the row in the destination parent to move the span to. + + For example, as shown in the diagram, we move three rows from + row 2 to 4 in the source, so \a sourceFirst is 2 and \a sourceLast is 4. + We move those items to above row 2 in the destination, so \a destinationChild is 2. + + \snippet doc/src/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp 6 + + This moves the three rows rows 2, 3, and 4 in the source to become 2, 3 and 4 in + the destination. Other affected siblings are displaced accordingly. + \row + \o \inlineimage modelview-move-rows-2.png Moving rows to append to another parent + \o To append rows to another parent, move them to after the last row. + + For example, as shown in the diagram, we move three rows to a + collection of 6 existing rows (ending in row 5), so \a destinationChild is 6: + + \snippet doc/src/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp 7 + + This moves the target rows to the end of the target parent as 6, 7 and 8. + \row + \o \inlineimage modelview-move-rows-3.png Moving rows in the same parent up + \o To move rows within the same parent, specify the row to move them to. + + For example, as shown in the diagram, we move one item from row 2 to row 0, + so \a sourceFirst and \a sourceLast are 2 and \a destinationChild is 0. + + \snippet doc/src/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp 8 + + Note that other rows may be displaced accordingly. Note also that when moving + items within the same parent you should not attempt invalid or no-op moves. In + the above example, item 2 is at row 2 before the move, so it can not be moved + to row 2 (where it is already) or row 3 (no-op as row 3 means above row 3, where + it is already) + + \row + \o \inlineimage modelview-move-rows-4.png Moving rows in the same parent down + \o To move rows within the same parent, specify the row to move them to. + + For example, as shown in the diagram, we move one item from row 2 to row 4, + so \a sourceFirst and \a sourceLast are 2 and \a destinationChild is 4. + + \snippet doc/src/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp 9 + + Note that other rows may be displaced accordingly. + \endtable + + \sa endMoveRows() +*/ +bool QAbstractItemModel::beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationChild) +{ + Q_ASSERT(sourceFirst >= 0); + Q_ASSERT(sourceLast >= sourceFirst); + Q_ASSERT(destinationChild >= 0); + Q_D(QAbstractItemModel); + + if (!d->allowMove(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Vertical)) { + return false; + } + + QAbstractItemModelPrivate::Change sourceChange(sourceParent, sourceFirst, sourceLast); + sourceChange.needsAdjust = sourceParent.isValid() && sourceParent.row() >= destinationChild && sourceParent.parent() == destinationParent; + d->changes.push(sourceChange); + int destinationLast = destinationChild + (sourceLast - sourceFirst); + QAbstractItemModelPrivate::Change destinationChange(destinationParent, destinationChild, destinationLast); + destinationChange.needsAdjust = destinationParent.isValid() && destinationParent.row() >= sourceLast && destinationParent.parent() == sourceParent; + d->changes.push(destinationChange); + + emit rowsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild); + emit layoutAboutToBeChanged(); + d->itemsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Vertical); + return true; +} + +/*! + Ends a row move operation. + + When implementing a subclass, you must call this + function \e after moving data within the model's underlying data + store. + + layoutChanged is emitted by this method for compatibility reasons. + + \sa beginMoveRows() + + \since 4.6 +*/ +void QAbstractItemModel::endMoveRows() +{ + Q_D(QAbstractItemModel); + + QAbstractItemModelPrivate::Change insertChange = d->changes.pop(); + QAbstractItemModelPrivate::Change removeChange = d->changes.pop(); + + QModelIndex adjustedSource = removeChange.parent; + QModelIndex adjustedDestination = insertChange.parent; + + const int numMoved = removeChange.last - removeChange.first + 1; + if (insertChange.needsAdjust) + adjustedDestination = createIndex(adjustedDestination.row() - numMoved, adjustedDestination.column(), adjustedDestination.internalPointer()); + + if (removeChange.needsAdjust) + adjustedSource = createIndex(adjustedSource.row() + numMoved, adjustedSource.column(), adjustedSource.internalPointer()); + + d->itemsMoved(adjustedSource, removeChange.first, removeChange.last, adjustedDestination, insertChange.first, Qt::Vertical); + + emit rowsMoved(adjustedSource, removeChange.first, removeChange.last, adjustedDestination, insertChange.first); + emit layoutChanged(); +} + +/*! + Begins a column insertion operation. + + When reimplementing insertColumns() in a subclass, you must call this + function \e before inserting data into the model's underlying data store. + + The \a parent index corresponds to the parent into which the new columns + are inserted; \a first and \a last are the column numbers of the new + columns will have after they have been inserted. + + \table 80% + \row + \o \inlineimage modelview-begin-insert-columns.png Inserting columns + \o Specify the first and last column numbers for the span of columns + you want to insert into an item in a model. + + For example, as shown in the diagram, we insert three columns + before column 4, so \a first is 4 and \a last is 6: + + \snippet doc/src/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp 3 + + This inserts the three new columns as columns 4, 5, and 6. + \row + \o \inlineimage modelview-begin-append-columns.png Appending columns + \o To append columns, insert them after the last column. + + For example, as shown in the diagram, we append three columns to a + collection of six existing columns (ending in column 5), so + \a first is 6 and \a last is 8: + + \snippet doc/src/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp 4 + + This appends the two new columns as columns 6, 7, and 8. + \endtable + + \note This function emits the columnsAboutToBeInserted() signal which + connected views (or proxies) must handle before the data is inserted. + Otherwise, the views may end up in an invalid state. + + \sa endInsertColumns() +*/ +void QAbstractItemModel::beginInsertColumns(const QModelIndex &parent, int first, int last) +{ + Q_ASSERT(first >= 0); + Q_ASSERT(last >= first); + Q_D(QAbstractItemModel); + d->changes.push(QAbstractItemModelPrivate::Change(parent, first, last)); + emit columnsAboutToBeInserted(parent, first, last); + d->columnsAboutToBeInserted(parent, first, last); +} + +/*! + Ends a column insertion operation. + + When reimplementing insertColumns() in a subclass, you must call this + function \e after inserting data into the model's underlying data + store. + + \sa beginInsertColumns() +*/ +void QAbstractItemModel::endInsertColumns() +{ + Q_D(QAbstractItemModel); + QAbstractItemModelPrivate::Change change = d->changes.pop(); + d->columnsInserted(change.parent, change.first, change.last); + emit columnsInserted(change.parent, change.first, change.last); +} + +/*! + Begins a column removal operation. + + When reimplementing removeColumns() in a subclass, you must call this + function \e before removing data from the model's underlying data store. + + The \a parent index corresponds to the parent from which the new columns + are removed; \a first and \a last are the column numbers of the first and + last columns to be removed. + + \table 80% + \row + \o \inlineimage modelview-begin-remove-columns.png Removing columns + \o Specify the first and last column numbers for the span of columns + you want to remove from an item in a model. + + For example, as shown in the diagram, we remove the three columns + from column 4 to column 6, so \a first is 4 and \a last is 6: + + \snippet doc/src/snippets/code/src_corelib_kernel_qabstractitemmodel.cpp 5 + \endtable + + \note This function emits the columnsAboutToBeRemoved() signal which + connected views (or proxies) must handle before the data is removed. + Otherwise, the views may end up in an invalid state. + + \sa endRemoveColumns() +*/ +void QAbstractItemModel::beginRemoveColumns(const QModelIndex &parent, int first, int last) +{ + Q_ASSERT(first >= 0); + Q_ASSERT(last >= first); + Q_D(QAbstractItemModel); + d->changes.push(QAbstractItemModelPrivate::Change(parent, first, last)); + emit columnsAboutToBeRemoved(parent, first, last); + d->columnsAboutToBeRemoved(parent, first, last); +} + +/*! + Ends a column removal operation. + + When reimplementing removeColumns() in a subclass, you must call this + function \e after removing data from the model's underlying data store. + + \sa beginRemoveColumns() +*/ +void QAbstractItemModel::endRemoveColumns() +{ + Q_D(QAbstractItemModel); + QAbstractItemModelPrivate::Change change = d->changes.pop(); + d->columnsRemoved(change.parent, change.first, change.last); + emit columnsRemoved(change.parent, change.first, change.last); +} + +/*! + Begins a column move operation. + + When reimplementing a subclass, this method simplifies moving + entities in your model. This method is responsible for moving + persistent indexes in the model, which you would otherwise be + required to do yourself. Using beginMoveRows and endMoveRows + is an alternative to emitting layoutAboutToBeChanged and + layoutChanged directly along with changePersistentIndexes. + layoutAboutToBeChanged is emitted by this method for compatibility + reasons. + + The \a sourceParent index corresponds to the parent from which the + columns are moved; \a sourceFirst and \a sourceLast are the first and last + column numbers of the columns to be moved. The \a destinationParent index + corresponds to the parent into which those columns are moved. The \a + destinationChild is the column to which the columns will be moved. That + is, the index at column \a sourceFirst in \a sourceParent will become + column \a destinationChild in \a destinationParent, followed by all other + columns up to \a sourceLast. + + However, when moving columns down in the same parent (\a sourceParent + and \a destinationParent are equal), the columnss will be placed before the + \a destinationChild index. That is, if you wish to move columns 0 and 1 so + they will become columns 1 and 2, \a destinationChild should be 3. In this + case, the new index for the source column \c i (which is between + \a sourceFirst and \a sourceLast) is equal to + \c {(destinationChild-sourceLast-1+i)}. + + Note that if \a sourceParent and \a destinationParent are the same, + you must ensure that the \a destinationChild is not within the range + of \a sourceFirst and \a sourceLast + 1. You must also ensure that you + do not attempt to move a column to one of its own children or ancestors. + This method returns false if either condition is true, in which case you + should abort your move operation. + + \sa endMoveColumns() + + \since 4.6 +*/ +bool QAbstractItemModel::beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationChild) +{ + Q_ASSERT(sourceFirst >= 0); + Q_ASSERT(sourceLast >= sourceFirst); + Q_ASSERT(destinationChild >= 0); + Q_D(QAbstractItemModel); + + if (!d->allowMove(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Horizontal)) { + return false; + } + + QAbstractItemModelPrivate::Change sourceChange(sourceParent, sourceFirst, sourceLast); + sourceChange.needsAdjust = sourceParent.isValid() && sourceParent.row() >= destinationChild && sourceParent.parent() == destinationParent; + d->changes.push(sourceChange); + int destinationLast = destinationChild + (sourceLast - sourceFirst); + QAbstractItemModelPrivate::Change destinationChange(destinationParent, destinationChild, destinationLast); + destinationChange.needsAdjust = destinationParent.isValid() && destinationParent.row() >= sourceLast && destinationParent.parent() == sourceParent; + d->changes.push(destinationChange); + + d->itemsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild, Qt::Horizontal); + + emit columnsAboutToBeMoved(sourceParent, sourceFirst, sourceLast, destinationParent, destinationChild); + emit layoutAboutToBeChanged(); + return true; +} + +/*! + Ends a column move operation. + + When implementing a subclass, you must call this + function \e after moving data within the model's underlying data + store. + + layoutChanged is emitted by this method for compatibility reasons. + + \sa beginMoveColumns() + + \since 4.6 +*/ +void QAbstractItemModel::endMoveColumns() +{ + Q_D(QAbstractItemModel); + + QAbstractItemModelPrivate::Change insertChange = d->changes.pop(); + QAbstractItemModelPrivate::Change removeChange = d->changes.pop(); + + QModelIndex adjustedSource = removeChange.parent; + QModelIndex adjustedDestination = insertChange.parent; + + const int numMoved = removeChange.last - removeChange.first + 1; + if (insertChange.needsAdjust) + adjustedDestination = createIndex(adjustedDestination.row(), adjustedDestination.column() - numMoved, adjustedDestination.internalPointer()); + + if (removeChange.needsAdjust) + adjustedSource = createIndex(adjustedSource.row(), adjustedSource.column() + numMoved, adjustedSource.internalPointer()); + + d->itemsMoved(adjustedSource, removeChange.first, removeChange.last, adjustedDestination, insertChange.first, Qt::Horizontal); + + emit columnsMoved(adjustedSource, removeChange.first, removeChange.last, adjustedDestination, insertChange.first); + emit layoutChanged(); +} + +/*! + Resets the model to its original state in any attached views. + + \note Use beginResetModel() and endResetModel() instead whenever possible. + Use this method only if there is no way to call beginResetModel() before invalidating the model. + Otherwise it could lead to unexpected behaviour, especially when used with proxy models. +*/ +void QAbstractItemModel::reset() +{ + Q_D(QAbstractItemModel); + emit modelAboutToBeReset(); + d->invalidatePersistentIndexes(); + emit modelReset(); +} + +/*! + Begins a model reset operation. + + A reset operation resets the model to its current state in any attached views. + + \note Any views attached to this model will be reset as well. + + When a model is reset it means that any previous data reported from the + model is now invalid and has to be queried for again. This also means that + the current item and any selected items will become invalid. + + When a model radically changes its data it can sometimes be easier to just + call this function rather than emit dataChanged() to inform other + components when the underlying data source, or its structure, has changed. + + You must call this function before resetting any internal data structures in your model + or proxy model. + + \sa modelAboutToBeReset(), modelReset(), endResetModel() + \since 4.6 +*/ +void QAbstractItemModel::beginResetModel() +{ + emit modelAboutToBeReset(); +} + +/*! + Completes a model reset operation. + + You must call this function after resetting any internal data structure in your model + or proxy model. + + \sa beginResetModel() + \since 4.6 +*/ +void QAbstractItemModel::endResetModel() +{ + Q_D(QAbstractItemModel); + d->invalidatePersistentIndexes(); + emit modelReset(); +} + +/*! + Changes the QPersistentModelIndex that is equal to the given \a from model + index to the given \a to model index. + + If no persistent model index equal to the given \a from model index was + found, nothing is changed. + + \sa persistentIndexList(), changePersistentIndexList() +*/ +void QAbstractItemModel::changePersistentIndex(const QModelIndex &from, const QModelIndex &to) +{ + Q_D(QAbstractItemModel); + if (d->persistent.indexes.isEmpty()) + return; + // find the data and reinsert it sorted + const QHash::iterator it = d->persistent.indexes.find(from); + if (it != d->persistent.indexes.end()) { + QPersistentModelIndexData *data = *it; + d->persistent.indexes.erase(it); + data->index = to; + if (to.isValid()) + d->persistent.insertMultiAtEnd(to, data); + else + data->model = 0; + } +} + +/*! + \since 4.1 + + Changes the QPersistentModelIndexes that is equal to the indexes in the + given \a from model index list to the given \a to model index list. + + If no persistent model indexes equal to the indexes in the given \a from + model index list was found, nothing is changed. + + \sa persistentIndexList(), changePersistentIndex() +*/ +void QAbstractItemModel::changePersistentIndexList(const QModelIndexList &from, + const QModelIndexList &to) +{ + Q_D(QAbstractItemModel); + if (d->persistent.indexes.isEmpty()) + return; + QVector toBeReinserted; + toBeReinserted.reserve(to.count()); + for (int i = 0; i < from.count(); ++i) { + if (from.at(i) == to.at(i)) + continue; + const QHash::iterator it = d->persistent.indexes.find(from.at(i)); + if (it != d->persistent.indexes.end()) { + QPersistentModelIndexData *data = *it; + d->persistent.indexes.erase(it); + data->index = to.at(i); + if (data->index.isValid()) + toBeReinserted << data; + else + data->model = 0; + } + } + + for (QVector::const_iterator it = toBeReinserted.constBegin(); + it != toBeReinserted.constEnd() ; ++it) { + QPersistentModelIndexData *data = *it; + d->persistent.insertMultiAtEnd(data->index, data); + } +} + +/*! + \since 4.2 + + Returns the list of indexes stored as persistent indexes in the model. +*/ +QModelIndexList QAbstractItemModel::persistentIndexList() const +{ + Q_D(const QAbstractItemModel); + QModelIndexList result; + for (QHash::const_iterator it = d->persistent.indexes.constBegin(); + it != d->persistent.indexes.constEnd(); ++it) { + QPersistentModelIndexData *data = *it; + result.append(data->index); + } + return result; +} + + +/*! + \class QAbstractTableModel + \brief The QAbstractTableModel class provides an abstract model that can be + subclassed to create table models. + + \ingroup model-view + + QAbstractTableModel provides a standard interface for models that represent + their data as a two-dimensional array of items. It is not used directly, + but must be subclassed. + + Since the model provides a more specialized interface than + QAbstractItemModel, it is not suitable for use with tree views, although it + can be used to provide data to a QListView. If you need to represent a + simple list of items, and only need a model to contain a single column of + data, subclassing the QAbstractListModel may be more appropriate. + + The rowCount() and columnCount() functions return the dimensions of the + table. To retrieve a model index corresponding to an item in the model, use + index() and provide only the row and column numbers. + + \section1 Subclassing + + When subclassing QAbstractTableModel, you must implement rowCount(), + columnCount(), and data(). Default implementations of the index() and + parent() functions are provided by QAbstractTableModel. + Well behaved models will also implement headerData(). + + Editable models need to implement setData(), and implement flags() to + return a value containing + \l{Qt::ItemFlags}{Qt::ItemIsEditable}. + + Models that provide interfaces to resizable data structures can + provide implementations of insertRows(), removeRows(), insertColumns(), + and removeColumns(). When implementing these functions, it is + important to call the appropriate functions so that all connected views + are aware of any changes: + + \list + \o An insertRows() implementation must call beginInsertRows() + \e before inserting new rows into the data structure, and it must + call endInsertRows() \e{immediately afterwards}. + \o An insertColumns() implementation must call beginInsertColumns() + \e before inserting new columns into the data structure, and it must + call endInsertColumns() \e{immediately afterwards}. + \o A removeRows() implementation must call beginRemoveRows() + \e before the rows are removed from the data structure, and it must + call endRemoveRows() \e{immediately afterwards}. + \o A removeColumns() implementation must call beginRemoveColumns() + \e before the columns are removed from the data structure, and it must + call endRemoveColumns() \e{immediately afterwards}. + \endlist + + \note Some general guidelines for subclassing models are available in the + \l{Model Subclassing Reference}. + + \note + + \sa {Model Classes}, QAbstractItemModel, QAbstractListModel, + {Pixelator Example} +*/ + +/*! + Constructs an abstract table model for the given \a parent. +*/ + +QAbstractTableModel::QAbstractTableModel(QObject *parent) + : QAbstractItemModel(parent) +{ + +} + +/*! + \internal + + Constructs an abstract table model with \a dd and the given \a parent. +*/ + +QAbstractTableModel::QAbstractTableModel(QAbstractItemModelPrivate &dd, QObject *parent) + : QAbstractItemModel(dd, parent) +{ + +} + +/*! + Destroys the abstract table model. +*/ + +QAbstractTableModel::~QAbstractTableModel() +{ + +} + +/*! + \fn QModelIndex QAbstractTableModel::index(int row, int column, const QModelIndex &parent = QModelIndex()) const + + Returns the index of the data in \a row and \a column with \a parent. + + \sa parent() +*/ + +QModelIndex QAbstractTableModel::index(int row, int column, const QModelIndex &parent) const +{ + return hasIndex(row, column, parent) ? createIndex(row, column, 0) : QModelIndex(); +} + +/*! + \fn QModelIndex QAbstractTableModel::parent(const QModelIndex &index) const + + Returns the parent of the model item with the given \a index. + + \sa index() hasChildren() +*/ + +QModelIndex QAbstractTableModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +bool QAbstractTableModel::hasChildren(const QModelIndex &parent) const +{ + if (parent.model() == this || !parent.isValid()) + return rowCount(parent) > 0 && columnCount(parent) > 0; + return false; +} + +/*! + \class QAbstractListModel + \brief The QAbstractListModel class provides an abstract model that can be + subclassed to create one-dimensional list models. + + \ingroup model-view + + QAbstractListModel provides a standard interface for models that represent + their data as a simple non-hierarchical sequence of items. It is not used + directly, but must be subclassed. + + Since the model provides a more specialized interface than + QAbstractItemModel, it is not suitable for use with tree views; you will + need to subclass QAbstractItemModel if you want to provide a model for + that purpose. If you need to use a number of list models to manage data, + it may be more appropriate to subclass QAbstractTableModel class instead. + + Simple models can be created by subclassing this class and implementing + the minimum number of required functions. For example, we could implement + a simple read-only QStringList-based model that provides a list of strings + to a QListView widget. In such a case, we only need to implement the + rowCount() function to return the number of items in the list, and the + data() function to retrieve items from the list. + + Since the model represents a one-dimensional structure, the rowCount() + function returns the total number of items in the model. The columnCount() + function is implemented for interoperability with all kinds of views, but + by default informs views that the model contains only one column. + + \section1 Subclassing + + When subclassing QAbstractListModel, you must provide implementations + of the rowCount() and data() functions. Well behaved models also provide + a headerData() implementation. + + For editable list models, you must also provide an implementation of + setData(), implement the flags() function so that it returns a value + containing \l{Qt::ItemFlags}{Qt::ItemIsEditable}. + + Note that QAbstractListModel provides a default implementation of + columnCount() that informs views that there is only a single column + of items in this model. + + Models that provide interfaces to resizable list-like data structures + can provide implementations of insertRows() and removeRows(). When + implementing these functions, it is important to call the appropriate + functions so that all connected views are aware of any changes: + + \list + \o An insertRows() implementation must call beginInsertRows() + \e before inserting new rows into the data structure, and it must + call endInsertRows() \e{immediately afterwards}. + \o A removeRows() implementation must call beginRemoveRows() + \e before the rows are removed from the data structure, and it must + call endRemoveRows() \e{immediately afterwards}. + \endlist + + \note Some general guidelines for subclassing models are available in the + \l{Model Subclassing Reference}. + + \sa {Model Classes}, {Model Subclassing Reference}, QAbstractItemView, + QAbstractTableModel, {Item Views Puzzle Example} +*/ + +/*! + Constructs an abstract list model with the given \a parent. +*/ + +QAbstractListModel::QAbstractListModel(QObject *parent) + : QAbstractItemModel(parent) +{ + +} + +/*! + \internal + + Constructs an abstract list model with \a dd and the given \a parent. +*/ + +QAbstractListModel::QAbstractListModel(QAbstractItemModelPrivate &dd, QObject *parent) + : QAbstractItemModel(dd, parent) +{ + +} + +/*! + Destroys the abstract list model. +*/ + +QAbstractListModel::~QAbstractListModel() +{ + +} + +/*! + \fn QModelIndex QAbstractListModel::index(int row, int column, const QModelIndex &parent = QModelIndex()) const + + Returns the index of the data in \a row and \a column with \a parent. + + \sa parent() +*/ + +QModelIndex QAbstractListModel::index(int row, int column, const QModelIndex &parent) const +{ + return hasIndex(row, column, parent) ? createIndex(row, column, 0) : QModelIndex(); +} + +/*! + Returns the parent of the model item with the given \a index. + + \sa index() hasChildren() +*/ + +QModelIndex QAbstractListModel::parent(const QModelIndex & /* index */) const +{ + return QModelIndex(); +} + +/*! + \internal + + Returns the number of columns in the list with the given \a parent. + + \sa rowCount() +*/ + +int QAbstractListModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 1; +} + +bool QAbstractListModel::hasChildren(const QModelIndex &parent) const +{ + return parent.isValid() ? false : (rowCount() > 0); +} + +/*! + \typedef QModelIndexList + \relates QModelIndex + + Synonym for QList. +*/ + +/*! + \reimp +*/ +bool QAbstractTableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + if (!data || !(action == Qt::CopyAction || action == Qt::MoveAction)) + return false; + + QStringList types = mimeTypes(); + if (types.isEmpty()) + return false; + QString format = types.at(0); + if (!data->hasFormat(format)) + return false; + + QByteArray encoded = data->data(format); + QDataStream stream(&encoded, QIODevice::ReadOnly); + + // if the drop is on an item, replace the data in the items + if (parent.isValid() && row == -1 && column == -1) { + int top = INT_MAX; + int left = INT_MAX; + QVector rows, columns; + QVector > data; + + while (!stream.atEnd()) { + int r, c; + QMap v; + stream >> r >> c >> v; + rows.append(r); + columns.append(c); + data.append(v); + top = qMin(r, top); + left = qMin(c, left); + } + + for (int i = 0; i < data.size(); ++i) { + int r = (rows.at(i) - top) + parent.row(); + int c = (columns.at(i) - left) + parent.column(); + if (hasIndex(r, c)) + setItemData(index(r, c), data.at(i)); + } + + return true; + } + + // otherwise insert new rows for the data + return decodeData(row, column, parent, stream); +} + +/*! + \reimp +*/ +bool QAbstractListModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + if (!data || !(action == Qt::CopyAction || action == Qt::MoveAction)) + return false; + + QStringList types = mimeTypes(); + if (types.isEmpty()) + return false; + QString format = types.at(0); + if (!data->hasFormat(format)) + return false; + + QByteArray encoded = data->data(format); + QDataStream stream(&encoded, QIODevice::ReadOnly); + + // if the drop is on an item, replace the data in the items + if (parent.isValid() && row == -1 && column == -1) { + int top = INT_MAX; + int left = INT_MAX; + QVector rows, columns; + QVector > data; + + while (!stream.atEnd()) { + int r, c; + QMap v; + stream >> r >> c >> v; + rows.append(r); + columns.append(c); + data.append(v); + top = qMin(r, top); + left = qMin(c, left); + } + + for (int i = 0; i < data.size(); ++i) { + int r = (rows.at(i) - top) + parent.row(); + if (columns.at(i) == left && hasIndex(r, 0)) + setItemData(index(r), data.at(i)); + } + + return true; + } + + if (row == -1) + row = rowCount(parent); + + // otherwise insert new rows for the data + return decodeData(row, column, parent, stream); +} + +/*! + \fn QAbstractItemModel::modelAboutToBeReset() + \since 4.2 + + This signal is emitted when reset() is called, before the model's internal + state (e.g. persistent model indexes) has been invalidated. + + \sa beginResetModel(), modelReset() +*/ + +/*! + \fn QAbstractItemModel::modelReset() + \since 4.1 + + This signal is emitted when reset() is called, after the model's internal + state (e.g. persistent model indexes) has been invalidated. + + \sa endResetModel(), modelAboutToBeReset() +*/ + +/*! + \fn bool QModelIndex::operator<(const QModelIndex &other) const + \since 4.1 + + Returns true if this model index is smaller than the \a other + model index; otherwise returns false. +*/ + +/*! + \fn uint qHash(const QPersistentModelIndex &index) + \since 4.5 + + Returns a hash of the QPersistentModelIndex + */ + + +/*! + \internal + QHash::insertMulti insert the value before the old value. and find() return the new value. + We need insertMultiAtEnd because we don't want to overwrite the old one, which should be removed later + + There should be only one instance QPersistentModelIndexData per index, but in some intermediate state there may be + severals of PersistantModelIndex pointing to the same index, but one is already updated, and the other one is not. + This make sure than when updating the first one we don't overwrite the second one in the hash, and the second one + will be updated right later. + */ +void QAbstractItemModelPrivate::Persistent::insertMultiAtEnd(const QModelIndex& key, QPersistentModelIndexData *data) +{ + QHash::iterator newIt = + indexes.insertMulti(key, data); + QHash::iterator it = newIt + 1; + while (it != indexes.end() && it.key() == key) { + qSwap(*newIt,*it); + newIt = it; + ++it; + } +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qabstractitemmodel.h b/src/corelib/kernel/qabstractitemmodel.h new file mode 100644 index 0000000000..6de3bf5e4f --- /dev/null +++ b/src/corelib/kernel/qabstractitemmodel.h @@ -0,0 +1,410 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QABSTRACTITEMMODEL_H +#define QABSTRACTITEMMODEL_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QAbstractItemModel; +class QPersistentModelIndex; + +class Q_CORE_EXPORT QModelIndex +{ + friend class QAbstractItemModel; + friend class QProxyModel; +public: + inline QModelIndex() : r(-1), c(-1), p(0), m(0) {} + inline QModelIndex(const QModelIndex &other) + : r(other.r), c(other.c), p(other.p), m(other.m) {} + inline ~QModelIndex() { p = 0; m = 0; } + inline int row() const { return r; } + inline int column() const { return c; } + inline void *internalPointer() const { return p; } + inline qint64 internalId() const { return reinterpret_cast(p); } + inline QModelIndex parent() const; + inline QModelIndex sibling(int row, int column) const; + inline QModelIndex child(int row, int column) const; + inline QVariant data(int role = Qt::DisplayRole) const; + inline Qt::ItemFlags flags() const; + inline const QAbstractItemModel *model() const { return m; } + inline bool isValid() const { return (r >= 0) && (c >= 0) && (m != 0); } + inline bool operator==(const QModelIndex &other) const + { return (other.r == r) && (other.p == p) && (other.c == c) && (other.m == m); } + inline bool operator!=(const QModelIndex &other) const + { return !(*this == other); } + inline bool operator<(const QModelIndex &other) const + { + if (r < other.r) return true; + if (r == other.r) { + if (c < other.c) return true; + if (c == other.c) { + if (p < other.p) return true; + if (p == other.p) return m < other.m; + } + } + return false; } +private: + inline QModelIndex(int row, int column, void *ptr, const QAbstractItemModel *model); + int r, c; + void *p; + const QAbstractItemModel *m; +}; +Q_DECLARE_TYPEINFO(QModelIndex, Q_MOVABLE_TYPE); + +#ifndef QT_NO_DEBUG_STREAM +Q_CORE_EXPORT QDebug operator<<(QDebug, const QModelIndex &); +#endif + +class QPersistentModelIndexData; + +class Q_CORE_EXPORT QPersistentModelIndex +{ +public: + QPersistentModelIndex(); + QPersistentModelIndex(const QModelIndex &index); + QPersistentModelIndex(const QPersistentModelIndex &other); + ~QPersistentModelIndex(); + bool operator<(const QPersistentModelIndex &other) const; + bool operator==(const QPersistentModelIndex &other) const; + inline bool operator!=(const QPersistentModelIndex &other) const + { return !operator==(other); } + QPersistentModelIndex &operator=(const QPersistentModelIndex &other); + bool operator==(const QModelIndex &other) const; + bool operator!=(const QModelIndex &other) const; + QPersistentModelIndex &operator=(const QModelIndex &other); + operator const QModelIndex&() const; + int row() const; + int column() const; + void *internalPointer() const; + qint64 internalId() const; + QModelIndex parent() const; + QModelIndex sibling(int row, int column) const; + QModelIndex child(int row, int column) const; + QVariant data(int role = Qt::DisplayRole) const; + Qt::ItemFlags flags() const; + const QAbstractItemModel *model() const; + bool isValid() const; +private: + QPersistentModelIndexData *d; + friend uint qHash(const QPersistentModelIndex &); +#ifndef QT_NO_DEBUG_STREAM + friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QPersistentModelIndex &); +#endif +}; +Q_DECLARE_TYPEINFO(QPersistentModelIndex, Q_MOVABLE_TYPE); + +inline uint qHash(const QPersistentModelIndex &index) +{ return qHash(index.d); } + + +#ifndef QT_NO_DEBUG_STREAM +Q_CORE_EXPORT QDebug operator<<(QDebug, const QPersistentModelIndex &); +#endif + +template class QList; +typedef QList QModelIndexList; + +class QMimeData; +class QAbstractItemModelPrivate; +template class QMap; + + +class Q_CORE_EXPORT QAbstractItemModel : public QObject +{ + Q_OBJECT + + friend class QPersistentModelIndexData; + friend class QAbstractItemViewPrivate; +public: + + explicit QAbstractItemModel(QObject *parent = 0); + virtual ~QAbstractItemModel(); + + bool hasIndex(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const = 0; + virtual QModelIndex parent(const QModelIndex &child) const = 0; + + inline QModelIndex sibling(int row, int column, const QModelIndex &idx) const + { return index(row, column, parent(idx)); } + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const = 0; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0; + virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const; + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const = 0; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, + int role = Qt::EditRole); + + virtual QMap itemData(const QModelIndex &index) const; + virtual bool setItemData(const QModelIndex &index, const QMap &roles); + + virtual QStringList mimeTypes() const; + virtual QMimeData *mimeData(const QModelIndexList &indexes) const; + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent); + virtual Qt::DropActions supportedDropActions() const; + + Qt::DropActions supportedDragActions() const; + void setSupportedDragActions(Qt::DropActions); + + virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); + virtual bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()); + virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + virtual bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()); + + inline bool insertRow(int row, const QModelIndex &parent = QModelIndex()); + inline bool insertColumn(int column, const QModelIndex &parent = QModelIndex()); + inline bool removeRow(int row, const QModelIndex &parent = QModelIndex()); + inline bool removeColumn(int column, const QModelIndex &parent = QModelIndex()); + + virtual void fetchMore(const QModelIndex &parent); + virtual bool canFetchMore(const QModelIndex &parent) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + virtual QModelIndex buddy(const QModelIndex &index) const; + virtual QModelIndexList match(const QModelIndex &start, int role, + const QVariant &value, int hits = 1, + Qt::MatchFlags flags = + Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const; + virtual QSize span(const QModelIndex &index) const; + + const QHash &roleNames() const; + +#ifdef Q_NO_USING_KEYWORD + inline QObject *parent() const { return QObject::parent(); } +#else + using QObject::parent; +#endif + +Q_SIGNALS: + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void headerDataChanged(Qt::Orientation orientation, int first, int last); + void layoutChanged(); + void layoutAboutToBeChanged(); + +#if !defined(Q_MOC_RUN) && !defined(qdoc) +private: // can only be emitted by QAbstractItemModel +#endif + void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last); + void rowsInserted(const QModelIndex &parent, int first, int last); + + void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last); + void rowsRemoved(const QModelIndex &parent, int first, int last); + + void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last); + void columnsInserted(const QModelIndex &parent, int first, int last); + + void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last); + void columnsRemoved(const QModelIndex &parent, int first, int last); + + void modelAboutToBeReset(); + void modelReset(); + + void rowsAboutToBeMoved( const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow ); + void rowsMoved( const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row ); + + void columnsAboutToBeMoved( const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationColumn ); + void columnsMoved( const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column ); + + +public Q_SLOTS: + virtual bool submit(); + virtual void revert(); + +protected: + QAbstractItemModel(QAbstractItemModelPrivate &dd, QObject *parent = 0); + + inline QModelIndex createIndex(int row, int column, void *data = 0) const; + inline QModelIndex createIndex(int row, int column, int id) const; + inline QModelIndex createIndex(int row, int column, quint32 id) const; + + void encodeData(const QModelIndexList &indexes, QDataStream &stream) const; + bool decodeData(int row, int column, const QModelIndex &parent, QDataStream &stream); + + void beginInsertRows(const QModelIndex &parent, int first, int last); + void endInsertRows(); + + void beginRemoveRows(const QModelIndex &parent, int first, int last); + void endRemoveRows(); + + bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationRow); + void endMoveRows(); + + void beginInsertColumns(const QModelIndex &parent, int first, int last); + void endInsertColumns(); + + void beginRemoveColumns(const QModelIndex &parent, int first, int last); + void endRemoveColumns(); + + bool beginMoveColumns(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationColumn); + void endMoveColumns(); + + void reset(); + + void beginResetModel(); + void endResetModel(); + + void changePersistentIndex(const QModelIndex &from, const QModelIndex &to); + void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to); + QModelIndexList persistentIndexList() const; + + void setRoleNames(const QHash &roleNames); + +private: + Q_DECLARE_PRIVATE(QAbstractItemModel) + Q_DISABLE_COPY(QAbstractItemModel) +}; + +inline bool QAbstractItemModel::insertRow(int arow, const QModelIndex &aparent) +{ return insertRows(arow, 1, aparent); } +inline bool QAbstractItemModel::insertColumn(int acolumn, const QModelIndex &aparent) +{ return insertColumns(acolumn, 1, aparent); } +inline bool QAbstractItemModel::removeRow(int arow, const QModelIndex &aparent) +{ return removeRows(arow, 1, aparent); } +inline bool QAbstractItemModel::removeColumn(int acolumn, const QModelIndex &aparent) +{ return removeColumns(acolumn, 1, aparent); } + +inline QModelIndex QAbstractItemModel::createIndex(int arow, int acolumn, void *adata) const +{ return QModelIndex(arow, acolumn, adata, this); } +inline QModelIndex QAbstractItemModel::createIndex(int arow, int acolumn, int aid) const +#if defined(Q_CC_MSVC) +#pragma warning( push ) +#pragma warning( disable : 4312 ) // avoid conversion warning on 64-bit +#endif +{ return QModelIndex(arow, acolumn, reinterpret_cast(aid), this); } +#if defined(Q_CC_MSVC) +#pragma warning( pop ) +#endif +inline QModelIndex QAbstractItemModel::createIndex(int arow, int acolumn, quint32 aid) const +#if defined(Q_CC_MSVC) +#pragma warning( push ) +#pragma warning( disable : 4312 ) // avoid conversion warning on 64-bit +#endif +{ return QModelIndex(arow, acolumn, reinterpret_cast(aid), this); } +#if defined(Q_CC_MSVC) +#pragma warning( pop ) +#endif + + +class Q_CORE_EXPORT QAbstractTableModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit QAbstractTableModel(QObject *parent = 0); + ~QAbstractTableModel(); + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent); +protected: + QAbstractTableModel(QAbstractItemModelPrivate &dd, QObject *parent); + +private: + Q_DISABLE_COPY(QAbstractTableModel) + QModelIndex parent(const QModelIndex &child) const; + bool hasChildren(const QModelIndex &parent) const; +}; + +class Q_CORE_EXPORT QAbstractListModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit QAbstractListModel(QObject *parent = 0); + ~QAbstractListModel(); + + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent); +protected: + QAbstractListModel(QAbstractItemModelPrivate &dd, QObject *parent); + +private: + Q_DISABLE_COPY(QAbstractListModel) + QModelIndex parent(const QModelIndex &child) const; + int columnCount(const QModelIndex &parent) const; + bool hasChildren(const QModelIndex &parent) const; +}; + +// inline implementations + +inline QModelIndex::QModelIndex(int arow, int acolumn, void *adata, + const QAbstractItemModel *amodel) + : r(arow), c(acolumn), p(adata), m(amodel) {} + +inline QModelIndex QModelIndex::parent() const +{ return m ? m->parent(*this) : QModelIndex(); } + +inline QModelIndex QModelIndex::sibling(int arow, int acolumn) const +{ return m ? (r == arow && c == acolumn) ? *this : m->index(arow, acolumn, m->parent(*this)) : QModelIndex(); } + +inline QModelIndex QModelIndex::child(int arow, int acolumn) const +{ return m ? m->index(arow, acolumn, *this) : QModelIndex(); } + +inline QVariant QModelIndex::data(int arole) const +{ return m ? m->data(*this, arole) : QVariant(); } + +inline Qt::ItemFlags QModelIndex::flags() const +{ return m ? m->flags(*this) : Qt::ItemFlags(0); } + +inline uint qHash(const QModelIndex &index) +{ return uint((index.row() << 4) + index.column() + index.internalId()); } + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QABSTRACTITEMMODEL_H diff --git a/src/corelib/kernel/qabstractitemmodel_p.h b/src/corelib/kernel/qabstractitemmodel_p.h new file mode 100644 index 0000000000..dfe5cef86a --- /dev/null +++ b/src/corelib/kernel/qabstractitemmodel_p.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QABSTRACTITEMMODEL_P_H +#define QABSTRACTITEMMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of QAbstractItemModel*. This header file may change from version +// to version without notice, or even be removed. +// +// We mean it. +// +// + +#include "private/qobject_p.h" +#include "QtCore/qstack.h" +#include "QtCore/qset.h" +#include "QtCore/qhash.h" + +QT_BEGIN_NAMESPACE + +class QPersistentModelIndexData +{ +public: + QPersistentModelIndexData() : model(0) {} + QPersistentModelIndexData(const QModelIndex &idx) : index(idx), model(idx.model()) {} + QModelIndex index; + QAtomicInt ref; + const QAbstractItemModel *model; + static QPersistentModelIndexData *create(const QModelIndex &index); + static void destroy(QPersistentModelIndexData *data); +}; + +class Q_CORE_EXPORT QAbstractItemModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QAbstractItemModel) + +public: + QAbstractItemModelPrivate() : QObjectPrivate(), supportedDragActions(-1), roleNames(defaultRoleNames()) {} + void removePersistentIndexData(QPersistentModelIndexData *data); + void movePersistentIndexes(QVector indexes, int change, const QModelIndex &parent, Qt::Orientation orientation); + void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last); + void rowsInserted(const QModelIndex &parent, int first, int last); + void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last); + void rowsRemoved(const QModelIndex &parent, int first, int last); + void columnsAboutToBeInserted(const QModelIndex &parent, int first, int last); + void columnsInserted(const QModelIndex &parent, int first, int last); + void columnsAboutToBeRemoved(const QModelIndex &parent, int first, int last); + void columnsRemoved(const QModelIndex &parent, int first, int last); + static QAbstractItemModel *staticEmptyModel(); + static bool variantLessThan(const QVariant &v1, const QVariant &v2); + + void itemsAboutToBeMoved(const QModelIndex &srcParent, int srcFirst, int srcLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation); + void itemsMoved(const QModelIndex &srcParent, int srcFirst, int srcLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation); + bool allowMove(const QModelIndex &srcParent, int srcFirst, int srcLast, const QModelIndex &destinationParent, int destinationChild, Qt::Orientation orientation); + + inline QModelIndex createIndex(int row, int column, void *data = 0) const { + return q_func()->createIndex(row, column, data); + } + + inline QModelIndex createIndex(int row, int column, int id) const { + return q_func()->createIndex(row, column, id); + } + + inline bool indexValid(const QModelIndex &index) const { + return (index.row() >= 0) && (index.column() >= 0) && (index.model() == q_func()); + } + + inline void invalidatePersistentIndexes() { + foreach (QPersistentModelIndexData *data, persistent.indexes) { + data->index = QModelIndex(); + data->model = 0; + } + persistent.indexes.clear(); + } + + /*! + \internal + clean the QPersistentModelIndex relative to the index if there is one. + To be used before an index is invalided + */ + inline void invalidatePersistentIndex(const QModelIndex &index) { + QHash::iterator it = persistent.indexes.find(index); + if(it != persistent.indexes.end()) { + QPersistentModelIndexData *data = *it; + persistent.indexes.erase(it); + data->index = QModelIndex(); + data->model = 0; + } + } + + struct Change { + Change() : first(-1), last(-1) {} + Change(const Change &c) : parent(c.parent), first(c.first), last(c.last), needsAdjust(c.needsAdjust) {} + Change(const QModelIndex &p, int f, int l) : parent(p), first(f), last(l), needsAdjust(false) {} + QModelIndex parent; + int first, last; + + + // In cases such as this: + // - A + // - B + // - C + // - - D + // - - E + // - - F + // + // If B is moved to above E, C is the source parent in the signal and its row is 2. When the move is + // completed however, C is at row 1 and there is no row 2 at the same level in the model at all. + // The QModelIndex is adjusted to correct that in those cases before reporting it though the + // rowsMoved signal. + bool needsAdjust; + + bool isValid() { return first >= 0 && last >= 0; } + }; + QStack changes; + + struct Persistent { + Persistent() {} + QHash indexes; + QStack > moved; + QStack > invalidated; + void insertMultiAtEnd(const QModelIndex& key, QPersistentModelIndexData *data); + } persistent; + + Qt::DropActions supportedDragActions; + + QHash roleNames; + static const QHash &defaultRoleNames(); +}; + +QT_END_NAMESPACE + +#endif // QABSTRACTITEMMODEL_P_H diff --git a/src/corelib/kernel/qbasictimer.cpp b/src/corelib/kernel/qbasictimer.cpp new file mode 100644 index 0000000000..6924848773 --- /dev/null +++ b/src/corelib/kernel/qbasictimer.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qbasictimer.h" +#include "qcoreapplication.h" +#include "qabstracteventdispatcher.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QBasicTimer + \brief The QBasicTimer class provides timer events for objects. + + \ingroup events + + This is a fast, lightweight, and low-level class used by Qt + internally. We recommend using the higher-level QTimer class + rather than this class if you want to use timers in your + applications. Note that this timer is a repeating timer that + will send subsequent timer events unless the stop() function is called. + + To use this class, create a QBasicTimer, and call its start() + function with a timeout interval and with a pointer to a QObject + subclass. When the timer times out it will send a timer event to + the QObject subclass. The timer can be stopped at any time using + stop(). isActive() returns true for a timer that is running; + i.e. it has been started, has not reached the timeout time, and + has not been stopped. The timer's ID can be retrieved using + timerId(). + + The \l{widgets/wiggly}{Wiggly} example uses QBasicTimer to repaint + a widget at regular intervals. + + \sa QTimer, QTimerEvent, QObject::timerEvent(), Timers, {Wiggly Example} +*/ + + +/*! + \fn QBasicTimer::QBasicTimer() + + Contructs a basic timer. + + \sa start() +*/ +/*! + \fn QBasicTimer::~QBasicTimer() + + Destroys the basic timer. +*/ + +/*! + \fn bool QBasicTimer::isActive() const + + Returns true if the timer is running and has not been stopped; otherwise + returns false. + + \sa start() stop() +*/ + +/*! + \fn int QBasicTimer::timerId() const + + Returns the timer's ID. + + \sa QTimerEvent::timerId() +*/ + +/*! + \fn void QBasicTimer::start(int msec, QObject *object) + + Starts (or restarts) the timer with a \a msec milliseconds + timeout. + + The given \a object will receive timer events. + + \sa stop() isActive() QObject::timerEvent() + */ +void QBasicTimer::start(int msec, QObject *obj) +{ + stop(); + if (obj) + id = obj->startTimer(msec); +} + +/*! + Stops the timer. + + \sa start() isActive() +*/ +void QBasicTimer::stop() +{ + if (id) { + QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(); + if (eventDispatcher) + eventDispatcher->unregisterTimer(id); + } + id = 0; +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qbasictimer.h b/src/corelib/kernel/qbasictimer.h new file mode 100644 index 0000000000..2b56624086 --- /dev/null +++ b/src/corelib/kernel/qbasictimer.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QBASICTIMER_H +#define QBASICTIMER_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QObject; + +class Q_CORE_EXPORT QBasicTimer +{ + int id; +public: + inline QBasicTimer() : id(0) {} + inline ~QBasicTimer() { if (id) stop(); } + + inline bool isActive() const { return id != 0; } + inline int timerId() const { return id; } + + void start(int msec, QObject *obj); + void stop(); +}; +Q_DECLARE_TYPEINFO(QBasicTimer, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QBASICTIMER_H diff --git a/src/corelib/kernel/qcore_mac.cpp b/src/corelib/kernel/qcore_mac.cpp new file mode 100644 index 0000000000..7b3f35e308 --- /dev/null +++ b/src/corelib/kernel/qcore_mac.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 +#include +#include "qvarlengtharray.h" + +QT_BEGIN_NAMESPACE + +QString QCFString::toQString(CFStringRef str) +{ + if(!str) + return QString(); + CFIndex length = CFStringGetLength(str); + const UniChar *chars = CFStringGetCharactersPtr(str); + if (chars) + return QString(reinterpret_cast(chars), length); + + QVarLengthArray buffer(length); + CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data()); + return QString(reinterpret_cast(buffer.constData()), length); +} + +QCFString::operator QString() const +{ + if (string.isEmpty() && type) + const_cast(this)->string = toQString(type); + return string; +} + +CFStringRef QCFString::toCFStringRef(const QString &string) +{ + return CFStringCreateWithCharacters(0, reinterpret_cast(string.unicode()), + string.length()); +} + +QCFString::operator CFStringRef() const +{ + if (!type) + const_cast(this)->type = toCFStringRef(string); + return type; +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h new file mode 100644 index 0000000000..f7f6bfe483 --- /dev/null +++ b/src/corelib/kernel/qcore_mac_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QCORE_MAC_P_H +#define QCORE_MAC_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef __IMAGECAPTURE__ +# define __IMAGECAPTURE__ +#endif + +#undef OLD_DEBUG +#ifdef DEBUG +# define OLD_DEBUG DEBUG +# undef DEBUG +#endif +#define DEBUG 0 +#ifdef qDebug +# define old_qDebug qDebug +# undef qDebug +#endif + +#if defined(QT_BUILD_QMAKE) || defined(QT_BOOTSTRAPPED) +#include +#else +#include +#endif + +#ifndef QT_NO_CORESERVICES +#include +#endif + +#undef DEBUG +#ifdef OLD_DEBUG +# define DEBUG OLD_DEBUG +# undef OLD_DEBUG +#endif + +#ifdef old_qDebug +# undef qDebug +# define qDebug QT_NO_QDEBUG_MACRO +# undef old_qDebug +#endif + +#include "qstring.h" + +QT_BEGIN_NAMESPACE + +/* + Helper class that automates refernce counting for CFtypes. + After constructing the QCFType object, it can be copied like a + value-based type. + + Note that you must own the object you are wrapping. + This is typically the case if you get the object from a Core + Foundation function with the word "Create" or "Copy" in it. If + you got the object from a "Get" function, either retain it or use + constructFromGet(). One exception to this rule is the + HIThemeGet*Shape functions, which in reality are "Copy" functions. +*/ +template +class Q_CORE_EXPORT QCFType +{ +public: + inline QCFType(const T &t = 0) : type(t) {} + inline QCFType(const QCFType &helper) : type(helper.type) { if (type) CFRetain(type); } + inline ~QCFType() { if (type) CFRelease(type); } + inline operator T() { return type; } + inline QCFType operator =(const QCFType &helper) + { + if (helper.type) + CFRetain(helper.type); + CFTypeRef type2 = type; + type = helper.type; + if (type2) + CFRelease(type2); + return *this; + } + inline T *operator&() { return &type; } + static QCFType constructFromGet(const T &t) + { + CFRetain(t); + return QCFType(t); + } +protected: + T type; +}; + +class Q_CORE_EXPORT QCFString : public QCFType +{ +public: + inline QCFString(const QString &str) : QCFType(0), string(str) {} + inline QCFString(const CFStringRef cfstr = 0) : QCFType(cfstr) {} + inline QCFString(const QCFType &other) : QCFType(other) {} + operator QString() const; + operator CFStringRef() const; + static QString toQString(CFStringRef cfstr); + static CFStringRef toCFStringRef(const QString &str); +private: + QString string; +}; + +QT_END_NAMESPACE + +#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5) +#ifndef __LP64__ + typedef float CGFloat; + typedef int NSInteger; + typedef unsigned int NSUInteger; + #define SRefCon SInt32 + #define URefCon UInt32 +#endif +#endif + +#endif // QCORE_MAC_P_H diff --git a/src/corelib/kernel/qcore_symbian_p.cpp b/src/corelib/kernel/qcore_symbian_p.cpp new file mode 100644 index 0000000000..c717cb9f69 --- /dev/null +++ b/src/corelib/kernel/qcore_symbian_p.cpp @@ -0,0 +1,317 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 +#include +#include +#include "qcore_symbian_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +/* + Helper function for calling into Symbian classes that expect a TDes&. + This function converts a QString to a TDes by allocating memory that + must be deleted by the caller. +*/ + +Q_CORE_EXPORT HBufC* qt_QString2HBufC(const QString& aString) +{ + HBufC *buffer; +#ifdef QT_NO_UNICODE + TPtrC8 ptr(reinterpret_cast(aString.toLocal8Bit().constData())); +#else + TPtrC16 ptr(qt_QString2TPtrC(aString)); +#endif + buffer = HBufC::New(ptr.Length()); + Q_CHECK_PTR(buffer); + buffer->Des().Copy(ptr); + return buffer; +} + +Q_CORE_EXPORT QString qt_TDesC2QString(const TDesC& aDescriptor) +{ +#ifdef QT_NO_UNICODE + return QString::fromLocal8Bit(aDescriptor.Ptr(), aDescriptor.Length()); +#else + return QString(reinterpret_cast(aDescriptor.Ptr()), aDescriptor.Length()); +#endif +} + +QHBufC::QHBufC() + : m_hBufC(0) +{ +} + +QHBufC::QHBufC(const QHBufC &src) + : m_hBufC(src.m_hBufC->Alloc()) +{ + Q_CHECK_PTR(m_hBufC); +} + +/*! + \internal + Constructs a QHBufC from an HBufC. Note that the QHBufC instance takes + ownership of the HBufC. +*/ +QHBufC::QHBufC(HBufC *src) + : m_hBufC(src) +{ +} + +QHBufC::QHBufC(const QString &src) +{ + m_hBufC = qt_QString2HBufC(src); +} + +QHBufC::~QHBufC() +{ + if (m_hBufC) + delete m_hBufC; +} + +class QS60PluginResolver +{ +public: + QS60PluginResolver() + : initTried(false) {} + + ~QS60PluginResolver() { + lib.Close(); + } + + TLibraryFunction resolve(int ordinal) { + if (!initTried) { + init(); + initTried = true; + } + + if (lib.Handle()) + return lib.Lookup(ordinal); + else + return reinterpret_cast(NULL); + } + +private: + void init() + { + _LIT(KLibName_3_1, "qts60plugin_3_1" QT_LIBINFIX_UNICODE L".dll"); + _LIT(KLibName_3_2, "qts60plugin_3_2" QT_LIBINFIX_UNICODE L".dll"); + _LIT(KLibName_5_0, "qts60plugin_5_0" QT_LIBINFIX_UNICODE L".dll"); + + TPtrC libName; + TInt uidValue; + switch (QSysInfo::s60Version()) { + case QSysInfo::SV_S60_3_1: + libName.Set(KLibName_3_1); + uidValue = 0x2001E620; + break; + case QSysInfo::SV_S60_3_2: + libName.Set(KLibName_3_2); + uidValue = 0x2001E621; + break; + case QSysInfo::SV_S60_5_0: // Fall through to default + default: + // Default to 5.0 version, as any unknown platform is likely to be newer than that + libName.Set(KLibName_5_0); + uidValue = 0x2001E622; + break; + } + + TUidType libUid(KDynamicLibraryUid, KSharedLibraryUid, TUid::Uid(uidValue)); + lib.Load(libName, libUid); + + // Duplicate lib handle to enable process wide access to it. Since Duplicate overwrites + // existing handle without closing it, store original for subsequent closing. + RLibrary origHandleCloser = lib; + lib.Duplicate(RThread(), EOwnerProcess); + origHandleCloser.Close(); + } + + RLibrary lib; + bool initTried; +}; + +Q_GLOBAL_STATIC(QS60PluginResolver, qt_s60_plugin_resolver); + +/*! + \internal + Resolves a platform version specific function from S60 plugin. + If plugin is missing or resolving fails for another reason, NULL is returned. +*/ +Q_CORE_EXPORT TLibraryFunction qt_resolveS60PluginFunc(int ordinal) +{ + return qt_s60_plugin_resolver()->resolve(ordinal); +} + +class QS60RFsSession +{ +public: + QS60RFsSession() { + qt_symbian_throwIfError(iFs.Connect()); + qt_symbian_throwIfError(iFs.ShareProtected()); + } + + ~QS60RFsSession() { + iFs.Close(); + } + + RFs& GetRFs() { + return iFs; + } + +private: + + RFs iFs; +}; + +uint qHash(const RSubSessionBase& key) +{ + return qHash(key.SubSessionHandle()); +} + +Q_GLOBAL_STATIC(QS60RFsSession, qt_s60_RFsSession); + +Q_CORE_EXPORT RFs& qt_s60GetRFs() +{ + return qt_s60_RFsSession()->GetRFs(); +} + +QSymbianSocketManager::QSymbianSocketManager() : + iNextSocket(0), iDefaultConnection(0) +{ + TSessionPref preferences; + // ### In future this could be changed to KAfInet6 when that is more common than IPv4 + preferences.iAddrFamily = KAfInet; + preferences.iProtocol = KProtocolInetIp; + //use global message pool, as we do not know how many sockets app will use + //TODO: is this the right choice? + qt_symbian_throwIfError(iSocketServ.Connect(preferences, -1)); + qt_symbian_throwIfError(iSocketServ.ShareAuto()); +} + +QSymbianSocketManager::~QSymbianSocketManager() +{ + iSocketServ.Close(); + if(!socketMap.isEmpty()) { + qWarning("leaked %d sockets on exit", socketMap.count()); + } +} + +RSocketServ& QSymbianSocketManager::getSocketServer() { + return iSocketServ; +} + +int QSymbianSocketManager::addSocket(const RSocket& socket) { + QHashableSocket sock(static_cast(socket)); + QMutexLocker l(&iMutex); + Q_ASSERT(!socketMap.contains(sock)); + if(socketMap.contains(sock)) + return socketMap.value(sock); + // allocate socket number + int guard = 0; + while(reverseSocketMap.contains(iNextSocket)) { + iNextSocket++; + iNextSocket %= max_sockets; + guard++; + if(guard > max_sockets) + return -1; + } + int id = iNextSocket; + + socketMap[sock] = id; + reverseSocketMap[id] = sock; + return id + socket_offset; +} + +bool QSymbianSocketManager::removeSocket(const RSocket &socket) { + QHashableSocket sock(static_cast(socket)); + QMutexLocker l(&iMutex); + if(!socketMap.contains(sock)) + return false; + int id = socketMap.value(sock); + socketMap.remove(sock); + reverseSocketMap.remove(id); + return true; +} + +int QSymbianSocketManager::lookupSocket(const RSocket& socket) const { + QHashableSocket sock(static_cast(socket)); + QMutexLocker l(&iMutex); + if(!socketMap.contains(sock)) + return -1; + int id = socketMap.value(sock); + return id + socket_offset; +} + +bool QSymbianSocketManager::lookupSocket(int fd, RSocket& socket) const { + QMutexLocker l(&iMutex); + int id = fd - socket_offset; + if(!reverseSocketMap.contains(id)) + return false; + socket = reverseSocketMap.value(id); + return true; +} + +void QSymbianSocketManager::setDefaultConnection(RConnection* con) +{ + iDefaultConnection = con; +} + +RConnection* QSymbianSocketManager::defaultConnection() const +{ + return iDefaultConnection; +} + +Q_GLOBAL_STATIC(QSymbianSocketManager, qt_symbianSocketManager); + +QSymbianSocketManager& QSymbianSocketManager::instance() +{ + return *(qt_symbianSocketManager()); +} + +Q_CORE_EXPORT RSocketServ& qt_symbianGetSocketServer() +{ + return QSymbianSocketManager::instance().getSocketServer(); +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcore_symbian_p.h b/src/corelib/kernel/qcore_symbian_p.h new file mode 100644 index 0000000000..cbe84a8aa1 --- /dev/null +++ b/src/corelib/kernel/qcore_symbian_p.h @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QCORE_SYMBIAN_P_H +#define QCORE_SYMBIAN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#define QT_LSTRING2(x) L##x +#define QT_LSTRING(x) QT_LSTRING2(x) + +#if defined(QT_LIBINFIX) +# define QT_LIBINFIX_UNICODE QT_LSTRING(QT_LIBINFIX) +#else +# define QT_LIBINFIX_UNICODE L"" +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT HBufC* qt_QString2HBufC(const QString& aString); + +Q_CORE_EXPORT QString qt_TDesC2QString(const TDesC& aDescriptor); +inline QString qt_TDes2QString(const TDes& aDescriptor) { return qt_TDesC2QString(aDescriptor); } + +static inline QSize qt_TSize2QSize(const TSize& ts) +{ + return QSize(ts.iWidth, ts.iHeight); +} + +static inline TSize qt_QSize2TSize(const QSize& qs) +{ + return TSize(qs.width(), qs.height()); +} + +static inline QRect qt_TRect2QRect(const TRect& tr) +{ + return QRect(tr.iTl.iX, tr.iTl.iY, tr.Width(), tr.Height()); +} + +static inline TRect qt_QRect2TRect(const QRect& qr) +{ + return TRect(TPoint(qr.left(), qr.top()), TSize(qr.width(), qr.height())); +} + +// Returned TPtrC is valid as long as the given parameter is valid and unmodified +static inline TPtrC qt_QString2TPtrC( const QString& string ) +{ + return TPtrC16(static_cast(string.utf16()), string.length()); +} + +/*! + \internal + This class is a wrapper around the Symbian HBufC descriptor class. + It makes sure that the heap allocated HBufC class is freed when it is + destroyed. +*/ +class Q_CORE_EXPORT QHBufC +{ +public: + QHBufC(); + QHBufC(const QHBufC &src); + QHBufC(HBufC *src); + QHBufC(const QString &src); + ~QHBufC(); + + inline operator HBufC *() { return m_hBufC; } + inline operator const HBufC *() const { return m_hBufC; } + inline HBufC *data() { return m_hBufC; } + inline const HBufC *data() const { return m_hBufC; } + inline HBufC & operator*() { return *m_hBufC; } + inline const HBufC & operator*() const { return *m_hBufC; } + inline HBufC * operator->() { return m_hBufC; } + inline const HBufC * operator->() const { return m_hBufC; } + + inline bool operator==(const QHBufC ¶m) const { return data() == param.data(); } + inline bool operator!=(const QHBufC ¶m) const { return data() != param.data(); } + +private: + HBufC *m_hBufC; +}; + +inline uint qHash(TUid uid) +{ + return qHash(uid.iUid); +} + +// S60 version specific function ordinals that can be resolved +enum S60PluginFuncOrdinals +{ + S60Plugin_TimeFormatL = 1, + S60Plugin_GetTimeFormatSpec = 2, + S60Plugin_GetLongDateFormatSpec = 3, + S60Plugin_GetShortDateFormatSpec = 4, + S60Plugin_LocalizedDirectoryName = 5, + S60Plugin_GetSystemDrive = 6 +}; + +Q_CORE_EXPORT TLibraryFunction qt_resolveS60PluginFunc(int ordinal); + +Q_CORE_EXPORT RFs& qt_s60GetRFs(); +Q_CORE_EXPORT RSocketServ& qt_symbianGetSocketServer(); + +// Defined in qlocale_symbian.cpp. +Q_CORE_EXPORT QByteArray qt_symbianLocaleName(int code); + +template +struct QScopedPointerRCloser +{ + static inline void cleanup(R *rPointer) + { + // Enforce a complete type. + // If you get a compile error here, read the section on forward declared + // classes in the QScopedPointer documentation. + typedef char IsIncompleteType[ sizeof(R) ? 1 : -1 ]; + (void) sizeof(IsIncompleteType); + + if (rPointer) + rPointer->Close(); + } +}; + +//Wrapper for RSocket so it can be used as a key in QHash or QMap +class QHashableSocket : public RSocket +{ +public: + bool operator==(const QHashableSocket &other) const + { + return SubSessionHandle() == other.SubSessionHandle() + && Session().Handle() == other.Session().Handle(); + } + bool operator<(const QHashableSocket &other) const + { + if (Session().Handle() == other.Session().Handle()) + return SubSessionHandle() < other.SubSessionHandle(); + return Session().Handle() < other.Session().Handle(); + } +}; + +uint qHash(const RSubSessionBase& key); + +/*! + \internal + This class exists in QtCore for the benefit of QSocketNotifier, which uses integer + file descriptors in its public API. + So we need a way to map between int and RSocket. + Additionally, it is used to host the global RSocketServ session +*/ +class Q_CORE_EXPORT QSymbianSocketManager +{ +public: + QSymbianSocketManager(); + ~QSymbianSocketManager(); + + /*! + \internal + \return handle to the socket server + */ + RSocketServ& getSocketServer(); + /*! + \internal + Adds a symbian socket to the global map + \param an open socket + \return pseudo file descriptor, -1 if out of resources + */ + int addSocket(const RSocket &sock); + /*! + \internal + Removes a symbian socket from the global map + \param an open socket + \return true if the socket was in the map + */ + bool removeSocket(const RSocket &sock); + /*! + \internal + Get pseudo file descriptor for a socket + \param an open socket + \return integer handle, or -1 if not in map + */ + int lookupSocket(const RSocket &sock) const; + /*! + \internal + Get socket for a pseudo file descriptor + \param an open socket fd + \param sock (out) socket handle + \return true on success or false if not in map + */ + bool lookupSocket(int fd, RSocket& sock) const; + + /*! + \internal + Set the default connection to use for new sockets + \param an open connection + */ + void setDefaultConnection(RConnection* con); + /*! + \internal + Get the default connection to use for new sockets + \return the connection, or null pointer if there is none set + */ + RConnection *defaultConnection() const; + + /*! + \internal + Gets a reference to the singleton socket manager + */ + static QSymbianSocketManager& instance(); +private: + int allocateSocket(); + + const static int max_sockets = 0x20000; //covers all TCP and UDP ports, probably run out of memory first + const static int socket_offset = 0x40000000; //hacky way of separating sockets from file descriptors + int iNextSocket; + QHash socketMap; + QHash reverseSocketMap; + mutable QMutex iMutex; + RSocketServ iSocketServ; + RConnection *iDefaultConnection; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QCORE_SYMBIAN_P_H diff --git a/src/corelib/kernel/qcore_unix.cpp b/src/corelib/kernel/qcore_unix.cpp new file mode 100644 index 0000000000..834f05b208 --- /dev/null +++ b/src/corelib/kernel/qcore_unix.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qcore_unix_p.h" +#include "qelapsedtimer.h" + +#ifdef Q_OS_NACL +#elif !defined (Q_OS_VXWORKS) +# if !defined(Q_OS_HPUX) || defined(__ia64) +# include +# endif +# include +#else +# include +#endif + +#include + +#ifdef Q_OS_MAC +#include +#endif + +QT_BEGIN_NAMESPACE + +static inline bool time_update(struct timeval *tv, const struct timeval &start, + const struct timeval &timeout) +{ + if (!QElapsedTimer::isMonotonic()) { + // we cannot recalculate the timeout without a monotonic clock as the time may have changed + return false; + } + + // clock source is monotonic, so we can recalculate how much timeout is left + struct timeval now = qt_gettime(); + *tv = timeout + start - now; + return tv->tv_sec >= 0; +} + +int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, + const struct timeval *orig_timeout) +{ + if (!orig_timeout) { + // no timeout -> block forever + register int ret; + EINTR_LOOP(ret, select(nfds, fdread, fdwrite, fdexcept, 0)); + return ret; + } + + timeval start = qt_gettime(); + timeval timeout = *orig_timeout; + + // loop and recalculate the timeout as needed + int ret; + forever { + ret = ::select(nfds, fdread, fdwrite, fdexcept, &timeout); + if (ret != -1 || errno != EINTR) + return ret; + + // recalculate the timeout + if (!time_update(&timeout, start, *orig_timeout)) { + // timeout during update + // or clock reset, fake timeout error + return 0; + } + } +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h new file mode 100644 index 0000000000..33df0daab4 --- /dev/null +++ b/src/corelib/kernel/qcore_unix_p.h @@ -0,0 +1,333 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QCORE_UNIX_P_H +#define QCORE_UNIX_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt code on Unix. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qplatformdefs.h" + +#ifndef Q_OS_UNIX +# error "qcore_unix_p.h included on a non-Unix system" +#endif + +#include +#include +#include + +#include +#include +#include + +#if defined(Q_OS_VXWORKS) +# include +#endif + +struct sockaddr; + +#if defined(Q_OS_LINUX) && defined(O_CLOEXEC) +# define QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC 1 +QT_BEGIN_NAMESPACE +namespace QtLibcSupplement { + inline int accept4(int, sockaddr *, QT_SOCKLEN_T *, int) + { errno = ENOSYS; return -1; } + inline int dup3(int, int, int) + { errno = ENOSYS; return -1; } + inline int pipe2(int [], int ) + { errno = ENOSYS; return -1; } +} +QT_END_NAMESPACE +using namespace QT_PREPEND_NAMESPACE(QtLibcSupplement); + +#else +# define QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC 0 +#endif + +#define EINTR_LOOP(var, cmd) \ + do { \ + var = cmd; \ + } while (var == -1 && errno == EINTR) + +QT_BEGIN_NAMESPACE + +// Internal operator functions for timevals +inline timeval &normalizedTimeval(timeval &t) +{ + while (t.tv_usec > 1000000l) { + ++t.tv_sec; + t.tv_usec -= 1000000l; + } + while (t.tv_usec < 0l) { + --t.tv_sec; + t.tv_usec += 1000000l; + } + return t; +} +inline bool operator<(const timeval &t1, const timeval &t2) +{ return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_usec < t2.tv_usec); } +inline bool operator==(const timeval &t1, const timeval &t2) +{ return t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec; } +inline timeval &operator+=(timeval &t1, const timeval &t2) +{ + t1.tv_sec += t2.tv_sec; + t1.tv_usec += t2.tv_usec; + return normalizedTimeval(t1); +} +inline timeval operator+(const timeval &t1, const timeval &t2) +{ + timeval tmp; + tmp.tv_sec = t1.tv_sec + t2.tv_sec; + tmp.tv_usec = t1.tv_usec + t2.tv_usec; + return normalizedTimeval(tmp); +} +inline timeval operator-(const timeval &t1, const timeval &t2) +{ + timeval tmp; + tmp.tv_sec = t1.tv_sec - (t2.tv_sec - 1); + tmp.tv_usec = t1.tv_usec - (t2.tv_usec + 1000000); + return normalizedTimeval(tmp); +} +inline timeval operator*(const timeval &t1, int mul) +{ + timeval tmp; + tmp.tv_sec = t1.tv_sec * mul; + tmp.tv_usec = t1.tv_usec * mul; + return normalizedTimeval(tmp); +} + +// don't call QT_OPEN or ::open +// call qt_safe_open +static inline int qt_safe_open(const char *pathname, int flags, mode_t mode = 0777) +{ +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; +#endif + register int fd; + EINTR_LOOP(fd, QT_OPEN(pathname, flags, mode)); + + // unknown flags are ignored, so we have no way of verifying if + // O_CLOEXEC was accepted + if (fd != -1) + ::fcntl(fd, F_SETFD, FD_CLOEXEC); + return fd; +} +#undef QT_OPEN +#define QT_OPEN qt_safe_open + +#ifndef Q_OS_VXWORKS // no POSIX pipes in VxWorks +// don't call ::pipe +// call qt_safe_pipe +static inline int qt_safe_pipe(int pipefd[2], int flags = 0) +{ +#ifdef O_CLOEXEC + Q_ASSERT((flags & ~(O_CLOEXEC | O_NONBLOCK)) == 0); +#else + Q_ASSERT((flags & ~O_NONBLOCK) == 0); +#endif + + register int ret; +#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(O_CLOEXEC) + // use pipe2 + flags |= O_CLOEXEC; + ret = ::pipe2(pipefd, flags); // pipe2 is Linux-specific and is documented not to return EINTR + if (ret == 0 || errno != ENOSYS) + return ret; +#endif + + ret = ::pipe(pipefd); + if (ret == -1) + return -1; + + ::fcntl(pipefd[0], F_SETFD, FD_CLOEXEC); + ::fcntl(pipefd[1], F_SETFD, FD_CLOEXEC); + + // set non-block too? + if (flags & O_NONBLOCK) { + ::fcntl(pipefd[0], F_SETFL, ::fcntl(pipefd[0], F_GETFL) | O_NONBLOCK); + ::fcntl(pipefd[1], F_SETFL, ::fcntl(pipefd[1], F_GETFL) | O_NONBLOCK); + } + + return 0; +} + +#endif // Q_OS_VXWORKS + +// don't call dup or fcntl(F_DUPFD) +static inline int qt_safe_dup(int oldfd, int atleast = 0, int flags = FD_CLOEXEC) +{ + Q_ASSERT(flags == FD_CLOEXEC || flags == 0); + + register int ret; +#ifdef F_DUPFD_CLOEXEC + // use this fcntl + if (flags & FD_CLOEXEC) { + ret = ::fcntl(oldfd, F_DUPFD_CLOEXEC, atleast); + if (ret != -1 || errno != EINVAL) + return ret; + } +#endif + + // use F_DUPFD + ret = ::fcntl(oldfd, F_DUPFD, atleast); + + if (flags && ret != -1) + ::fcntl(ret, F_SETFD, flags); + return ret; +} + +// don't call dup2 +// call qt_safe_dup2 +static inline int qt_safe_dup2(int oldfd, int newfd, int flags = FD_CLOEXEC) +{ + Q_ASSERT(flags == FD_CLOEXEC || flags == 0); + + register int ret; +#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(O_CLOEXEC) + // use dup3 + if (flags & FD_CLOEXEC) { + EINTR_LOOP(ret, ::dup3(oldfd, newfd, O_CLOEXEC)); + if (ret == 0 || errno != ENOSYS) + return ret; + } +#endif + EINTR_LOOP(ret, ::dup2(oldfd, newfd)); + if (ret == -1) + return -1; + + if (flags) + ::fcntl(newfd, F_SETFD, flags); + return 0; +} + +static inline qint64 qt_safe_read(int fd, void *data, qint64 maxlen) +{ + qint64 ret = 0; + EINTR_LOOP(ret, QT_READ(fd, data, maxlen)); + return ret; +} +#undef QT_READ +#define QT_READ qt_safe_read + +static inline qint64 qt_safe_write(int fd, const void *data, qint64 len) +{ + qint64 ret = 0; + EINTR_LOOP(ret, QT_WRITE(fd, data, len)); + return ret; +} +#undef QT_WRITE +#define QT_WRITE qt_safe_write + +static inline int qt_safe_close(int fd) +{ + register int ret; + EINTR_LOOP(ret, QT_CLOSE(fd)); + return ret; +} +#undef QT_CLOSE +#define QT_CLOSE qt_safe_close + +// - Open C does not (yet?) implement these on Symbian OS +// - VxWorks doesn't have processes +#if !defined(Q_OS_SYMBIAN) && !defined(Q_OS_VXWORKS) +static inline int qt_safe_execve(const char *filename, char *const argv[], + char *const envp[]) +{ + register int ret; + EINTR_LOOP(ret, ::execve(filename, argv, envp)); + return ret; +} + +static inline int qt_safe_execv(const char *path, char *const argv[]) +{ + register int ret; + EINTR_LOOP(ret, ::execv(path, argv)); + return ret; +} + +static inline int qt_safe_execvp(const char *file, char *const argv[]) +{ + register int ret; + EINTR_LOOP(ret, ::execvp(file, argv)); + return ret; +} +#endif + +#ifndef Q_OS_VXWORKS // no processes on VxWorks +static inline pid_t qt_safe_waitpid(pid_t pid, int *status, int options) +{ + register int ret; + EINTR_LOOP(ret, ::waitpid(pid, status, options)); + return ret; +} + +#endif // Q_OS_VXWORKS + +#if !defined(_POSIX_MONOTONIC_CLOCK) +# define _POSIX_MONOTONIC_CLOCK -1 +#endif + +timeval qt_gettime(); // in qelapsedtimer_mac.cpp or qtimestamp_unix.cpp + +Q_CORE_EXPORT int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, + const struct timeval *tv); + +// according to X/OPEN we have to define semun ourselves +// we use prefix as on some systems sem.h will have it +struct semid_ds; +union qt_semun { + int val; /* value for SETVAL */ + struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ + unsigned short *array; /* array for GETALL, SETALL */ +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp new file mode 100644 index 0000000000..be86c58d47 --- /dev/null +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -0,0 +1,2730 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qcoreapplication.h" +#include "qcoreapplication_p.h" + +#include "qabstracteventdispatcher.h" +#include "qcoreevent.h" +#include "qeventloop.h" +#include "qcorecmdlineargs_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_SYMBIAN +# include +# include +# include +# include "qeventdispatcher_symbian_p.h" +# include "private/qcore_symbian_p.h" +# include "private/qfilesystemengine_p.h" +# include +#elif defined(Q_OS_UNIX) +# if !defined(QT_NO_GLIB) +# include "qeventdispatcher_glib_p.h" +# endif +# include "qeventdispatcher_unix_p.h" +#endif + +#ifdef Q_OS_WIN +# include "qeventdispatcher_win_p.h" +#endif + +#ifdef Q_OS_MAC +# include "qcore_mac_p.h" +#endif + +#include + +#ifdef Q_OS_UNIX +# include +#endif + +#ifdef Q_OS_VXWORKS +# include +#endif + +QT_BEGIN_NAMESPACE + +class QMutexUnlocker +{ +public: + inline explicit QMutexUnlocker(QMutex *m) + : mtx(m) + { } + inline ~QMutexUnlocker() { unlock(); } + inline void unlock() { if (mtx) mtx->unlock(); mtx = 0; } + +private: + Q_DISABLE_COPY(QMutexUnlocker) + + QMutex *mtx; +}; + +#ifdef Q_OS_SYMBIAN +typedef TDriveNumber (*SystemDriveFunc)(RFs&); +static SystemDriveFunc PtrGetSystemDrive = 0; +static CApaCommandLine* apaCommandLine = 0; +static char *apaTail = 0; +static QVector *apaArgv = 0; + +static void qt_cleanup_apa_cmd_line() +{ + delete apaCommandLine; + apaCommandLine = 0; + delete apaArgv; + apaArgv = 0; + delete apaTail; + apaTail = 0; +} + +static inline void qt_init_symbian_apa_arguments(int &argc, char **&argv) +{ + // If app is launched via CApaCommandLine::StartApp(), normal arguments only contain + // application name. + if (argc == 1) { + CApaCommandLine* commandLine = QCoreApplicationPrivate::symbianCommandLine(); + if(commandLine) { + TPtrC8 apaCmdLine = commandLine->TailEnd(); + int tailLen = apaCmdLine.Length(); + if (tailLen) { + apaTail = reinterpret_cast(qMalloc(tailLen + 1)); + qMemCopy(apaTail, reinterpret_cast(apaCmdLine.Ptr()), tailLen); + apaTail[tailLen] = '\0'; + apaArgv = new QVector(8); + // Reuse windows command line parsing + *apaArgv = qWinCmdLine(apaTail, tailLen, argc); + apaArgv->insert(0, argv[0]); + argc++; + argv = apaArgv->data(); + } + } + } +} + +CApaCommandLine* QCoreApplicationPrivate::symbianCommandLine() +{ + // Getting of Apa command line needs to be static as it can only be called successfully + // once per process. + if (!apaCommandLine) { + TInt err = CApaCommandLine::GetCommandLineFromProcessEnvironment(apaCommandLine); + if (err == KErrNone) { + qAddPostRoutine(qt_cleanup_apa_cmd_line); + } + } + return apaCommandLine; +} + +#endif + +#if defined(Q_WS_WIN) || defined(Q_WS_MAC) +extern QString qAppFileName(); +#endif + +int QCoreApplicationPrivate::app_compile_version = 0x040000; //we don't know exactly, but it's at least 4.0.0 +#if defined(QT3_SUPPORT) +bool QCoreApplicationPrivate::useQt3Support = true; +#endif + +#if !defined(Q_OS_WIN) +#ifdef Q_OS_MAC +QString QCoreApplicationPrivate::macMenuBarName() +{ + QString bundleName; + CFTypeRef string = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("CFBundleName")); + if (string) + bundleName = QCFString::toQString(static_cast(string)); + return bundleName; +} +#endif +QString QCoreApplicationPrivate::appName() const +{ + static QString applName; +#ifdef Q_OS_MAC + applName = macMenuBarName(); +#endif + if (applName.isEmpty() && argv[0]) { + char *p = strrchr(argv[0], '/'); + applName = QString::fromLocal8Bit(p ? p + 1 : argv[0]); + } + return applName; +} +#endif + +bool QCoreApplicationPrivate::checkInstance(const char *function) +{ + bool b = (QCoreApplication::self != 0); + if (!b) + qWarning("QApplication::%s: Please instantiate the QApplication object first", function); + return b; +} + +// Support for introspection + +QSignalSpyCallbackSet Q_CORE_EXPORT qt_signal_spy_callback_set = { 0, 0, 0, 0 }; + +void qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet &callback_set) +{ + qt_signal_spy_callback_set = callback_set; +} + +extern "C" void Q_CORE_EXPORT qt_startup_hook() +{ +} + +typedef QList QVFuncList; +Q_GLOBAL_STATIC(QVFuncList, postRList) + +void qAddPostRoutine(QtCleanUpFunction p) +{ + QVFuncList *list = postRList(); + if (!list) + return; + list->prepend(p); +} + +void qRemovePostRoutine(QtCleanUpFunction p) +{ + QVFuncList *list = postRList(); + if (!list) + return; + list->removeAll(p); +} + +void Q_CORE_EXPORT qt_call_post_routines() +{ + QVFuncList *list = 0; + QT_TRY { + list = postRList(); + } QT_CATCH(const std::bad_alloc &) { + // ignore - if we can't allocate a post routine list, + // there's a high probability that there's no post + // routine to be executed :) + } + if (!list) + return; + while (!list->isEmpty()) + (list->takeFirst())(); +} + + +// app starting up if false +bool QCoreApplicationPrivate::is_app_running = false; + // app closing down if true +bool QCoreApplicationPrivate::is_app_closing = false; +// initialized in qcoreapplication and in qtextstream autotest when setlocale is called. +Q_CORE_EXPORT bool qt_locale_initialized = false; + + +Q_CORE_EXPORT uint qGlobalPostedEventsCount() +{ + QThreadData *currentThreadData = QThreadData::current(); + return currentThreadData->postEventList.size() - currentThreadData->postEventList.startOffset; +} + + +void qt_set_current_thread_to_main_thread() +{ + QCoreApplicationPrivate::theMainThread = QThread::currentThread(); +} + + + +QCoreApplication *QCoreApplication::self = 0; +QAbstractEventDispatcher *QCoreApplicationPrivate::eventDispatcher = 0; +uint QCoreApplicationPrivate::attribs; + +#ifdef Q_OS_UNIX +Qt::HANDLE qt_application_thread_id = 0; +#endif + +struct QCoreApplicationData { + QCoreApplicationData() { +#ifndef QT_NO_LIBRARY + app_libpaths = 0; +#endif + } + ~QCoreApplicationData() { +#ifndef QT_NO_LIBRARY + delete app_libpaths; +#endif + + // cleanup the QAdoptedThread created for the main() thread + if (QCoreApplicationPrivate::theMainThread) { + QThreadData *data = QThreadData::get2(QCoreApplicationPrivate::theMainThread); + QCoreApplicationPrivate::theMainThread = 0; + data->deref(); // deletes the data and the adopted thread + } + } + QString orgName, orgDomain, application; + QString applicationVersion; + +#ifndef QT_NO_LIBRARY + QStringList *app_libpaths; +#endif + +}; + +Q_GLOBAL_STATIC(QCoreApplicationData, coreappdata) + +QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint flags) + : QObjectPrivate(), argc(aargc), argv(aargv), application_type(0), eventFilter(0), + in_exec(false), aboutToQuitEmitted(false) +{ + app_compile_version = flags & 0xffffff; +#if defined(QT3_SUPPORT) + useQt3Support = !(flags & 0x01000000); +#endif + static const char *const empty = ""; + if (argc == 0 || argv == 0) { + argc = 0; + argv = (char **)∅ // ouch! careful with QCoreApplication::argv()! + } + QCoreApplicationPrivate::is_app_closing = false; + +#ifdef Q_OS_SYMBIAN + qt_init_symbian_apa_arguments(argc, argv); +#endif + +#ifdef Q_OS_UNIX + qt_application_thread_id = QThread::currentThreadId(); +#endif + + // note: this call to QThread::currentThread() may end up setting theMainThread! + if (QThread::currentThread() != theMainThread) + qWarning("WARNING: QApplication was not created in the main() thread."); +} + +QCoreApplicationPrivate::~QCoreApplicationPrivate() +{ + if (threadData) { +#ifndef QT_NO_THREAD + void *data = &threadData->tls; + QThreadStorageData::finish((void **)data); +#endif + + // need to clear the state of the mainData, just in case a new QCoreApplication comes along. + QMutexLocker locker(&threadData->postEventList.mutex); + for (int i = 0; i < threadData->postEventList.size(); ++i) { + const QPostEvent &pe = threadData->postEventList.at(i); + if (pe.event) { + --pe.receiver->d_func()->postedEvents; + pe.event->posted = false; + delete pe.event; + } + } + threadData->postEventList.clear(); + threadData->postEventList.recursion = 0; + threadData->quitNow = false; + } +} + +void QCoreApplicationPrivate::createEventDispatcher() +{ + Q_Q(QCoreApplication); +#if defined(Q_OS_SYMBIAN) + eventDispatcher = new QEventDispatcherSymbian(q); +#elif defined(Q_OS_UNIX) +# if !defined(QT_NO_GLIB) + if (qgetenv("QT_NO_GLIB").isEmpty() && QEventDispatcherGlib::versionSupported()) + eventDispatcher = new QEventDispatcherGlib(q); + else +# endif + eventDispatcher = new QEventDispatcherUNIX(q); +#elif defined(Q_OS_WIN) + eventDispatcher = new QEventDispatcherWin32(q); +#else +# error "QEventDispatcher not yet ported to this platform" +#endif +} + +QThread *QCoreApplicationPrivate::theMainThread = 0; +QThread *QCoreApplicationPrivate::mainThread() +{ + Q_ASSERT(theMainThread != 0); + return theMainThread; +} + +#if !defined (QT_NO_DEBUG) || defined (QT_MAC_FRAMEWORK_BUILD) +void QCoreApplicationPrivate::checkReceiverThread(QObject *receiver) +{ + QThread *currentThread = QThread::currentThread(); + QThread *thr = receiver->thread(); + Q_ASSERT_X(currentThread == thr || !thr, + "QCoreApplication::sendEvent", + QString::fromLatin1("Cannot send events to objects owned by a different thread. " + "Current thread %1. Receiver '%2' (of type '%3') was created in thread %4") + .arg(QString::number((quintptr) currentThread, 16)) + .arg(receiver->objectName()) + .arg(QLatin1String(receiver->metaObject()->className())) + .arg(QString::number((quintptr) thr, 16)) + .toLocal8Bit().data()); + Q_UNUSED(currentThread); + Q_UNUSED(thr); +} +#elif defined(Q_OS_SYMBIAN) && defined (QT_NO_DEBUG) +// no implementation in release builds, but keep the symbol present +void QCoreApplicationPrivate::checkReceiverThread(QObject * /* receiver */) +{ +} +#endif + +void QCoreApplicationPrivate::appendApplicationPathToLibraryPaths() +{ +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + QStringList *app_libpaths = coreappdata()->app_libpaths; + Q_ASSERT(app_libpaths); +# if defined(Q_OS_SYMBIAN) + QString app_location( QCoreApplication::applicationDirPath() ); + // File existence check for application's private dir requires additional '\' or + // platform security will not allow it. + if (app_location != QLibraryInfo::location(QLibraryInfo::PluginsPath) && QFile::exists(app_location + QLatin1Char('\\')) && !app_libpaths->contains(app_location)) +# else + QString app_location( QCoreApplication::applicationFilePath() ); + app_location.truncate(app_location.lastIndexOf(QLatin1Char('/'))); + app_location = QDir(app_location).canonicalPath(); + if (QFile::exists(app_location) && !app_libpaths->contains(app_location)) +# endif + app_libpaths->append(app_location); +#endif +} + +QString qAppName() +{ + if (!QCoreApplicationPrivate::checkInstance("qAppName")) + return QString(); + return QCoreApplication::instance()->d_func()->appName(); +} + +/*! + \class QCoreApplication + \brief The QCoreApplication class provides an event loop for console Qt + applications. + + This class is used by non-GUI applications to provide their event + loop. For non-GUI application that uses Qt, there should be exactly + one QCoreApplication object. For GUI applications, see + QApplication. + + QCoreApplication contains the main event loop, where all events + from the operating system (e.g., timer and network events) and + other sources are processed and dispatched. It also handles the + application's initialization and finalization, as well as + system-wide and application-wide settings. + + \section1 The Event Loop and Event Handling + + The event loop is started with a call to exec(). Long running + operations can call processEvents() to keep the application + responsive. + + In general, we recommend that you create a QCoreApplication or a + QApplication object in your \c main() function as early as + possible. exec() will not return until the event loop exits; e.g., + when quit() is called. + + Several static convenience functions are also provided. The + QCoreApplication object is available from instance(). Events can + be sent or posted using sendEvent(), postEvent(), and + sendPostedEvents(). Pending events can be removed with + removePostedEvents() or flushed with flush(). + + The class provides a quit() slot and an aboutToQuit() signal. + + \section1 Application and Library Paths + + An application has an applicationDirPath() and an + applicationFilePath(). Library paths (see QLibrary) can be retrieved + with libraryPaths() and manipulated by setLibraryPaths(), addLibraryPath(), + and removeLibraryPath(). + + \section1 Internationalization and Translations + + Translation files can be added or removed + using installTranslator() and removeTranslator(). Application + strings can be translated using translate(). The QObject::tr() + and QObject::trUtf8() functions are implemented in terms of + translate(). + + \section1 Accessing Command Line Arguments + + The command line arguments which are passed to QCoreApplication's + constructor should be accessed using the arguments() function. + Note that some arguments supplied by the user may have been + processed and removed by QCoreApplication. + + In cases where command line arguments need to be obtained using the + argv() function, you must convert them from the local string encoding + using QString::fromLocal8Bit(). + + \section1 Locale Settings + + On Unix/Linux Qt is configured to use the system locale settings by + default. This can cause a conflict when using POSIX functions, for + instance, when converting between data types such as floats and + strings, since the notation may differ between locales. To get + around this problem, call the POSIX function \c{setlocale(LC_NUMERIC,"C")} + right after initializing QApplication or QCoreApplication to reset + the locale that is used for number formatting to "C"-locale. + + \sa QApplication, QAbstractEventDispatcher, QEventLoop, + {Semaphores Example}, {Wait Conditions Example} +*/ + +/*! + \fn static QCoreApplication *QCoreApplication::instance() + + Returns a pointer to the application's QCoreApplication (or + QApplication) instance. + + If no instance has been allocated, \c null is returned. +*/ + +/*!\internal + */ +QCoreApplication::QCoreApplication(QCoreApplicationPrivate &p) + : QObject(p, 0) +{ + init(); + // note: it is the subclasses' job to call + // QCoreApplicationPrivate::eventDispatcher->startingUp(); +} + +/*! + Flushes the platform specific event queues. + + If you are doing graphical changes inside a loop that does not + return to the event loop on asynchronous window systems like X11 + or double buffered window systems like Mac OS X, and you want to + visualize these changes immediately (e.g. Splash Screens), call + this function. + + \sa sendPostedEvents() +*/ +void QCoreApplication::flush() +{ + if (self && self->d_func()->eventDispatcher) + self->d_func()->eventDispatcher->flush(); +} + +/*! + Constructs a Qt kernel application. Kernel applications are + applications without a graphical user interface. These type of + applications are used at the console or as server processes. + + The \a argc and \a argv arguments are processed by the application, + and made available in a more convenient form by the arguments() + function. + + \warning The data referred to by \a argc and \a argv must stay valid + for the entire lifetime of the QCoreApplication object. In addition, + \a argc must be greater than zero and \a argv must contain at least + one valid character string. +*/ +QCoreApplication::QCoreApplication(int &argc, char **argv) + : QObject(*new QCoreApplicationPrivate(argc, argv, 0x040000)) +{ + init(); + QCoreApplicationPrivate::eventDispatcher->startingUp(); +#if defined(Q_OS_SYMBIAN) && !defined(QT_NO_LIBRARY) + // Refresh factoryloader, as text codecs are requested during lib path + // resolving process and won't be therefore properly loaded. + // Unknown if this is symbian specific issue. + QFactoryLoader::refreshAll(); +#endif + +#if defined(Q_OS_SYMBIAN) && !defined(QT_NO_SYSTEMLOCALE) + d_func()->symbianInit(); +#endif +} + +QCoreApplication::QCoreApplication(int &argc, char **argv, int _internal) +: QObject(*new QCoreApplicationPrivate(argc, argv, _internal)) +{ + init(); + QCoreApplicationPrivate::eventDispatcher->startingUp(); +#if defined(Q_OS_SYMBIAN) +#ifndef QT_NO_LIBRARY + // Refresh factoryloader, as text codecs are requested during lib path + // resolving process and won't be therefore properly loaded. + // Unknown if this is symbian specific issue. + QFactoryLoader::refreshAll(); +#endif +#ifndef QT_NO_SYSTEMLOCALE + d_func()->symbianInit(); +#endif +#endif //Q_OS_SYMBIAN +} + + +// ### move to QCoreApplicationPrivate constructor? +void QCoreApplication::init() +{ + Q_D(QCoreApplication); + +#ifdef Q_OS_UNIX + setlocale(LC_ALL, ""); // use correct char set mapping + qt_locale_initialized = true; +#endif + + Q_ASSERT_X(!self, "QCoreApplication", "there should be only one application object"); + QCoreApplication::self = this; + +#ifdef Q_OS_SYMBIAN + //ensure temp and working directories exist + QFileSystemEngine::createDirectory(QFileSystemEntry(QFileSystemEngine::tempPath()), true); + QFileSystemEngine::createDirectory(QFileSystemEntry(QFileSystemEngine::currentPath()), true); +#endif + +#ifndef QT_NO_THREAD + QThread::initialize(); +#endif + + // use the event dispatcher created by the app programmer (if any) + if (!QCoreApplicationPrivate::eventDispatcher) + QCoreApplicationPrivate::eventDispatcher = d->threadData->eventDispatcher; + // otherwise we create one + if (!QCoreApplicationPrivate::eventDispatcher) + d->createEventDispatcher(); + Q_ASSERT(QCoreApplicationPrivate::eventDispatcher != 0); + + if (!QCoreApplicationPrivate::eventDispatcher->parent()) + QCoreApplicationPrivate::eventDispatcher->moveToThread(d->threadData->thread); + + d->threadData->eventDispatcher = QCoreApplicationPrivate::eventDispatcher; + +#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS) + if (!coreappdata()->app_libpaths) { + // make sure that library paths is initialized + libraryPaths(); + } else { + d->appendApplicationPathToLibraryPaths(); + } +#endif + +#if defined(Q_OS_UNIX) && !(defined(QT_NO_PROCESS)) + // Make sure the process manager thread object is created in the main + // thread. + QProcessPrivate::initializeProcessManager(); +#endif + +#ifdef QT_EVAL + extern void qt_core_eval_init(uint); + qt_core_eval_init(d->application_type); +#endif + +#if defined(Q_OS_SYMBIAN) \ + && defined(Q_CC_NOKIAX86) \ + && defined(QT_DEBUG) + /** + * Prevent the executable from being locked in the Symbian emulator. The + * code dramatically simplifies debugging on Symbian, but beyond that has + * no impact. + * + * Force the ZLazyUnloadTimer to fire and therefore unload code segments + * immediately. The code affects Symbian's file server and on the other + * hand needs only to be run once in each emulator run. + */ + { + RLoader loader; + CleanupClosePushL(loader); + User::LeaveIfError(loader.Connect()); + User::LeaveIfError(loader.CancelLazyDllUnload()); + CleanupStack::PopAndDestroy(&loader); + } +#endif + + qt_startup_hook(); +} + +#if defined(Q_OS_SYMBIAN) && !defined(QT_NO_SYSTEMLOCALE) +void QCoreApplicationPrivate::symbianInit() +{ + if (!environmentChangeNotifier) + environmentChangeNotifier.reset(new QEnvironmentChangeNotifier); +} +#endif + + +/*! + Destroys the QCoreApplication object. +*/ +QCoreApplication::~QCoreApplication() +{ + qt_call_post_routines(); + + self = 0; + QCoreApplicationPrivate::is_app_closing = true; + QCoreApplicationPrivate::is_app_running = false; + +#if !defined(QT_NO_THREAD) +#if !defined(QT_NO_CONCURRENT) + // Synchronize and stop the global thread pool threads. + QThreadPool *globalThreadPool = 0; + QT_TRY { + globalThreadPool = QThreadPool::globalInstance(); + } QT_CATCH (...) { + // swallow the exception, since destructors shouldn't throw + } + if (globalThreadPool) + globalThreadPool->waitForDone(); +#endif + QThread::cleanup(); +#endif + + d_func()->threadData->eventDispatcher = 0; + if (QCoreApplicationPrivate::eventDispatcher) + QCoreApplicationPrivate::eventDispatcher->closingDown(); + QCoreApplicationPrivate::eventDispatcher = 0; + +#ifndef QT_NO_LIBRARY + delete coreappdata()->app_libpaths; + coreappdata()->app_libpaths = 0; +#endif +} + + +/*! + Sets the attribute \a attribute if \a on is true; + otherwise clears the attribute. + + One of the attributes that can be set with this method is + Qt::AA_ImmediateWidgetCreation. It tells Qt to create toplevel + windows immediately. Normally, resources for widgets are allocated + on demand to improve efficiency and minimize resource usage. + Therefore, if it is important to minimize resource consumption, do + not set this attribute. + + \sa testAttribute() +*/ +void QCoreApplication::setAttribute(Qt::ApplicationAttribute attribute, bool on) +{ + if (on) + QCoreApplicationPrivate::attribs |= 1 << attribute; + else + QCoreApplicationPrivate::attribs &= ~(1 << attribute); +#ifdef Q_OS_MAC + // Turn on the no native menubar here, since we used to + // do this implicitly. We DO NOT flip it off if someone sets + // it to false. + // Ideally, we'd have magic that would be something along the lines of + // "follow MacPluginApplication" unless explicitly set. + // Considering this attribute isn't only at the beginning + // it's unlikely it will ever be a problem, but I want + // to have the behavior documented here. + if (attribute == Qt::AA_MacPluginApplication && on + && !testAttribute(Qt::AA_DontUseNativeMenuBar)) { + setAttribute(Qt::AA_DontUseNativeMenuBar, true); + } +#endif +} + +/*! + Returns true if attribute \a attribute is set; + otherwise returns false. + + \sa setAttribute() + */ +bool QCoreApplication::testAttribute(Qt::ApplicationAttribute attribute) +{ + return QCoreApplicationPrivate::testAttribute(attribute); +} + + +/*! + \internal + + This function is here to make it possible for Qt extensions to + hook into event notification without subclassing QApplication +*/ +bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event) +{ + // Make it possible for Qt Jambi and QSA to hook into events even + // though QApplication is subclassed... + bool result = false; + void *cbdata[] = { receiver, event, &result }; + if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) { + return result; + } + + // Qt enforces the rule that events can only be sent to objects in + // the current thread, so receiver->d_func()->threadData is + // equivalent to QThreadData::current(), just without the function + // call overhead. + QObjectPrivate *d = receiver->d_func(); + QThreadData *threadData = d->threadData; + ++threadData->loopLevel; + +#ifdef QT_JAMBI_BUILD + int deleteWatch = 0; + int *oldDeleteWatch = QObjectPrivate::setDeleteWatch(d, &deleteWatch); + + bool inEvent = d->inEventHandler; + d->inEventHandler = true; +#endif + + bool returnValue; + QT_TRY { + returnValue = notify(receiver, event); + } QT_CATCH (...) { + --threadData->loopLevel; + QT_RETHROW; + } + +#ifdef QT_JAMBI_BUILD + // Restore the previous state if the object was not deleted.. + if (!deleteWatch) { + d->inEventHandler = inEvent; + } + QObjectPrivate::resetDeleteWatch(d, oldDeleteWatch, deleteWatch); +#endif + --threadData->loopLevel; + return returnValue; +} + + +/*! + Sends \a event to \a receiver: \a {receiver}->event(\a event). + Returns the value that is returned from the receiver's event + handler. Note that this function is called for all events sent to + any object in any thread. + + For certain types of events (e.g. mouse and key events), + the event will be propagated to the receiver's parent and so on up to + the top-level object if the receiver is not interested in the event + (i.e., it returns false). + + There are five different ways that events can be processed; + reimplementing this virtual function is just one of them. All five + approaches are listed below: + \list 1 + \i Reimplementing paintEvent(), mousePressEvent() and so + on. This is the commonest, easiest and least powerful way. + + \i Reimplementing this function. This is very powerful, providing + complete control; but only one subclass can be active at a time. + + \i Installing an event filter on QCoreApplication::instance(). Such + an event filter is able to process all events for all widgets, so + it's just as powerful as reimplementing notify(); furthermore, it's + possible to have more than one application-global event filter. + Global event filters even see mouse events for + \l{QWidget::isEnabled()}{disabled widgets}. Note that application + event filters are only called for objects that live in the main + thread. + + \i Reimplementing QObject::event() (as QWidget does). If you do + this you get Tab key presses, and you get to see the events before + any widget-specific event filters. + + \i Installing an event filter on the object. Such an event filter gets all + the events, including Tab and Shift+Tab key press events, as long as they + do not change the focus widget. + \endlist + + \sa QObject::event(), installEventFilter() +*/ + +bool QCoreApplication::notify(QObject *receiver, QEvent *event) +{ + Q_D(QCoreApplication); + // no events are delivered after ~QCoreApplication() has started + if (QCoreApplicationPrivate::is_app_closing) + return true; + + if (receiver == 0) { // serious error + qWarning("QCoreApplication::notify: Unexpected null receiver"); + return true; + } + +#ifndef QT_NO_DEBUG + d->checkReceiverThread(receiver); +#endif + + return receiver->isWidgetType() ? false : d->notify_helper(receiver, event); +} + +bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiver, QEvent *event) +{ + if (receiver->d_func()->threadData == this->threadData) { + // application event filters are only called for objects in the GUI thread + for (int i = 0; i < eventFilters.size(); ++i) { + register QObject *obj = eventFilters.at(i); + if (!obj) + continue; + if (obj->d_func()->threadData != threadData) { + qWarning("QCoreApplication: Application event filter cannot be in a different thread."); + continue; + } + if (obj->eventFilter(receiver, event)) + return true; + } + } + return false; +} + +bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject *receiver, QEvent *event) +{ + Q_Q(QCoreApplication); + if (receiver != q) { + for (int i = 0; i < receiver->d_func()->eventFilters.size(); ++i) { + register QObject *obj = receiver->d_func()->eventFilters.at(i); + if (!obj) + continue; + if (obj->d_func()->threadData != receiver->d_func()->threadData) { + qWarning("QCoreApplication: Object event filter cannot be in a different thread."); + continue; + } + if (obj->eventFilter(receiver, event)) + return true; + } + } + return false; +} + +/*!\internal + + Helper function called by notify() + */ +bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event) +{ + // send to all application event filters + if (sendThroughApplicationEventFilters(receiver, event)) + return true; + // send to all receiver event filters + if (sendThroughObjectEventFilters(receiver, event)) + return true; + // deliver the event + return receiver->event(event); +} + +/*! + Returns true if an application object has not been created yet; + otherwise returns false. + + \sa closingDown() +*/ + +bool QCoreApplication::startingUp() +{ + return !QCoreApplicationPrivate::is_app_running; +} + +/*! + Returns true if the application objects are being destroyed; + otherwise returns false. + + \sa startingUp() +*/ + +bool QCoreApplication::closingDown() +{ + return QCoreApplicationPrivate::is_app_closing; +} + + +/*! + Processes all pending events for the calling thread according to + the specified \a flags until there are no more events to process. + + You can call this function occasionally when your program is busy + performing a long operation (e.g. copying a file). + + In event you are running a local loop which calls this function + continuously, without an event loop, the + \l{QEvent::DeferredDelete}{DeferredDelete} events will + not be processed. This can affect the behaviour of widgets, + e.g. QToolTip, that rely on \l{QEvent::DeferredDelete}{DeferredDelete} + events to function properly. An alternative would be to call + \l{QCoreApplication::sendPostedEvents()}{sendPostedEvents()} from + within that local loop. + + Calling this function processes events only for the calling thread. + + \threadsafe + + \sa exec(), QTimer, QEventLoop::processEvents(), flush(), sendPostedEvents() +*/ +void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + QThreadData *data = QThreadData::current(); + if (!data->eventDispatcher) + return; + if (flags & QEventLoop::DeferredDeletion) + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + data->eventDispatcher->processEvents(flags); +} + +/*! + \overload processEvents() + + Processes pending events for the calling thread for \a maxtime + milliseconds or until there are no more events to process, + whichever is shorter. + + You can call this function occasionally when you program is busy + doing a long operation (e.g. copying a file). + + Calling this function processes events only for the calling thread. + + \threadsafe + + \sa exec(), QTimer, QEventLoop::processEvents() +*/ +void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int maxtime) +{ + QThreadData *data = QThreadData::current(); + if (!data->eventDispatcher) + return; + QElapsedTimer start; + start.start(); + if (flags & QEventLoop::DeferredDeletion) + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + while (data->eventDispatcher->processEvents(flags & ~QEventLoop::WaitForMoreEvents)) { + if (start.elapsed() > maxtime) + break; + if (flags & QEventLoop::DeferredDeletion) + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } +} + +/***************************************************************************** + Main event loop wrappers + *****************************************************************************/ + +/*! + Enters the main event loop and waits until exit() is called. + Returns the value that was set to exit() (which is 0 if exit() is + called via quit()). + + It is necessary to call this function to start event handling. The + main event loop receives events from the window system and + dispatches these to the application widgets. + + To make your application perform idle processing (i.e. executing a + special function whenever there are no pending events), use a + QTimer with 0 timeout. More advanced idle processing schemes can + be achieved using processEvents(). + + We recommend that you connect clean-up code to the + \l{QCoreApplication::}{aboutToQuit()} signal, instead of putting it in + your application's \c{main()} function because on some platforms the + QCoreApplication::exec() call may not return. For example, on Windows + when the user logs off, the system terminates the process after Qt + closes all top-level windows. Hence, there is no guarantee that the + application will have time to exit its event loop and execute code at + the end of the \c{main()} function after the QCoreApplication::exec() + call. + + \sa quit(), exit(), processEvents(), QApplication::exec() +*/ +int QCoreApplication::exec() +{ + if (!QCoreApplicationPrivate::checkInstance("exec")) + return -1; + + QThreadData *threadData = self->d_func()->threadData; + if (threadData != QThreadData::current()) { + qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className()); + return -1; + } + if (!threadData->eventLoops.isEmpty()) { + qWarning("QCoreApplication::exec: The event loop is already running"); + return -1; + } + + threadData->quitNow = false; + QEventLoop eventLoop; + self->d_func()->in_exec = true; + self->d_func()->aboutToQuitEmitted = false; + int returnCode = eventLoop.exec(); + threadData->quitNow = false; + if (self) { + self->d_func()->in_exec = false; + if (!self->d_func()->aboutToQuitEmitted) + emit self->aboutToQuit(); + self->d_func()->aboutToQuitEmitted = true; + sendPostedEvents(0, QEvent::DeferredDelete); + } + + return returnCode; +} + + +/*! + Tells the application to exit with a return code. + + After this function has been called, the application leaves the + main event loop and returns from the call to exec(). The exec() + function returns \a returnCode. If the event loop is not running, + this function does nothing. + + By convention, a \a returnCode of 0 means success, and any non-zero + value indicates an error. + + Note that unlike the C library function of the same name, this + function \e does return to the caller -- it is event processing that + stops. + + \sa quit(), exec() +*/ +void QCoreApplication::exit(int returnCode) +{ + if (!self) + return; + QThreadData *data = self->d_func()->threadData; + data->quitNow = true; + for (int i = 0; i < data->eventLoops.size(); ++i) { + QEventLoop *eventLoop = data->eventLoops.at(i); + eventLoop->exit(returnCode); + } +} + +/***************************************************************************** + QCoreApplication management of posted events + *****************************************************************************/ + +/*! + \fn bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event) + + Sends event \a event directly to receiver \a receiver, using the + notify() function. Returns the value that was returned from the + event handler. + + The event is \e not deleted when the event has been sent. The normal + approach is to create the event on the stack, for example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 0 + + \sa postEvent(), notify() +*/ + +/*! + Adds the event \a event, with the object \a receiver as the + receiver of the event, to an event queue and returns immediately. + + The event must be allocated on the heap since the post event queue + will take ownership of the event and delete it once it has been + posted. It is \e {not safe} to access the event after + it has been posted. + + When control returns to the main event loop, all events that are + stored in the queue will be sent using the notify() function. + + Events are processed in the order posted. For more control over + the processing order, use the postEvent() overload below, which + takes a priority argument. This function posts all event with a + Qt::NormalEventPriority. + + \threadsafe + + \sa sendEvent(), notify(), sendPostedEvents() +*/ + +void QCoreApplication::postEvent(QObject *receiver, QEvent *event) +{ + postEvent(receiver, event, Qt::NormalEventPriority); +} + + +/*! + \overload postEvent() + \since 4.3 + + Adds the event \a event, with the object \a receiver as the + receiver of the event, to an event queue and returns immediately. + + The event must be allocated on the heap since the post event queue + will take ownership of the event and delete it once it has been + posted. It is \e {not safe} to access the event after + it has been posted. + + When control returns to the main event loop, all events that are + stored in the queue will be sent using the notify() function. + + Events are sorted in descending \a priority order, i.e. events + with a high \a priority are queued before events with a lower \a + priority. The \a priority can be any integer value, i.e. between + INT_MAX and INT_MIN, inclusive; see Qt::EventPriority for more + details. Events with equal \a priority will be processed in the + order posted. + + \threadsafe + + \sa sendEvent(), notify(), sendPostedEvents(), Qt::EventPriority +*/ +void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority) +{ + if (receiver == 0) { + qWarning("QCoreApplication::postEvent: Unexpected null receiver"); + delete event; + return; + } + + QThreadData * volatile * pdata = &receiver->d_func()->threadData; + QThreadData *data = *pdata; + if (!data) { + // posting during destruction? just delete the event to prevent a leak + delete event; + return; + } + + // lock the post event mutex + data->postEventList.mutex.lock(); + + // if object has moved to another thread, follow it + while (data != *pdata) { + data->postEventList.mutex.unlock(); + + data = *pdata; + if (!data) { + // posting during destruction? just delete the event to prevent a leak + delete event; + return; + } + + data->postEventList.mutex.lock(); + } + + QMutexUnlocker locker(&data->postEventList.mutex); + + // if this is one of the compressible events, do compression + if (receiver->d_func()->postedEvents + && self && self->compressEvent(event, receiver, &data->postEventList)) { + return; + } + + if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) { + // remember the current running eventloop for DeferredDelete + // events posted in the receiver's thread + event->d = reinterpret_cast(quintptr(data->loopLevel)); + } + + // delete the event on exceptions to protect against memory leaks till the event is + // properly owned in the postEventList + QScopedPointer eventDeleter(event); + if (data->postEventList.isEmpty() || data->postEventList.last().priority >= priority) { + // optimization: we can simply append if the last event in + // the queue has higher or equal priority + data->postEventList.append(QPostEvent(receiver, event, priority)); + } else { + // insert event in descending priority order, using upper + // bound for a given priority (to ensure proper ordering + // of events with the same priority) + QPostEventList::iterator begin = data->postEventList.begin() + + data->postEventList.insertionOffset, + end = data->postEventList.end(); + QPostEventList::iterator at = qUpperBound(begin, end, priority); + data->postEventList.insert(at, QPostEvent(receiver, event, priority)); + } + eventDeleter.take(); + event->posted = true; + ++receiver->d_func()->postedEvents; + data->canWait = false; + locker.unlock(); + + if (data->eventDispatcher) + data->eventDispatcher->wakeUp(); +} + +/*! + \internal + Returns true if \a event was compressed away (possibly deleted) and should not be added to the list. +*/ +bool QCoreApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents) +{ +#ifdef Q_WS_WIN + Q_ASSERT(event); + Q_ASSERT(receiver); + Q_ASSERT(postedEvents); + + // compress posted timers to this object. + if (event->type() == QEvent::Timer && receiver->d_func()->postedEvents > 0) { + int timerId = ((QTimerEvent *) event)->timerId(); + for (int i=0; isize(); ++i) { + const QPostEvent &e = postedEvents->at(i); + if (e.receiver == receiver && e.event && e.event->type() == QEvent::Timer + && ((QTimerEvent *) e.event)->timerId() == timerId) { + delete event; + return true; + } + } + } else +#endif + if ((event->type() == QEvent::DeferredDelete + || event->type() == QEvent::Quit) + && receiver->d_func()->postedEvents > 0) { + for (int i = 0; i < postedEvents->size(); ++i) { + const QPostEvent &cur = postedEvents->at(i); + if (cur.receiver != receiver + || cur.event == 0 + || cur.event->type() != event->type()) + continue; + // found an event for this receiver + delete event; + return true; + } + } + return false; +} + +/*! + \fn void QCoreApplication::sendPostedEvents() + \overload sendPostedEvents() + + Dispatches all posted events, i.e. empties the event queue. +*/ + +/*! + Immediately dispatches all events which have been previously queued + with QCoreApplication::postEvent() and which are for the object \a receiver + and have the event type \a event_type. + + Events from the window system are \e not dispatched by this + function, but by processEvents(). + + If \a receiver is null, the events of \a event_type are sent for all + objects. If \a event_type is 0, all the events are sent for \a receiver. + + \note This method must be called from the same thread as its QObject parameter, \a receiver. + + \sa flush(), postEvent() +*/ + +void QCoreApplication::sendPostedEvents(QObject *receiver, int event_type) +{ + QThreadData *data = QThreadData::current(); + + QCoreApplicationPrivate::sendPostedEvents(receiver, event_type, data); +} + +void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type, + QThreadData *data) +{ + if (event_type == -1) { + // we were called by an obsolete event dispatcher. + event_type = 0; + } + + if (receiver && receiver->d_func()->threadData != data) { + qWarning("QCoreApplication::sendPostedEvents: Cannot send " + "posted events for objects in another thread"); + return; + } + + ++data->postEventList.recursion; + +#ifdef QT3_SUPPORT + if (event_type == QEvent::ChildInserted) { + if (receiver) { + // optimize sendPostedEvents(w, QEvent::ChildInserted) calls away + receiver->d_func()->sendPendingChildInsertedEvents(); + --data->postEventList.recursion; + return; + } + + // ChildInserted events are sent in response to *Request + event_type = QEvent::ChildInsertedRequest; + } +#endif + + QMutexLocker locker(&data->postEventList.mutex); + + // by default, we assume that the event dispatcher can go to sleep after + // processing all events. if any new events are posted while we send + // events, canWait will be set to false. + data->canWait = (data->postEventList.size() == 0); + + if (data->postEventList.size() == 0 || (receiver && !receiver->d_func()->postedEvents)) { + --data->postEventList.recursion; + return; + } + + data->canWait = true; + + // okay. here is the tricky loop. be careful about optimizing + // this, it looks the way it does for good reasons. + int startOffset = data->postEventList.startOffset; + int &i = (!event_type && !receiver) ? data->postEventList.startOffset : startOffset; + data->postEventList.insertionOffset = data->postEventList.size(); + + while (i < data->postEventList.size()) { + // avoid live-lock + if (i >= data->postEventList.insertionOffset) + break; + + const QPostEvent &pe = data->postEventList.at(i); + ++i; + + if (!pe.event) + continue; + if ((receiver && receiver != pe.receiver) || (event_type && event_type != pe.event->type())) { + data->canWait = false; + continue; + } + + if (pe.event->type() == QEvent::DeferredDelete) { + // DeferredDelete events are only sent when we are explicitly asked to + // (s.a. QEvent::DeferredDelete), and then only if the event loop that + // posted the event has returned. + const bool allowDeferredDelete = + (quintptr(pe.event->d) > unsigned(data->loopLevel) + || (!quintptr(pe.event->d) && data->loopLevel > 0) + || (event_type == QEvent::DeferredDelete + && quintptr(pe.event->d) == unsigned(data->loopLevel))); + if (!allowDeferredDelete) { + // cannot send deferred delete + if (!event_type && !receiver) { + // don't lose the event + data->postEventList.append(pe); + const_cast(pe).event = 0; + } + continue; + } + } + + // first, we diddle the event so that we can deliver + // it, and that no one will try to touch it later. + pe.event->posted = false; + QEvent * e = pe.event; + QObject * r = pe.receiver; + + --r->d_func()->postedEvents; + Q_ASSERT(r->d_func()->postedEvents >= 0); + + // next, update the data structure so that we're ready + // for the next event. + const_cast(pe).event = 0; + + locker.unlock(); + // after all that work, it's time to deliver the event. +#ifdef QT_NO_EXCEPTIONS + QCoreApplication::sendEvent(r, e); +#else + try { + QCoreApplication::sendEvent(r, e); + } catch (...) { + delete e; + locker.relock(); + + // since we were interrupted, we need another pass to make sure we clean everything up + data->canWait = false; + + // uglehack: copied from below + --data->postEventList.recursion; + if (!data->postEventList.recursion && !data->canWait && data->eventDispatcher) + data->eventDispatcher->wakeUp(); + throw; // rethrow + } +#endif + + delete e; + locker.relock(); + + // careful when adding anything below this point - the + // sendEvent() call might invalidate any invariants this + // function depends on. + } + + --data->postEventList.recursion; + if (!data->postEventList.recursion && !data->canWait && data->eventDispatcher) + data->eventDispatcher->wakeUp(); + + // clear the global list, i.e. remove everything that was + // delivered. + if (!event_type && !receiver && data->postEventList.startOffset >= 0) { + const QPostEventList::iterator it = data->postEventList.begin(); + data->postEventList.erase(it, it + data->postEventList.startOffset); + data->postEventList.insertionOffset -= data->postEventList.startOffset; + Q_ASSERT(data->postEventList.insertionOffset >= 0); + data->postEventList.startOffset = 0; + } +} + +/*! + Removes all events posted using postEvent() for \a receiver. + + The events are \e not dispatched, instead they are removed from the + queue. You should never need to call this function. If you do call it, + be aware that killing events may cause \a receiver to break one or + more invariants. + + \threadsafe +*/ + +void QCoreApplication::removePostedEvents(QObject *receiver) +{ + removePostedEvents(receiver, 0); +} + +/*! + \overload removePostedEvents() + \since 4.3 + + Removes all events of the given \a eventType that were posted + using postEvent() for \a receiver. + + The events are \e not dispatched, instead they are removed from + the queue. You should never need to call this function. If you do + call it, be aware that killing events may cause \a receiver to + break one or more invariants. + + If \a receiver is null, the events of \a eventType are removed for + all objects. If \a eventType is 0, all the events are removed for + \a receiver. + + \threadsafe +*/ + +void QCoreApplication::removePostedEvents(QObject *receiver, int eventType) +{ +#ifdef QT3_SUPPORT + if (eventType == QEvent::ChildInserted) + eventType = QEvent::ChildInsertedRequest; +#endif + + QThreadData *data = receiver ? receiver->d_func()->threadData : QThreadData::current(); + QMutexLocker locker(&data->postEventList.mutex); + + // the QObject destructor calls this function directly. this can + // happen while the event loop is in the middle of posting events, + // and when we get here, we may not have any more posted events + // for this object. + if (receiver && !receiver->d_func()->postedEvents) + return; + + //we will collect all the posted events for the QObject + //and we'll delete after the mutex was unlocked + QVarLengthArray events; + int n = data->postEventList.size(); + int j = 0; + + for (int i = 0; i < n; ++i) { + const QPostEvent &pe = data->postEventList.at(i); + + if ((!receiver || pe.receiver == receiver) + && (pe.event && (eventType == 0 || pe.event->type() == eventType))) { + --pe.receiver->d_func()->postedEvents; +#ifdef QT3_SUPPORT + if (pe.event->type() == QEvent::ChildInsertedRequest) + pe.receiver->d_func()->pendingChildInsertedEvents.clear(); +#endif + pe.event->posted = false; + events.append(pe.event); + const_cast(pe).event = 0; + } else if (!data->postEventList.recursion) { + if (i != j) + data->postEventList.swap(i, j); + ++j; + } + } + +#ifdef QT_DEBUG + if (receiver && eventType == 0) { + Q_ASSERT(!receiver->d_func()->postedEvents); + } +#endif + + if (!data->postEventList.recursion) { + // truncate list + data->postEventList.erase(data->postEventList.begin() + j, data->postEventList.end()); + } + + locker.unlock(); + for (int i = 0; i < events.count(); ++i) { + delete events[i]; + } +} + +/*! + Removes \a event from the queue of posted events, and emits a + warning message if appropriate. + + \warning This function can be \e really slow. Avoid using it, if + possible. + + \threadsafe +*/ + +void QCoreApplicationPrivate::removePostedEvent(QEvent * event) +{ + if (!event || !event->posted) + return; + + QThreadData *data = QThreadData::current(); + + QMutexLocker locker(&data->postEventList.mutex); + + if (data->postEventList.size() == 0) { +#if defined(QT_DEBUG) + qDebug("QCoreApplication::removePostedEvent: Internal error: %p %d is posted", + (void*)event, event->type()); + return; +#endif + } + + for (int i = 0; i < data->postEventList.size(); ++i) { + const QPostEvent & pe = data->postEventList.at(i); + if (pe.event == event) { +#ifndef QT_NO_DEBUG + qWarning("QCoreApplication::removePostedEvent: Event of type %d deleted while posted to %s %s", + event->type(), + pe.receiver->metaObject()->className(), + pe.receiver->objectName().toLocal8Bit().data()); +#endif + --pe.receiver->d_func()->postedEvents; + pe.event->posted = false; + delete pe.event; + const_cast(pe).event = 0; + return; + } + } +} + +/*!\reimp + +*/ +bool QCoreApplication::event(QEvent *e) +{ + if (e->type() == QEvent::Quit) { + quit(); + return true; + } + return QObject::event(e); +} + +/*! \enum QCoreApplication::Encoding + + This enum type defines the 8-bit encoding of character string + arguments to translate(): + + \value CodecForTr The encoding specified by + QTextCodec::codecForTr() (Latin-1 if none has + been set). + \value UnicodeUTF8 UTF-8. + \value DefaultCodec (Obsolete) Use CodecForTr instead. + + \sa QObject::tr(), QObject::trUtf8(), QString::fromUtf8() +*/ + +/*! + Tells the application to exit with return code 0 (success). + Equivalent to calling QCoreApplication::exit(0). + + It's common to connect the QApplication::lastWindowClosed() signal + to quit(), and you also often connect e.g. QAbstractButton::clicked() or + signals in QAction, QMenu, or QMenuBar to it. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 1 + + \sa exit(), aboutToQuit(), QApplication::lastWindowClosed() +*/ + +void QCoreApplication::quit() +{ + exit(0); +} + +/*! + \fn void QCoreApplication::aboutToQuit() + + This signal is emitted when the application is about to quit the + main event loop, e.g. when the event loop level drops to zero. + This may happen either after a call to quit() from inside the + application or when the users shuts down the entire desktop session. + + The signal is particularly useful if your application has to do some + last-second cleanup. Note that no user interaction is possible in + this state. + + \sa quit() +*/ + +#ifndef QT_NO_TRANSLATION +/*! + Adds the translation file \a translationFile to the list of + translation files to be used for translations. + + Multiple translation files can be installed. Translations are + searched for in the reverse order in which they were installed, + so the most recently installed translation file is searched first + and the first translation file installed is searched last. + The search stops as soon as a translation containing a matching + string is found. + + Installing or removing a QTranslator, or changing an installed QTranslator + generates a \l{QEvent::LanguageChange}{LanguageChange} event for the + QCoreApplication instance. A QApplication instance will propagate the event + to all toplevel windows, where a reimplementation of changeEvent can + re-translate the user interface by passing user-visible strings via the + tr() function to the respective property setters. User-interface classes + generated by \l{Qt Designer} provide a \c retranslateUi() function that can be + called. + + \sa removeTranslator() translate() QTranslator::load() {Dynamic Translation} +*/ + +void QCoreApplication::installTranslator(QTranslator *translationFile) +{ + if (!translationFile) + return; + + if (!QCoreApplicationPrivate::checkInstance("installTranslator")) + return; + QCoreApplicationPrivate *d = self->d_func(); + d->translators.prepend(translationFile); + +#ifndef QT_NO_TRANSLATION_BUILDER + if (translationFile->isEmpty()) + return; +#endif + + QEvent ev(QEvent::LanguageChange); + QCoreApplication::sendEvent(self, &ev); +} + +/*! + Removes the translation file \a translationFile from the list of + translation files used by this application. (It does not delete the + translation file from the file system.) + + \sa installTranslator() translate(), QObject::tr() +*/ + +void QCoreApplication::removeTranslator(QTranslator *translationFile) +{ + if (!translationFile) + return; + if (!QCoreApplicationPrivate::checkInstance("removeTranslator")) + return; + QCoreApplicationPrivate *d = self->d_func(); + if (d->translators.removeAll(translationFile) && !self->closingDown()) { + QEvent ev(QEvent::LanguageChange); + QCoreApplication::sendEvent(self, &ev); + } +} + +/*! + \overload translate() +*/ +QString QCoreApplication::translate(const char *context, const char *sourceText, + const char *disambiguation, Encoding encoding) +{ + return translate(context, sourceText, disambiguation, encoding, -1); +} + +static void replacePercentN(QString *result, int n) +{ + if (n >= 0) { + int percentPos = 0; + int len = 0; + while ((percentPos = result->indexOf(QLatin1Char('%'), percentPos + len)) != -1) { + len = 1; + QString fmt; + if (result->at(percentPos + len) == QLatin1Char('L')) { + ++len; + fmt = QLatin1String("%L1"); + } else { + fmt = QLatin1String("%1"); + } + if (result->at(percentPos + len) == QLatin1Char('n')) { + fmt = fmt.arg(n); + ++len; + result->replace(percentPos, len, fmt); + len = fmt.length(); + } + } + } +} + +/*! + \reentrant + \since 4.5 + + Returns the translation text for \a sourceText, by querying the + installed translation files. The translation files are searched + from the most recently installed file back to the first + installed file. + + QObject::tr() and QObject::trUtf8() provide this functionality + more conveniently. + + \a context is typically a class name (e.g., "MyDialog") and \a + sourceText is either English text or a short identifying text. + + \a disambiguation is an identifying string, for when the same \a + sourceText is used in different roles within the same context. By + default, it is null. + + See the \l QTranslator and \l QObject::tr() documentation for + more information about contexts, disambiguations and comments. + + \a encoding indicates the 8-bit encoding of character strings. + + \a n is used in conjunction with \c %n to support plural forms. + See QObject::tr() for details. + + If none of the translation files contain a translation for \a + sourceText in \a context, this function returns a QString + equivalent of \a sourceText. The encoding of \a sourceText is + specified by \e encoding; it defaults to CodecForTr. + + This function is not virtual. You can use alternative translation + techniques by subclassing \l QTranslator. + + \warning This method is reentrant only if all translators are + installed \e before calling this method. Installing or removing + translators while performing translations is not supported. Doing + so will most likely result in crashes or other undesirable + behavior. + + \sa QObject::tr() installTranslator() QTextCodec::codecForTr() +*/ + + +QString QCoreApplication::translate(const char *context, const char *sourceText, + const char *disambiguation, Encoding encoding, int n) +{ + QString result; + + if (!sourceText) + return result; + + if (self && !self->d_func()->translators.isEmpty()) { + QList::ConstIterator it; + QTranslator *translationFile; + for (it = self->d_func()->translators.constBegin(); it != self->d_func()->translators.constEnd(); ++it) { + translationFile = *it; + result = translationFile->translate(context, sourceText, disambiguation, n); + if (!result.isEmpty()) + break; + } + } + + if (result.isEmpty()) { +#ifdef QT_NO_TEXTCODEC + Q_UNUSED(encoding) +#else + if (encoding == UnicodeUTF8) + result = QString::fromUtf8(sourceText); + else if (QTextCodec::codecForTr() != 0) + result = QTextCodec::codecForTr()->toUnicode(sourceText); + else +#endif + result = QString::fromLatin1(sourceText); + } + + replacePercentN(&result, n); + return result; +} + +// Declared in qglobal.h +QString qtTrId(const char *id, int n) +{ + return QCoreApplication::translate(0, id, 0, QCoreApplication::UnicodeUTF8, n); +} + +bool QCoreApplicationPrivate::isTranslatorInstalled(QTranslator *translator) +{ + return QCoreApplication::self + && QCoreApplication::self->d_func()->translators.contains(translator); +} + +#endif //QT_NO_TRANSLATE + +/*! + Returns the directory that contains the application executable. + + For example, if you have installed Qt in the \c{C:\Trolltech\Qt} + directory, and you run the \c{regexp} example, this function will + return "C:/Trolltech/Qt/examples/tools/regexp". + + On Mac OS X this will point to the directory actually containing the + executable, which may be inside of an application bundle (if the + application is bundled). + + \warning On Linux, this function will try to get the path from the + \c {/proc} file system. If that fails, it assumes that \c + {argv[0]} contains the absolute file name of the executable. The + function also assumes that the current directory has not been + changed by the application. + + In Symbian this function will return the application private directory, + not the path to executable itself, as those are always in \c {/sys/bin}. + If the application is in a read only drive, i.e. ROM, then the private path + on the system drive will be returned. + + \sa applicationFilePath() +*/ +QString QCoreApplication::applicationDirPath() +{ + if (!self) { + qWarning("QCoreApplication::applicationDirPath: Please instantiate the QApplication object first"); + return QString(); + } + + QCoreApplicationPrivate *d = self->d_func(); + if (d->cachedApplicationDirPath.isNull()) +#if defined(Q_OS_SYMBIAN) + { + QString appPath; + RFs& fs = qt_s60GetRFs(); + TChar driveChar; + QChar qDriveChar; + driveChar = (RProcess().FileName())[0]; + + //Check if the process is installed in a read only drive (typically ROM), + //and use the system drive (typically C:) if so. + TInt drive; + TDriveInfo driveInfo; + TInt err = fs.CharToDrive(driveChar, drive); + if (err == KErrNone) { + err = fs.Drive(driveInfo, drive); + } + if (err != KErrNone || (driveInfo.iDriveAtt & KDriveAttRom) || (driveInfo.iMediaAtt + & KMediaAttWriteProtected)) { + if(!PtrGetSystemDrive) + PtrGetSystemDrive = reinterpret_cast(qt_resolveS60PluginFunc(S60Plugin_GetSystemDrive)); + Q_ASSERT(PtrGetSystemDrive); + drive = PtrGetSystemDrive(fs); + fs.DriveToChar(drive, driveChar); + } + + qDriveChar = QChar(QLatin1Char(driveChar)).toUpper(); + + TFileName privatePath; + fs.PrivatePath(privatePath); + appPath = qt_TDesC2QString(privatePath); + appPath.prepend(QLatin1Char(':')).prepend(qDriveChar); + + // Create the appPath if it doesn't exist. Non-existing appPath will cause + // Platform Security violations later on if the app doesn't have AllFiles capability. + err = fs.CreatePrivatePath(drive); + if (err != KErrNone) + qWarning("QCoreApplication::applicationDirPath: Failed to create private path."); + + d->cachedApplicationDirPath = QFileInfo(appPath).path(); + } +#else + d->cachedApplicationDirPath = QFileInfo(applicationFilePath()).path(); +#endif + return d->cachedApplicationDirPath; +} + +/*! + Returns the file path of the application executable. + + For example, if you have installed Qt in the \c{/usr/local/qt} + directory, and you run the \c{regexp} example, this function will + return "/usr/local/qt/examples/tools/regexp/regexp". + + \warning On Linux, this function will try to get the path from the + \c {/proc} file system. If that fails, it assumes that \c + {argv[0]} contains the absolute file name of the executable. The + function also assumes that the current directory has not been + changed by the application. + + \sa applicationDirPath() +*/ +QString QCoreApplication::applicationFilePath() +{ + if (!self) { + qWarning("QCoreApplication::applicationFilePath: Please instantiate the QApplication object first"); + return QString(); + } + + QCoreApplicationPrivate *d = self->d_func(); + if (!d->cachedApplicationFilePath.isNull()) + return d->cachedApplicationFilePath; + +#if defined(Q_WS_WIN) + d->cachedApplicationFilePath = QFileInfo(qAppFileName()).filePath(); + return d->cachedApplicationFilePath; +#elif defined(Q_WS_MAC) + QString qAppFileName_str = qAppFileName(); + if(!qAppFileName_str.isEmpty()) { + QFileInfo fi(qAppFileName_str); + d->cachedApplicationFilePath = fi.exists() ? fi.canonicalFilePath() : QString(); + return d->cachedApplicationFilePath; + } +#endif +#if defined(Q_OS_SYMBIAN) + QString appPath; + RProcess proc; + TInt err = proc.Open(proc.Id()); + if (err == KErrNone) { + TFileName procName = proc.FileName(); + appPath.append(QString(reinterpret_cast(procName.Ptr()), procName.Length())); + proc.Close(); + } + + d->cachedApplicationFilePath = appPath; + return d->cachedApplicationFilePath; + +#elif defined( Q_OS_UNIX ) +# ifdef Q_OS_LINUX + // Try looking for a /proc//exe symlink first which points to + // the absolute path of the executable + QFileInfo pfi(QString::fromLatin1("/proc/%1/exe").arg(getpid())); + if (pfi.exists() && pfi.isSymLink()) { + d->cachedApplicationFilePath = pfi.canonicalFilePath(); + return d->cachedApplicationFilePath; + } +# endif + + QString argv0 = QFile::decodeName(QByteArray(argv()[0])); + QString absPath; + + if (!argv0.isEmpty() && argv0.at(0) == QLatin1Char('/')) { + /* + If argv0 starts with a slash, it is already an absolute + file path. + */ + absPath = argv0; + } else if (argv0.contains(QLatin1Char('/'))) { + /* + If argv0 contains one or more slashes, it is a file path + relative to the current directory. + */ + absPath = QDir::current().absoluteFilePath(argv0); + } else { + /* + Otherwise, the file path has to be determined using the + PATH environment variable. + */ + QByteArray pEnv = qgetenv("PATH"); + QDir currentDir = QDir::current(); + QStringList paths = QString::fromLocal8Bit(pEnv.constData()).split(QLatin1Char(':')); + for (QStringList::const_iterator p = paths.constBegin(); p != paths.constEnd(); ++p) { + if ((*p).isEmpty()) + continue; + QString candidate = currentDir.absoluteFilePath(*p + QLatin1Char('/') + argv0); + QFileInfo candidate_fi(candidate); + if (candidate_fi.exists() && !candidate_fi.isDir()) { + absPath = candidate; + break; + } + } + } + + absPath = QDir::cleanPath(absPath); + + QFileInfo fi(absPath); + d->cachedApplicationFilePath = fi.exists() ? fi.canonicalFilePath() : QString(); + return d->cachedApplicationFilePath; +#endif +} + +/*! + \since 4.4 + + Returns the current process ID for the application. +*/ +qint64 QCoreApplication::applicationPid() +{ +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) + return GetCurrentProcessId(); +#elif defined(Q_OS_VXWORKS) + return (pid_t) taskIdCurrent; +#else + return getpid(); +#endif +} + +/*! + \obsolete + + Use arguments().size() instead. +*/ +int QCoreApplication::argc() +{ + if (!self) { + qWarning("QCoreApplication::argc: Please instantiate the QApplication object first"); + return 0; + } + return self->d_func()->argc; +} + + +/*! + \obsolete + + Use arguments() instead. +*/ +char **QCoreApplication::argv() +{ + if (!self) { + qWarning("QCoreApplication::argv: Please instantiate the QApplication object first"); + return 0; + } + return self->d_func()->argv; +} + +/*! + \since 4.1 + + Returns the list of command-line arguments. + + Usually arguments().at(0) is the program name, arguments().at(1) + is the first argument, and arguments().last() is the last + argument. See the note below about Windows. + + Calling this function is slow - you should store the result in a variable + when parsing the command line. + + \warning On Unix, this list is built from the argc and argv parameters passed + to the constructor in the main() function. The string-data in argv is + interpreted using QString::fromLocal8Bit(); hence it is not possible to + pass, for example, Japanese command line arguments on a system that runs in a + Latin1 locale. Most modern Unix systems do not have this limitation, as they are + Unicode-based. + + On NT-based Windows, this limitation does not apply either. + On Windows, the arguments() are not built from the contents of argv/argc, as + the content does not support Unicode. Instead, the arguments() are constructed + from the return value of + \l{http://msdn2.microsoft.com/en-us/library/ms683156(VS.85).aspx}{GetCommandLine()}. + As a result of this, the string given by arguments().at(0) might not be + the program name on Windows, depending on how the application was started. + + For Symbian applications started with \c RApaLsSession::StartApp one can specify + arguments using \c CApaCommandLine::SetTailEndL function. Such arguments are only + available via this method; they will not be passed to \c main function. Also note + that only 8-bit string data set with \c CApaCommandLine::SetTailEndL is supported + by this function. + + \sa applicationFilePath() +*/ + +QStringList QCoreApplication::arguments() +{ + QStringList list; + + if (!self) { + qWarning("QCoreApplication::arguments: Please instantiate the QApplication object first"); + return list; + } +#ifdef Q_OS_WIN + QString cmdline = QString::fromWCharArray(GetCommandLine()); + +#if defined(Q_OS_WINCE) + wchar_t tempFilename[MAX_PATH+1]; + if (GetModuleFileName(0, tempFilename, MAX_PATH)) { + tempFilename[MAX_PATH] = 0; + cmdline.prepend(QLatin1Char('\"') + QString::fromWCharArray(tempFilename) + QLatin1String("\" ")); + } +#endif // Q_OS_WINCE + + list = qWinCmdArgs(cmdline); + if (self->d_func()->application_type) { // GUI app? Skip known - see qapplication.cpp + QStringList stripped; + for (int a = 0; a < list.count(); ++a) { + QString arg = list.at(a); + QByteArray l1arg = arg.toLatin1(); + if (l1arg == "-qdevel" || + l1arg == "-qdebug" || + l1arg == "-reverse" || + l1arg == "-stylesheet" || + l1arg == "-widgetcount") + ; + else if (l1arg.startsWith("-style=") || + l1arg.startsWith("-qmljsdebugger=")) + ; + else if (l1arg == "-style" || + l1arg == "-session" || + l1arg == "-graphicssystem" || + l1arg == "-testability") + ++a; + else + stripped += arg; + } + list = stripped; + } +#else + const int ac = self->d_func()->argc; + char ** const av = self->d_func()->argv; + for (int a = 0; a < ac; ++a) { + list << QString::fromLocal8Bit(av[a]); + } +#endif + + return list; +} + +/*! + \property QCoreApplication::organizationName + \brief the name of the organization that wrote this application + + The value is used by the QSettings class when it is constructed + using the empty constructor. This saves having to repeat this + information each time a QSettings object is created. + + On Mac, QSettings uses organizationDomain() as the organization + if it's not an empty string; otherwise it uses + organizationName(). On all other platforms, QSettings uses + organizationName() as the organization. + + \sa organizationDomain applicationName +*/ + +void QCoreApplication::setOrganizationName(const QString &orgName) +{ + coreappdata()->orgName = orgName; +} + +QString QCoreApplication::organizationName() +{ + return coreappdata()->orgName; +} + +/*! + \property QCoreApplication::organizationDomain + \brief the Internet domain of the organization that wrote this application + + The value is used by the QSettings class when it is constructed + using the empty constructor. This saves having to repeat this + information each time a QSettings object is created. + + On Mac, QSettings uses organizationDomain() as the organization + if it's not an empty string; otherwise it uses organizationName(). + On all other platforms, QSettings uses organizationName() as the + organization. + + \sa organizationName applicationName applicationVersion +*/ +void QCoreApplication::setOrganizationDomain(const QString &orgDomain) +{ + coreappdata()->orgDomain = orgDomain; +} + +QString QCoreApplication::organizationDomain() +{ + return coreappdata()->orgDomain; +} + +/*! + \property QCoreApplication::applicationName + \brief the name of this application + + The value is used by the QSettings class when it is constructed + using the empty constructor. This saves having to repeat this + information each time a QSettings object is created. + + \sa organizationName organizationDomain applicationVersion +*/ +void QCoreApplication::setApplicationName(const QString &application) +{ + coreappdata()->application = application; +} + +QString QCoreApplication::applicationName() +{ + return coreappdata()->application; +} + +/*! + \property QCoreApplication::applicationVersion + \since 4.4 + \brief the version of this application + + \sa applicationName organizationName organizationDomain +*/ +void QCoreApplication::setApplicationVersion(const QString &version) +{ + coreappdata()->applicationVersion = version; +} + +QString QCoreApplication::applicationVersion() +{ + return coreappdata()->applicationVersion; +} + +#ifndef QT_NO_LIBRARY + +Q_GLOBAL_STATIC_WITH_ARGS(QMutex, libraryPathMutex, (QMutex::Recursive)) + +/*! + Returns a list of paths that the application will search when + dynamically loading libraries. + + Qt provides default library paths, but they can also be set using + a \l{Using qt.conf}{qt.conf} file. Paths specified in this file + will override default values. + + This list will include the installation directory for plugins if + it exists (the default installation directory for plugins is \c + INSTALL/plugins, where \c INSTALL is the directory where Qt was + installed). The directory of the application executable (NOT the + working directory) is always added, as well as the colon separated + entries of the QT_PLUGIN_PATH environment variable. + + If you want to iterate over the list, you can use the \l foreach + pseudo-keyword: + + \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 2 + + \sa setLibraryPaths(), addLibraryPath(), removeLibraryPath(), QLibrary, + {How to Create Qt Plugins} +*/ +QStringList QCoreApplication::libraryPaths() +{ + QMutexLocker locker(libraryPathMutex()); + if (!coreappdata()->app_libpaths) { + QStringList *app_libpaths = coreappdata()->app_libpaths = new QStringList; + QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath); +#if defined(Q_OS_SYMBIAN) + // Add existing path on all drives for relative PluginsPath in Symbian + if (installPathPlugins.at(1) != QChar(QLatin1Char(':'))) { + QString tempPath = installPathPlugins; + if (tempPath.at(tempPath.length() - 1) != QDir::separator()) { + tempPath += QDir::separator(); + } + RFs& fs = qt_s60GetRFs(); + TPtrC tempPathPtr(reinterpret_cast (tempPath.constData())); + TFindFile finder(fs); + TInt err = finder.FindByDir(tempPathPtr, tempPathPtr); + while (err == KErrNone) { + QString foundDir(reinterpret_cast(finder.File().Ptr()), + finder.File().Length()); + foundDir = QDir(foundDir).canonicalPath(); + if (!app_libpaths->contains(foundDir)) + app_libpaths->append(foundDir); + err = finder.Find(); + } + } +#else + if (QFile::exists(installPathPlugins)) { + // Make sure we convert from backslashes to slashes. + installPathPlugins = QDir(installPathPlugins).canonicalPath(); + if (!app_libpaths->contains(installPathPlugins)) + app_libpaths->append(installPathPlugins); + } +#endif + + // If QCoreApplication is not yet instantiated, + // make sure we add the application path when we construct the QCoreApplication + if (self) self->d_func()->appendApplicationPathToLibraryPaths(); + + const QByteArray libPathEnv = qgetenv("QT_PLUGIN_PATH"); + if (!libPathEnv.isEmpty()) { +#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN) + QLatin1Char pathSep(';'); +#else + QLatin1Char pathSep(':'); +#endif + QStringList paths = QString::fromLatin1(libPathEnv).split(pathSep, QString::SkipEmptyParts); + for (QStringList::const_iterator it = paths.constBegin(); it != paths.constEnd(); ++it) { + QString canonicalPath = QDir(*it).canonicalPath(); + if (!canonicalPath.isEmpty() + && !app_libpaths->contains(canonicalPath)) { + app_libpaths->append(canonicalPath); + } + } + } + } + return *(coreappdata()->app_libpaths); +} + + + +/*! + + Sets the list of directories to search when loading libraries to + \a paths. All existing paths will be deleted and the path list + will consist of the paths given in \a paths. + + In Symbian this function is only useful for setting paths for + finding Qt extension plugin stubs, since the OS can only + load libraries from the \c{/sys/bin} directory. + + \sa libraryPaths(), addLibraryPath(), removeLibraryPath(), QLibrary + */ +void QCoreApplication::setLibraryPaths(const QStringList &paths) +{ + QMutexLocker locker(libraryPathMutex()); + if (!coreappdata()->app_libpaths) + coreappdata()->app_libpaths = new QStringList; + *(coreappdata()->app_libpaths) = paths; + locker.unlock(); + QFactoryLoader::refreshAll(); +} + +/*! + Prepends \a path to the beginning of the library path list, ensuring that + it is searched for libraries first. If \a path is empty or already in the + path list, the path list is not changed. + + The default path list consists of a single entry, the installation + directory for plugins. The default installation directory for plugins + is \c INSTALL/plugins, where \c INSTALL is the directory where Qt was + installed. + + In Symbian this function is only useful for adding paths for + finding Qt extension plugin stubs, since the OS can only + load libraries from the \c{/sys/bin} directory. + + \sa removeLibraryPath(), libraryPaths(), setLibraryPaths() + */ +void QCoreApplication::addLibraryPath(const QString &path) +{ + if (path.isEmpty()) + return; + + QMutexLocker locker(libraryPathMutex()); + + // make sure that library paths is initialized + libraryPaths(); + + QString canonicalPath = QDir(path).canonicalPath(); + if (!canonicalPath.isEmpty() + && !coreappdata()->app_libpaths->contains(canonicalPath)) { + coreappdata()->app_libpaths->prepend(canonicalPath); + locker.unlock(); + QFactoryLoader::refreshAll(); + } +} + +/*! + Removes \a path from the library path list. If \a path is empty or not + in the path list, the list is not changed. + + \sa addLibraryPath(), libraryPaths(), setLibraryPaths() +*/ +void QCoreApplication::removeLibraryPath(const QString &path) +{ + if (path.isEmpty()) + return; + + QMutexLocker locker(libraryPathMutex()); + + // make sure that library paths is initialized + libraryPaths(); + + QString canonicalPath = QDir(path).canonicalPath(); + coreappdata()->app_libpaths->removeAll(canonicalPath); + QFactoryLoader::refreshAll(); +} + +#endif //QT_NO_LIBRARY + +/*! + \typedef QCoreApplication::EventFilter + + A function with the following signature that can be used as an + event filter: + + \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 3 + + \sa setEventFilter() +*/ + +/*! + \fn EventFilter QCoreApplication::setEventFilter(EventFilter filter) + + Replaces the event filter function for the QCoreApplication with + \a filter and returns the pointer to the replaced event filter + function. Only the current event filter function is called. If you + want to use both filter functions, save the replaced EventFilter + in a place where yours can call it. + + The event filter function set here is called for all messages + received by all threads meant for all Qt objects. It is \e not + called for messages that are not meant for Qt objects. + + The event filter function should return true if the message should + be filtered, (i.e. stopped). It should return false to allow + processing the message to continue. + + By default, no event filter function is set (i.e., this function + returns a null EventFilter the first time it is called). + + \note The filter function set here receives native messages, + i.e. MSG or XEvent structs, that are going to Qt objects. It is + called by QCoreApplication::filterEvent(). If the filter function + returns false to indicate the message should be processed further, + the native message can then be translated into a QEvent and + handled by the standard Qt \l{QEvent} {event} filering, e.g. + QObject::installEventFilter(). + + \note The filter function set here is different form the filter + function set via QAbstractEventDispatcher::setEventFilter(), which + gets all messages received by its thread, even messages meant for + objects that are not handled by Qt. + + \sa QObject::installEventFilter(), QAbstractEventDispatcher::setEventFilter() +*/ +QCoreApplication::EventFilter +QCoreApplication::setEventFilter(QCoreApplication::EventFilter filter) +{ + Q_D(QCoreApplication); + EventFilter old = d->eventFilter; + d->eventFilter = filter; + return old; +} + +/*! + Sends \a message through the event filter that was set by + setEventFilter(). If no event filter has been set, this function + returns false; otherwise, this function returns the result of the + event filter function in the \a result parameter. + + \sa setEventFilter() +*/ +bool QCoreApplication::filterEvent(void *message, long *result) +{ + Q_D(QCoreApplication); + if (result) + *result = 0; + if (d->eventFilter) + return d->eventFilter(message, result); +#ifdef Q_OS_WIN + return winEventFilter(reinterpret_cast(message), result); +#else + return false; +#endif +} + +/*! + This function returns true if there are pending events; otherwise + returns false. Pending events can be either from the window + system or posted events using postEvent(). + + \sa QAbstractEventDispatcher::hasPendingEvents() +*/ +bool QCoreApplication::hasPendingEvents() +{ + QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(); + if (eventDispatcher) + return eventDispatcher->hasPendingEvents(); + return false; +} + +#ifdef QT3_SUPPORT +/*! \fn void QCoreApplication::lock() + + In Qt 3, this function locked the Qt library mutex, allowing + non-GUI threads to perform basic printing operations using + QPainter. + + In Qt 4, this is no longer supported, since painting is only + supported from within a paint event handler. This function does + nothing. + + \sa QWidget::paintEvent() +*/ + +/*! \fn void QCoreApplication::unlock(bool wakeUpGui) + + In Qt 3, this function unlocked the Qt library mutex. The mutex + allowed non-GUI threads to perform basic printing operations + using QPainter. + + In Qt 4, this is no longer supported, since painting is only + supported from within a paint event handler. This function does + nothing. +*/ + +/*! \fn bool QCoreApplication::locked() + + This function does nothing. It is there to keep old code working. + It always returns false. + + See lock() for details. +*/ + +/*! \fn bool QCoreApplication::tryLock() + + This function does nothing. It is there to keep old code working. + It always returns false. + + See lock() for details. +*/ + +/*! \fn void QCoreApplication::processOneEvent() + \obsolete + + Waits for an event to occur, processes it, then returns. + + This function is useful for adapting Qt to situations where the + event processing must be grafted onto existing program loops. + + Using this function in new applications may be an indication of design + problems. + + \sa processEvents(), exec(), QTimer +*/ + +/*! \obsolete + + This function enters the main event loop (recursively). Do not call + it unless you really know what you are doing. +*/ +int QCoreApplication::enter_loop() +{ + if (!QCoreApplicationPrivate::checkInstance("enter_loop")) + return -1; + if (QThreadData::current() != self->d_func()->threadData) { + qWarning("QCoreApplication::enter_loop: Must be called from the main thread"); + return -1; + } + QEventLoop eventLoop; + int returnCode = eventLoop.exec(); + return returnCode; +} + +/*! \obsolete + + This function exits from a recursive call to the main event loop. + Do not call it unless you are an expert. +*/ +void QCoreApplication::exit_loop() +{ + if (!QCoreApplicationPrivate::checkInstance("exit_loop")) + return; + QThreadData *data = QThreadData::current(); + if (data != self->d_func()->threadData) { + qWarning("QCoreApplication::exit_loop: Must be called from the main thread"); + return; + } + if (!data->eventLoops.isEmpty()) + data->eventLoops.top()->exit(); +} + +/*! \obsolete + + Returns the current loop level. +*/ +int QCoreApplication::loopLevel() +{ + if (!QCoreApplicationPrivate::checkInstance("loopLevel")) + return -1; + return self->d_func()->threadData->eventLoops.size(); +} +#endif + +/* + \fn void QCoreApplication::watchUnixSignal(int signal, bool watch) + \internal +*/ + +/*! + \fn void QCoreApplication::unixSignal(int number) + \internal + + This signal is emitted whenever a Unix signal is received by the + application. The Unix signal received is specified by its \a number. +*/ + +/*! + \fn void qAddPostRoutine(QtCleanUpFunction ptr) + \relates QCoreApplication + + Adds a global routine that will be called from the QApplication + destructor. This function is normally used to add cleanup routines + for program-wide functionality. + + The function specified by \a ptr should take no arguments and should + return nothing. For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 4 + + Note that for an application- or module-wide cleanup, + qAddPostRoutine() is often not suitable. For example, if the + program is split into dynamically loaded modules, the relevant + module may be unloaded long before the QApplication destructor is + called. + + For modules and libraries, using a reference-counted + initialization manager or Qt's parent-child deletion mechanism may + be better. Here is an example of a private class that uses the + parent-child mechanism to call a cleanup function at the right + time: + + \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 5 + + By selecting the right parent object, this can often be made to + clean up the module's data at the right moment. +*/ + +/*! + \macro Q_DECLARE_TR_FUNCTIONS(context) + \relates QCoreApplication + + The Q_DECLARE_TR_FUNCTIONS() macro declares and implements two + translation functions, \c tr() and \c trUtf8(), with these + signatures: + + \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 6 + + This macro is useful if you want to use QObject::tr() or + QObject::trUtf8() in classes that don't inherit from QObject. + + Q_DECLARE_TR_FUNCTIONS() must appear at the very top of the + class definition (before the first \c{public:} or \c{protected:}). + For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qcoreapplication.cpp 7 + + The \a context parameter is normally the class name, but it can + be any string. + + \sa Q_OBJECT, QObject::tr(), QObject::trUtf8() +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h new file mode 100644 index 0000000000..3957158d83 --- /dev/null +++ b/src/corelib/kernel/qcoreapplication.h @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QCOREAPPLICATION_H +#define QCOREAPPLICATION_H + +#include +#include +#include + +#ifdef QT_INCLUDE_COMPAT +#include +#endif + +#if defined(Q_WS_WIN) && !defined(tagMSG) +typedef struct tagMSG MSG; +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QCoreApplicationPrivate; +class QTextCodec; +class QTranslator; +class QPostEventList; +class QStringList; + +#define qApp QCoreApplication::instance() + +class Q_CORE_EXPORT QCoreApplication : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString applicationName READ applicationName WRITE setApplicationName) + Q_PROPERTY(QString applicationVersion READ applicationVersion WRITE setApplicationVersion) + Q_PROPERTY(QString organizationName READ organizationName WRITE setOrganizationName) + Q_PROPERTY(QString organizationDomain READ organizationDomain WRITE setOrganizationDomain) + + Q_DECLARE_PRIVATE(QCoreApplication) +public: + enum { ApplicationFlags = QT_VERSION +#if !defined(QT3_SUPPORT) + | 0x01000000 +#endif + }; + +#if defined(QT_BUILD_CORE_LIB) || defined(qdoc) + QCoreApplication(int &argc, char **argv); // ### Qt5 remove +#endif +#if !defined(qdoc) + QCoreApplication(int &argc, char **argv, int +#if !defined(QT_BUILD_CORE_LIB) + = ApplicationFlags +#endif + ); +#endif + + ~QCoreApplication(); + +#ifdef QT_DEPRECATED + QT_DEPRECATED static int argc(); + QT_DEPRECATED static char **argv(); +#endif + static QStringList arguments(); + + static void setAttribute(Qt::ApplicationAttribute attribute, bool on = true); + static bool testAttribute(Qt::ApplicationAttribute attribute); + + static void setOrganizationDomain(const QString &orgDomain); + static QString organizationDomain(); + static void setOrganizationName(const QString &orgName); + static QString organizationName(); + static void setApplicationName(const QString &application); + static QString applicationName(); + static void setApplicationVersion(const QString &version); + static QString applicationVersion(); + + static QCoreApplication *instance() { return self; } + + static int exec(); + static void processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents); + static void processEvents(QEventLoop::ProcessEventsFlags flags, int maxtime); + static void exit(int retcode=0); + + static bool sendEvent(QObject *receiver, QEvent *event); + static void postEvent(QObject *receiver, QEvent *event); + static void postEvent(QObject *receiver, QEvent *event, int priority); + static void sendPostedEvents(QObject *receiver, int event_type); + static void sendPostedEvents(); + static void removePostedEvents(QObject *receiver); + static void removePostedEvents(QObject *receiver, int eventType); + static bool hasPendingEvents(); + + virtual bool notify(QObject *, QEvent *); + + static bool startingUp(); + static bool closingDown(); + + static QString applicationDirPath(); + static QString applicationFilePath(); + static qint64 applicationPid(); + +#ifndef QT_NO_LIBRARY + static void setLibraryPaths(const QStringList &); + static QStringList libraryPaths(); + static void addLibraryPath(const QString &); + static void removeLibraryPath(const QString &); +#endif // QT_NO_LIBRARY + +#ifndef QT_NO_TRANSLATION + static void installTranslator(QTranslator * messageFile); + static void removeTranslator(QTranslator * messageFile); +#endif + enum Encoding { CodecForTr, UnicodeUTF8, DefaultCodec = CodecForTr }; + // ### Qt 5: merge + static QString translate(const char * context, + const char * key, + const char * disambiguation = 0, + Encoding encoding = CodecForTr); + static QString translate(const char * context, + const char * key, + const char * disambiguation, + Encoding encoding, int n); + + static void flush(); + +#if defined(QT3_SUPPORT) + inline QT3_SUPPORT void lock() {} + inline QT3_SUPPORT void unlock(bool = true) {} + inline QT3_SUPPORT bool locked() { return false; } + inline QT3_SUPPORT bool tryLock() { return false; } + + static inline QT3_SUPPORT void processOneEvent() + { processEvents(QEventLoop::WaitForMoreEvents); } + static QT3_SUPPORT int enter_loop(); + static QT3_SUPPORT void exit_loop(); + static QT3_SUPPORT int loopLevel(); +#endif + +#if defined(Q_WS_WIN) + virtual bool winEventFilter(MSG *message, long *result); +#endif + +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) + static void watchUnixSignal(int signal, bool watch); +#endif + + typedef bool (*EventFilter)(void *message, long *result); + EventFilter setEventFilter(EventFilter filter); + bool filterEvent(void *message, long *result); + +public Q_SLOTS: + static void quit(); + +Q_SIGNALS: + void aboutToQuit(); + void unixSignal(int); + +protected: + bool event(QEvent *); + + virtual bool compressEvent(QEvent *, QObject *receiver, QPostEventList *); + +protected: + QCoreApplication(QCoreApplicationPrivate &p); + +private: + static bool sendSpontaneousEvent(QObject *receiver, QEvent *event); + bool notifyInternal(QObject *receiver, QEvent *event); + + void init(); + + static QCoreApplication *self; + + Q_DISABLE_COPY(QCoreApplication) + + friend class QEventDispatcherUNIXPrivate; + friend class QApplication; + friend class QApplicationPrivate; + friend class QETWidget; + friend class Q3AccelManager; + friend class QShortcutMap; + friend class QWidget; + friend class QWidgetPrivate; + friend bool qt_sendSpontaneousEvent(QObject*, QEvent*); + friend Q_CORE_EXPORT QString qAppName(); + friend class QClassFactory; +}; + +inline bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event) +{ if (event) event->spont = false; return self ? self->notifyInternal(receiver, event) : false; } + +inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event) +{ if (event) event->spont = true; return self ? self->notifyInternal(receiver, event) : false; } + +inline void QCoreApplication::sendPostedEvents() { sendPostedEvents(0, 0); } + +#ifdef QT_NO_TRANSLATION +// Simple versions +inline QString QCoreApplication::translate(const char *, const char *sourceText, + const char *, Encoding encoding) +{ +#ifndef QT_NO_TEXTCODEC + if (encoding == UnicodeUTF8) + return QString::fromUtf8(sourceText); +#else + Q_UNUSED(encoding) +#endif + return QString::fromLatin1(sourceText); +} + +// Simple versions +inline QString QCoreApplication::translate(const char *, const char *sourceText, + const char *, Encoding encoding, int) +{ +#ifndef QT_NO_TEXTCODEC + if (encoding == UnicodeUTF8) + return QString::fromUtf8(sourceText); +#else + Q_UNUSED(encoding) +#endif + return QString::fromLatin1(sourceText); +} +#endif + +// ### merge the four functions into two (using "int n = -1") +#define Q_DECLARE_TR_FUNCTIONS(context) \ +public: \ + static inline QString tr(const char *sourceText, const char *disambiguation = 0) \ + { return QCoreApplication::translate(#context, sourceText, disambiguation); } \ + static inline QString trUtf8(const char *sourceText, const char *disambiguation = 0) \ + { return QCoreApplication::translate(#context, sourceText, disambiguation, \ + QCoreApplication::UnicodeUTF8); } \ + static inline QString tr(const char *sourceText, const char *disambiguation, int n) \ + { return QCoreApplication::translate(#context, sourceText, disambiguation, \ + QCoreApplication::CodecForTr, n); } \ + static inline QString trUtf8(const char *sourceText, const char *disambiguation, int n) \ + { return QCoreApplication::translate(#context, sourceText, disambiguation, \ + QCoreApplication::UnicodeUTF8, n); } \ +private: + +typedef void (*QtCleanUpFunction)(); + +Q_CORE_EXPORT void qAddPostRoutine(QtCleanUpFunction); +Q_CORE_EXPORT void qRemovePostRoutine(QtCleanUpFunction); +Q_CORE_EXPORT QString qAppName(); // get application name + +#if defined(Q_WS_WIN) && !defined(QT_NO_DEBUG_STREAM) +Q_CORE_EXPORT QString decodeMSG(const MSG &); +Q_CORE_EXPORT QDebug operator<<(QDebug, const MSG &); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QCOREAPPLICATION_H diff --git a/src/corelib/kernel/qcoreapplication_mac.cpp b/src/corelib/kernel/qcoreapplication_mac.cpp new file mode 100644 index 0000000000..0734c6ccd4 --- /dev/null +++ b/src/corelib/kernel/qcoreapplication_mac.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qcoreapplication.h" +#include "private/qcoreapplication_p.h" +#include + +QT_BEGIN_NAMESPACE + +/***************************************************************************** + QCoreApplication utility functions + *****************************************************************************/ +QString qAppFileName() +{ + static QString appFileName; + if (appFileName.isEmpty()) { + QCFType bundleURL(CFBundleCopyExecutableURL(CFBundleGetMainBundle())); + if(bundleURL) { + QCFString cfPath(CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle)); + if(cfPath) + appFileName = cfPath; + } + } + return appFileName; +} + + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h new file mode 100644 index 0000000000..add2a3553e --- /dev/null +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QCOREAPPLICATION_P_H +#define QCOREAPPLICATION_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/qcoreapplication.h" +#include "QtCore/qtranslator.h" +#include "private/qobject_p.h" + +#ifdef Q_OS_SYMBIAN +#include +#endif + +QT_BEGIN_NAMESPACE + +typedef QList QTranslatorList; + +#if defined(Q_OS_SYMBIAN) +# if !defined(QT_NO_SYSTEMLOCALE) +class QEnvironmentChangeNotifier; +# endif +class CApaCommandLine; +#endif +class QAbstractEventDispatcher; + +class Q_CORE_EXPORT QCoreApplicationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QCoreApplication) + +public: + QCoreApplicationPrivate(int &aargc, char **aargv, uint flags); + ~QCoreApplicationPrivate(); + + bool sendThroughApplicationEventFilters(QObject *, QEvent *); + bool sendThroughObjectEventFilters(QObject *, QEvent *); + bool notify_helper(QObject *, QEvent *); + + virtual QString appName() const; + virtual void createEventDispatcher(); + static void removePostedEvent(QEvent *); +#ifdef Q_OS_WIN + static void removePostedTimerEvent(QObject *object, int timerId); +#endif + +#ifdef Q_OS_MAC + static QString macMenuBarName(); +#endif + + static QThread *theMainThread; + static QThread *mainThread(); + static bool checkInstance(const char *method); + static void sendPostedEvents(QObject *receiver, int event_type, QThreadData *data); + +#if !defined (QT_NO_DEBUG) || defined (QT_MAC_FRAMEWORK_BUILD) || defined (Q_OS_SYMBIAN) + void checkReceiverThread(QObject *receiver); +#endif + int &argc; + char **argv; + void appendApplicationPathToLibraryPaths(void); + +#ifndef QT_NO_TRANSLATION + QTranslatorList translators; +#endif + uint application_type; + + QCoreApplication::EventFilter eventFilter; + + bool in_exec; + bool aboutToQuitEmitted; + QString cachedApplicationDirPath; + QString cachedApplicationFilePath; +#if defined(Q_OS_SYMBIAN) +# if !defined(QT_NO_SYSTEMLOCALE) + QScopedPointer environmentChangeNotifier; + void symbianInit(); +# endif + static CApaCommandLine* symbianCommandLine(); +#endif + + static bool isTranslatorInstalled(QTranslator *translator); + + static QAbstractEventDispatcher *eventDispatcher; + static bool is_app_running; + static bool is_app_closing; + + static uint attribs; + static inline bool testAttribute(uint flag) { return attribs & (1 << flag); } + static int app_compile_version; +#if defined(QT3_SUPPORT) + static bool useQt3Support; +#endif +}; + +QT_END_NAMESPACE + +#endif // QCOREAPPLICATION_P_H diff --git a/src/corelib/kernel/qcoreapplication_win.cpp b/src/corelib/kernel/qcoreapplication_win.cpp new file mode 100644 index 0000000000..a6f43a8900 --- /dev/null +++ b/src/corelib/kernel/qcoreapplication_win.cpp @@ -0,0 +1,1060 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qcoreapplication.h" +#include "qcoreapplication_p.h" +#include "qstringlist.h" +#include "qt_windows.h" +#include "qvector.h" +#include "qmutex.h" +#include "qfileinfo.h" +#include "qcorecmdlineargs_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +bool usingWinMain = false; // whether the qWinMain() is used or not +int appCmdShow = 0; + +Q_CORE_EXPORT HINSTANCE qWinAppInst() // get Windows app handle +{ + return GetModuleHandle(0); +} + +Q_CORE_EXPORT HINSTANCE qWinAppPrevInst() // get Windows prev app handle +{ + return 0; +} + +Q_CORE_EXPORT int qWinAppCmdShow() // get main window show command +{ +#if defined(Q_OS_WINCE) + return appCmdShow; +#else + STARTUPINFO startupInfo; + GetStartupInfo(&startupInfo); + + return (startupInfo.dwFlags & STARTF_USESHOWWINDOW) + ? startupInfo.wShowWindow + : SW_SHOWDEFAULT; +#endif +} + +Q_CORE_EXPORT QString qAppFileName() // get application file name +{ + // We do MAX_PATH + 2 here, and request with MAX_PATH + 1, so we can handle all paths + // up to, and including MAX_PATH size perfectly fine with string termination, as well + // as easily detect if the file path is indeed larger than MAX_PATH, in which case we + // need to use the heap instead. This is a work-around, since contrary to what the + // MSDN documentation states, GetModuleFileName sometimes doesn't set the + // ERROR_INSUFFICIENT_BUFFER error number, and we thus cannot rely on this value if + // GetModuleFileName(0, buffer, MAX_PATH) == MAX_PATH. + // GetModuleFileName(0, buffer, MAX_PATH + 1) == MAX_PATH just means we hit the normal + // file path limit, and we handle it normally, if the result is MAX_PATH + 1, we use + // heap (even if the result _might_ be exactly MAX_PATH + 1, but that's ok). + wchar_t buffer[MAX_PATH + 2]; + DWORD v = GetModuleFileName(0, buffer, MAX_PATH + 1); + buffer[MAX_PATH + 1] = 0; + + if (v == 0) + return QString(); + else if (v <= MAX_PATH) + return QString::fromWCharArray(buffer); + + // MAX_PATH sized buffer wasn't large enough to contain the full path, use heap + wchar_t *b = 0; + int i = 1; + size_t size; + do { + ++i; + size = MAX_PATH * i; + b = reinterpret_cast(realloc(b, (size + 1) * sizeof(wchar_t))); + if (b) + v = GetModuleFileName(NULL, b, size); + } while (b && v == size); + + if (b) + *(b + size) = 0; + QString res = QString::fromWCharArray(b); + free(b); + + return res; +} + +QString QCoreApplicationPrivate::appName() const +{ + return QFileInfo(qAppFileName()).baseName(); +} + +class QWinMsgHandlerCriticalSection +{ + CRITICAL_SECTION cs; +public: + QWinMsgHandlerCriticalSection() + { InitializeCriticalSection(&cs); } + ~QWinMsgHandlerCriticalSection() + { DeleteCriticalSection(&cs); } + + void lock() + { EnterCriticalSection(&cs); } + void unlock() + { LeaveCriticalSection(&cs); } +}; + +Q_CORE_EXPORT void qWinMsgHandler(QtMsgType t, const char* str) +{ + Q_UNUSED(t); + // OutputDebugString is not threadsafe. + + // cannot use QMutex here, because qWarning()s in the QMutex + // implementation may cause this function to recurse + static QWinMsgHandlerCriticalSection staticCriticalSection; + + if (!str) + str = "(null)"; + + staticCriticalSection.lock(); + + QString s(QString::fromLocal8Bit(str)); + s += QLatin1Char('\n'); + OutputDebugString((wchar_t*)s.utf16()); + + staticCriticalSection.unlock(); +} + + +/***************************************************************************** + qWinMain() - Initializes Windows. Called from WinMain() in qtmain_win.cpp + *****************************************************************************/ + +#if defined(Q_OS_WINCE) +Q_CORE_EXPORT void __cdecl qWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParam, + int cmdShow, int &argc, QVector &argv) +#else +Q_CORE_EXPORT +void qWinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParam, + int cmdShow, int &argc, QVector &argv) +#endif +{ + static bool already_called = false; + + if (already_called) { + qWarning("Qt: Internal error: qWinMain should be called only once"); + return; + } + already_called = true; + usingWinMain = true; + + // Install default debug handler + qInstallMsgHandler(qWinMsgHandler); + + // Create command line + argv = qWinCmdLine(cmdParam, int(strlen(cmdParam)), argc); + + appCmdShow = cmdShow; + + // Ignore Windows parameters + Q_UNUSED(instance); + Q_UNUSED(prevInstance); +} + +/*! + The message procedure calls this function for every message + received. Reimplement this function if you want to process window + messages \a msg that are not processed by Qt. If you don't want + the event to be processed by Qt, then return true and set \a result + to the value that the window procedure should return. Otherwise + return false. + + It is only directly addressed messages that are filtered. To + handle system wide messages, such as messages from a registered + hot key, you need to install an event filter on the event + dispatcher, which is returned from + QAbstractEventDispatcher::instance(). +*/ +bool QCoreApplication::winEventFilter(MSG *msg, long *result) // Windows event filter +{ + Q_UNUSED(msg); + Q_UNUSED(result); + return false; +} + +void QCoreApplicationPrivate::removePostedTimerEvent(QObject *object, int timerId) +{ + QThreadData *data = object->d_func()->threadData; + + QMutexLocker locker(&data->postEventList.mutex); + if (data->postEventList.size() == 0) + return; + for (int i = 0; i < data->postEventList.size(); ++i) { + const QPostEvent & pe = data->postEventList.at(i); + if (pe.receiver == object + && pe.event + && (pe.event->type() == QEvent::Timer || pe.event->type() == QEvent::ZeroTimerEvent) + && static_cast(pe.event)->timerId() == timerId) { + --pe.receiver->d_func()->postedEvents; + pe.event->posted = false; + delete pe.event; + const_cast(pe).event = 0; + return; + } + } +} + +#if defined(Q_WS_WIN) && !defined(QT_NO_DEBUG_STREAM) +/***************************************************************************** + Convenience functions for convert WM_* messages into human readable strings, + including a nifty QDebug operator<< for simpel QDebug() << msg output. + *****************************************************************************/ +QT_BEGIN_INCLUDE_NAMESPACE +#include +#include "qdebug.h" +QT_END_INCLUDE_NAMESPACE + +#if !defined(GET_X_LPARAM) +# define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) +# define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) +#endif +#ifdef _WIN32_WCE +# ifndef WM_NCACTIVATE +# define WM_NCACTIVATE 0x86 +# endif +#endif + +// The values below should never change. Note that none of the usual +// WM_...FIRST & WM_...LAST values are in the list, as they normally have other +// WM_... representations +struct KnownWM { + uint WM; + const char* str; +} knownWM[] = +{{ 0x0000, "WM_NULL" }, + { 0x0001, "WM_CREATE" }, + { 0x0002, "WM_DESTROY" }, + { 0x0003, "WM_MOVE" }, + { 0x0005, "WM_SIZE" }, + { 0x0006, "WM_ACTIVATE" }, + { 0x0007, "WM_SETFOCUS" }, + { 0x0008, "WM_KILLFOCUS" }, + { 0x000A, "WM_ENABLE" }, + { 0x000B, "WM_SETREDRAW" }, + { 0x000C, "WM_SETTEXT" }, + { 0x000D, "WM_GETTEXT" }, + { 0x000E, "WM_GETTEXTLENGTH" }, + { 0x000F, "WM_PAINT" }, + { 0x0010, "WM_CLOSE" }, + { 0x0011, "WM_QUERYENDSESSION" }, + { 0x0013, "WM_QUERYOPEN" }, + { 0x0016, "WM_ENDSESSION" }, + { 0x0012, "WM_QUIT" }, + { 0x0014, "WM_ERASEBKGND" }, + { 0x0015, "WM_SYSCOLORCHANGE" }, + { 0x0018, "WM_SHOWWINDOW" }, + { 0x001A, "WM_WININICHANGE" }, + { 0x001B, "WM_DEVMODECHANGE" }, + { 0x001C, "WM_ACTIVATEAPP" }, + { 0x001D, "WM_FONTCHANGE" }, + { 0x001E, "WM_TIMECHANGE" }, + { 0x001F, "WM_CANCELMODE" }, + { 0x0020, "WM_SETCURSOR" }, + { 0x0021, "WM_MOUSEACTIVATE" }, + { 0x0022, "WM_CHILDACTIVATE" }, + { 0x0023, "WM_QUEUESYNC" }, + { 0x0024, "WM_GETMINMAXINFO" }, + { 0x0026, "WM_PAINTICON" }, + { 0x0027, "WM_ICONERASEBKGND" }, + { 0x0028, "WM_NEXTDLGCTL" }, + { 0x002A, "WM_SPOOLERSTATUS" }, + { 0x002B, "WM_DRAWITEM" }, + { 0x002C, "WM_MEASUREITEM" }, + { 0x002D, "WM_DELETEITEM" }, + { 0x002E, "WM_VKEYTOITEM" }, + { 0x002F, "WM_CHARTOITEM" }, + { 0x0030, "WM_SETFONT" }, + { 0x0031, "WM_GETFONT" }, + { 0x0032, "WM_SETHOTKEY" }, + { 0x0033, "WM_GETHOTKEY" }, + { 0x0037, "WM_QUERYDRAGICON" }, + { 0x0039, "WM_COMPAREITEM" }, + { 0x003D, "WM_GETOBJECT" }, + { 0x0041, "WM_COMPACTING" }, + { 0x0044, "WM_COMMNOTIFY" }, + { 0x0046, "WM_WINDOWPOSCHANGING" }, + { 0x0047, "WM_WINDOWPOSCHANGED" }, + { 0x0048, "WM_POWER" }, + { 0x004A, "WM_COPYDATA" }, + { 0x004B, "WM_CANCELJOURNAL" }, + { 0x004E, "WM_NOTIFY" }, + { 0x0050, "WM_INPUTLANGCHANGEREQUEST" }, + { 0x0051, "WM_INPUTLANGCHANGE" }, + { 0x0052, "WM_TCARD" }, + { 0x0053, "WM_HELP" }, + { 0x0054, "WM_USERCHANGED" }, + { 0x0055, "WM_NOTIFYFORMAT" }, + { 0x007B, "WM_CONTEXTMENU" }, + { 0x007C, "WM_STYLECHANGING" }, + { 0x007D, "WM_STYLECHANGED" }, + { 0x007E, "WM_DISPLAYCHANGE" }, + { 0x007F, "WM_GETICON" }, + { 0x0080, "WM_SETICON" }, + { 0x0081, "WM_NCCREATE" }, + { 0x0082, "WM_NCDESTROY" }, + { 0x0083, "WM_NCCALCSIZE" }, + { 0x0084, "WM_NCHITTEST" }, + { 0x0085, "WM_NCPAINT" }, + { 0x0086, "WM_NCACTIVATE" }, + { 0x0087, "WM_GETDLGCODE" }, + { 0x0088, "WM_SYNCPAINT" }, + { 0x00A0, "WM_NCMOUSEMOVE" }, + { 0x00A1, "WM_NCLBUTTONDOWN" }, + { 0x00A2, "WM_NCLBUTTONUP" }, + { 0x00A3, "WM_NCLBUTTONDBLCLK" }, + { 0x00A4, "WM_NCRBUTTONDOWN" }, + { 0x00A5, "WM_NCRBUTTONUP" }, + { 0x00A6, "WM_NCRBUTTONDBLCLK" }, + { 0x00A7, "WM_NCMBUTTONDOWN" }, + { 0x00A8, "WM_NCMBUTTONUP" }, + { 0x00A9, "WM_NCMBUTTONDBLCLK" }, + { 0x00AB, "WM_NCXBUTTONDOWN" }, + { 0x00AC, "WM_NCXBUTTONUP" }, + { 0x00AD, "WM_NCXBUTTONDBLCLK" }, + { 0x00FF, "WM_INPUT" }, + { 0x0100, "WM_KEYDOWN" }, + { 0x0101, "WM_KEYUP" }, + { 0x0102, "WM_CHAR" }, + { 0x0103, "WM_DEADCHAR" }, + { 0x0104, "WM_SYSKEYDOWN" }, + { 0x0105, "WM_SYSKEYUP" }, + { 0x0106, "WM_SYSCHAR" }, + { 0x0107, "WM_SYSDEADCHAR" }, + { 0x0109, "WM_UNICHAR" }, + { 0x010D, "WM_IME_STARTCOMPOSITION" }, + { 0x010E, "WM_IME_ENDCOMPOSITION" }, + { 0x010F, "WM_IME_COMPOSITION" }, + { 0x0110, "WM_INITDIALOG" }, + { 0x0111, "WM_COMMAND" }, + { 0x0112, "WM_SYSCOMMAND" }, + { 0x0113, "WM_TIMER" }, + { 0x0114, "WM_HSCROLL" }, + { 0x0115, "WM_VSCROLL" }, + { 0x0116, "WM_INITMENU" }, + { 0x0117, "WM_INITMENUPOPUP" }, + { 0x011F, "WM_MENUSELECT" }, + { 0x0120, "WM_MENUCHAR" }, + { 0x0121, "WM_ENTERIDLE" }, + { 0x0122, "WM_MENURBUTTONUP" }, + { 0x0123, "WM_MENUDRAG" }, + { 0x0124, "WM_MENUGETOBJECT" }, + { 0x0125, "WM_UNINITMENUPOPUP" }, + { 0x0126, "WM_MENUCOMMAND" }, + { 0x0127, "WM_CHANGEUISTATE" }, + { 0x0128, "WM_UPDATEUISTATE" }, + { 0x0129, "WM_QUERYUISTATE" }, + { 0x0132, "WM_CTLCOLORMSGBOX" }, + { 0x0133, "WM_CTLCOLOREDIT" }, + { 0x0134, "WM_CTLCOLORLISTBOX" }, + { 0x0135, "WM_CTLCOLORBTN" }, + { 0x0136, "WM_CTLCOLORDLG" }, + { 0x0137, "WM_CTLCOLORSCROLLBAR" }, + { 0x0138, "WM_CTLCOLORSTATIC" }, + { 0x0200, "WM_MOUSEMOVE" }, + { 0x0201, "WM_LBUTTONDOWN" }, + { 0x0202, "WM_LBUTTONUP" }, + { 0x0203, "WM_LBUTTONDBLCLK" }, + { 0x0204, "WM_RBUTTONDOWN" }, + { 0x0205, "WM_RBUTTONUP" }, + { 0x0206, "WM_RBUTTONDBLCLK" }, + { 0x0207, "WM_MBUTTONDOWN" }, + { 0x0208, "WM_MBUTTONUP" }, + { 0x0209, "WM_MBUTTONDBLCLK" }, + { 0x020A, "WM_MOUSEWHEEL" }, + { 0x020B, "WM_XBUTTONDOWN" }, + { 0x020C, "WM_XBUTTONUP" }, + { 0x020D, "WM_XBUTTONDBLCLK" }, + { 0x020E, "WM_MOUSEHWHEEL" }, + { 0x0210, "WM_PARENTNOTIFY" }, + { 0x0211, "WM_ENTERMENULOOP" }, + { 0x0212, "WM_EXITMENULOOP" }, + { 0x0213, "WM_NEXTMENU" }, + { 0x0214, "WM_SIZING" }, + { 0x0215, "WM_CAPTURECHANGED" }, + { 0x0216, "WM_MOVING" }, + { 0x0218, "WM_POWERBROADCAST" }, + { 0x0219, "WM_DEVICECHANGE" }, + { 0x0220, "WM_MDICREATE" }, + { 0x0221, "WM_MDIDESTROY" }, + { 0x0222, "WM_MDIACTIVATE" }, + { 0x0223, "WM_MDIRESTORE" }, + { 0x0224, "WM_MDINEXT" }, + { 0x0225, "WM_MDIMAXIMIZE" }, + { 0x0226, "WM_MDITILE" }, + { 0x0227, "WM_MDICASCADE" }, + { 0x0228, "WM_MDIICONARRANGE" }, + { 0x0229, "WM_MDIGETACTIVE" }, + { 0x0230, "WM_MDISETMENU" }, + { 0x0231, "WM_ENTERSIZEMOVE" }, + { 0x0232, "WM_EXITSIZEMOVE" }, + { 0x0233, "WM_DROPFILES" }, + { 0x0234, "WM_MDIREFRESHMENU" }, + { 0x0281, "WM_IME_SETCONTEXT" }, + { 0x0282, "WM_IME_NOTIFY" }, + { 0x0283, "WM_IME_CONTROL" }, + { 0x0284, "WM_IME_COMPOSITIONFULL" }, + { 0x0285, "WM_IME_SELECT" }, + { 0x0286, "WM_IME_CHAR" }, + { 0x0288, "WM_IME_REQUEST" }, + { 0x0290, "WM_IME_KEYDOWN" }, + { 0x0291, "WM_IME_KEYUP" }, + { 0x02A0, "WM_NCMOUSEHOVER" }, + { 0x02A1, "WM_MOUSEHOVER" }, + { 0x02A2, "WM_NCMOUSELEAVE" }, + { 0x02A3, "WM_MOUSELEAVE" }, + { 0x02B1, "WM_WTSSESSION_CHANGE" }, + { 0x02C0, "WM_TABLET_FIRST" }, + { 0x02C1, "WM_TABLET_FIRST + 1" }, + { 0x02C2, "WM_TABLET_FIRST + 2" }, + { 0x02C3, "WM_TABLET_FIRST + 3" }, + { 0x02C4, "WM_TABLET_FIRST + 4" }, + { 0x02C5, "WM_TABLET_FIRST + 5" }, + { 0x02C6, "WM_TABLET_FIRST + 6" }, + { 0x02C7, "WM_TABLET_FIRST + 7" }, + { 0x02C8, "WM_TABLET_FIRST + 8" }, + { 0x02C9, "WM_TABLET_FIRST + 9" }, + { 0x02CA, "WM_TABLET_FIRST + 10" }, + { 0x02CB, "WM_TABLET_FIRST + 11" }, + { 0x02CC, "WM_TABLET_FIRST + 12" }, + { 0x02CD, "WM_TABLET_FIRST + 13" }, + { 0x02CE, "WM_TABLET_FIRST + 14" }, + { 0x02CF, "WM_TABLET_FIRST + 15" }, + { 0x02D0, "WM_TABLET_FIRST + 16" }, + { 0x02D1, "WM_TABLET_FIRST + 17" }, + { 0x02D2, "WM_TABLET_FIRST + 18" }, + { 0x02D3, "WM_TABLET_FIRST + 19" }, + { 0x02D4, "WM_TABLET_FIRST + 20" }, + { 0x02D5, "WM_TABLET_FIRST + 21" }, + { 0x02D6, "WM_TABLET_FIRST + 22" }, + { 0x02D7, "WM_TABLET_FIRST + 23" }, + { 0x02D8, "WM_TABLET_FIRST + 24" }, + { 0x02D9, "WM_TABLET_FIRST + 25" }, + { 0x02DA, "WM_TABLET_FIRST + 26" }, + { 0x02DB, "WM_TABLET_FIRST + 27" }, + { 0x02DC, "WM_TABLET_FIRST + 28" }, + { 0x02DD, "WM_TABLET_FIRST + 29" }, + { 0x02DE, "WM_TABLET_FIRST + 30" }, + { 0x02DF, "WM_TABLET_LAST" }, + { 0x0300, "WM_CUT" }, + { 0x0301, "WM_COPY" }, + { 0x0302, "WM_PASTE" }, + { 0x0303, "WM_CLEAR" }, + { 0x0304, "WM_UNDO" }, + { 0x0305, "WM_RENDERFORMAT" }, + { 0x0306, "WM_RENDERALLFORMATS" }, + { 0x0307, "WM_DESTROYCLIPBOARD" }, + { 0x0308, "WM_DRAWCLIPBOARD" }, + { 0x0309, "WM_PAINTCLIPBOARD" }, + { 0x030A, "WM_VSCROLLCLIPBOARD" }, + { 0x030B, "WM_SIZECLIPBOARD" }, + { 0x030C, "WM_ASKCBFORMATNAME" }, + { 0x030D, "WM_CHANGECBCHAIN" }, + { 0x030E, "WM_HSCROLLCLIPBOARD" }, + { 0x030F, "WM_QUERYNEWPALETTE" }, + { 0x0310, "WM_PALETTEISCHANGING" }, + { 0x0311, "WM_PALETTECHANGED" }, + { 0x0312, "WM_HOTKEY" }, + { 0x0317, "WM_PRINT" }, + { 0x0318, "WM_PRINTCLIENT" }, + { 0x0319, "WM_APPCOMMAND" }, + { 0x031A, "WM_THEMECHANGED" }, + { 0x0358, "WM_HANDHELDFIRST" }, + { 0x0359, "WM_HANDHELDFIRST + 1" }, + { 0x035A, "WM_HANDHELDFIRST + 2" }, + { 0x035B, "WM_HANDHELDFIRST + 3" }, + { 0x035C, "WM_HANDHELDFIRST + 4" }, + { 0x035D, "WM_HANDHELDFIRST + 5" }, + { 0x035E, "WM_HANDHELDFIRST + 6" }, + { 0x035F, "WM_HANDHELDLAST" }, + { 0x0360, "WM_AFXFIRST" }, + { 0x0361, "WM_AFXFIRST + 1" }, + { 0x0362, "WM_AFXFIRST + 2" }, + { 0x0363, "WM_AFXFIRST + 3" }, + { 0x0364, "WM_AFXFIRST + 4" }, + { 0x0365, "WM_AFXFIRST + 5" }, + { 0x0366, "WM_AFXFIRST + 6" }, + { 0x0367, "WM_AFXFIRST + 7" }, + { 0x0368, "WM_AFXFIRST + 8" }, + { 0x0369, "WM_AFXFIRST + 9" }, + { 0x036A, "WM_AFXFIRST + 10" }, + { 0x036B, "WM_AFXFIRST + 11" }, + { 0x036C, "WM_AFXFIRST + 12" }, + { 0x036D, "WM_AFXFIRST + 13" }, + { 0x036E, "WM_AFXFIRST + 14" }, + { 0x036F, "WM_AFXFIRST + 15" }, + { 0x0370, "WM_AFXFIRST + 16" }, + { 0x0371, "WM_AFXFIRST + 17" }, + { 0x0372, "WM_AFXFIRST + 18" }, + { 0x0373, "WM_AFXFIRST + 19" }, + { 0x0374, "WM_AFXFIRST + 20" }, + { 0x0375, "WM_AFXFIRST + 21" }, + { 0x0376, "WM_AFXFIRST + 22" }, + { 0x0377, "WM_AFXFIRST + 23" }, + { 0x0378, "WM_AFXFIRST + 24" }, + { 0x0379, "WM_AFXFIRST + 25" }, + { 0x037A, "WM_AFXFIRST + 26" }, + { 0x037B, "WM_AFXFIRST + 27" }, + { 0x037C, "WM_AFXFIRST + 28" }, + { 0x037D, "WM_AFXFIRST + 29" }, + { 0x037E, "WM_AFXFIRST + 30" }, + { 0x037F, "WM_AFXLAST" }, + { 0x0380, "WM_PENWINFIRST" }, + { 0x0381, "WM_PENWINFIRST + 1" }, + { 0x0382, "WM_PENWINFIRST + 2" }, + { 0x0383, "WM_PENWINFIRST + 3" }, + { 0x0384, "WM_PENWINFIRST + 4" }, + { 0x0385, "WM_PENWINFIRST + 5" }, + { 0x0386, "WM_PENWINFIRST + 6" }, + { 0x0387, "WM_PENWINFIRST + 7" }, + { 0x0388, "WM_PENWINFIRST + 8" }, + { 0x0389, "WM_PENWINFIRST + 9" }, + { 0x038A, "WM_PENWINFIRST + 10" }, + { 0x038B, "WM_PENWINFIRST + 11" }, + { 0x038C, "WM_PENWINFIRST + 12" }, + { 0x038D, "WM_PENWINFIRST + 13" }, + { 0x038E, "WM_PENWINFIRST + 14" }, + { 0x038F, "WM_PENWINLAST" }, + { 0x0400, "WM_USER" }, + { 0x8000, "WM_APP" }, + { 0,0 }}; // End of known messages + +// Looks up the WM_ message in the table above +static const char* findWMstr(uint msg) +{ + uint i = 0; + const char* result = 0; + // Known WM_'s + while (knownWM[i].str && (knownWM[i].WM != msg)) + ++i; + result = knownWM[i].str; + return result; +}; + +// Convenience function for converting flags and values into readable strings +struct FLAG_STRING_STRUCT +{ + uint value; + const char* str; +}; + +FLAG_STRING_STRUCT FLAG_STRING(int value = 0, const char *c = 0) +{ + FLAG_STRING_STRUCT s = {value, c}; + return s; +} + +#define FLGSTR(x) FLAG_STRING(x, #x) + +// Returns an ORed (" | ") together string for the flags active in the actual +// value. (...) must consist of FLAG_STRING, with a FLAG_STRING() as the last +// value in the list passed to the function +QString flagCheck(uint actual, ...) +{ + va_list ap; + va_start(ap, actual); + + QString result; + int count = 0; + FLAG_STRING_STRUCT v; + while((v=va_arg(ap,FLAG_STRING_STRUCT)).str) { + if ((actual & v.value) == v.value) { + if (count++) + result += QLatin1String(" | "); + result += QString::fromLatin1(v.str); + } + } + va_end(ap); + return result; +}; + +// Returns the string representation of the value in 'actual'. (...) must +// consist of FLAG_STRING, with a FLAG_STRING() as the last value in the list +// passed to the function +QString valueCheck(uint actual, ...) +{ + va_list ap; + va_start(ap, actual); + + QString result; + FLAG_STRING_STRUCT v; + while((v=va_arg(ap,FLAG_STRING_STRUCT)).str && (actual != v.value)) + ; + result = QString::fromLatin1(v.str); + + va_end(ap); + return result; +}; + +#ifdef Q_CC_BOR + +QString decodeMSG(const MSG& msg) +{ + return QString::fromLatin1("THis is not supported on Borland"); +} + +#else + +// Returns a "human readable" string representation of the MSG and the +// information it points to +QString decodeMSG(const MSG& msg) +{ + const WPARAM wParam = msg.wParam; + const LPARAM lParam = msg.lParam; + QString wmmsg = QString::fromLatin1(findWMstr(msg.message)); + // Unknown WM_, so use number + if (wmmsg.isEmpty()) + wmmsg = QString::fromLatin1("WM_(%1)").arg(msg.message); + + QString rawParameters; + rawParameters.sprintf("hwnd(0x%p) ", (void *)msg.hwnd); + + // Custom WM_'s + if (msg.message > WM_APP) + wmmsg = QString::fromLatin1("WM_APP + %1").arg(msg.message - WM_APP); + else if (msg.message > WM_USER) + wmmsg = QString::fromLatin1("WM_USER + %1").arg(msg.message - WM_USER); + + QString parameters; + switch (msg.message) { +#ifdef WM_ACTIVATE + case WM_ACTIVATE: + { + QString activation = valueCheck(wParam, + FLAG_STRING(WA_ACTIVE, "Activate"), + FLAG_STRING(WA_INACTIVE, "Deactivate"), + FLAG_STRING(WA_CLICKACTIVE, "Activate by mouseclick"), + FLAG_STRING()); + parameters.sprintf("%s Hwnd (0x%p)", activation.toLatin1().data(), (void *)msg.hwnd); + } + break; +#endif +#ifdef WM_CAPTURECHANGED + case WM_CAPTURECHANGED: + parameters.sprintf("Hwnd gaining capture (0x%p)", (void *)lParam); + break; +#endif +#ifdef WM_CREATE + case WM_CREATE: + { + LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lParam; + QString styles = flagCheck(lpcs->style, + FLGSTR(WS_BORDER), + FLGSTR(WS_CAPTION), + FLGSTR(WS_CHILD), + FLGSTR(WS_CLIPCHILDREN), + FLGSTR(WS_CLIPSIBLINGS), + FLGSTR(WS_DISABLED), + FLGSTR(WS_DLGFRAME), + FLGSTR(WS_GROUP), + FLGSTR(WS_HSCROLL), + FLGSTR(WS_OVERLAPPED), +#if defined(WS_OVERLAPPEDWINDOW) && (WS_OVERLAPPEDWINDOW != 0) + FLGSTR(WS_OVERLAPPEDWINDOW), +#endif +#ifdef WS_ICONIC + FLGSTR(WS_ICONIC), +#endif + FLGSTR(WS_MAXIMIZE), + FLGSTR(WS_MAXIMIZEBOX), + FLGSTR(WS_MINIMIZE), + FLGSTR(WS_MINIMIZEBOX), + FLGSTR(WS_OVERLAPPEDWINDOW), + FLGSTR(WS_POPUP), +#ifdef WS_POPUPWINDOW + FLGSTR(WS_POPUPWINDOW), +#endif + FLGSTR(WS_SIZEBOX), + FLGSTR(WS_SYSMENU), + FLGSTR(WS_TABSTOP), + FLGSTR(WS_THICKFRAME), +#ifdef WS_TILED + FLGSTR(WS_TILED), +#endif +#ifdef WS_TILEDWINDOW + FLGSTR(WS_TILEDWINDOW), +#endif + FLGSTR(WS_VISIBLE), + FLGSTR(WS_VSCROLL), + FLAG_STRING()); + + QString exStyles = flagCheck(lpcs->dwExStyle, +#ifdef WS_EX_ACCEPTFILES + FLGSTR(WS_EX_ACCEPTFILES), +#endif +#ifdef WS_EX_APPWINDOW + FLGSTR(WS_EX_APPWINDOW), +#endif + FLGSTR(WS_EX_CLIENTEDGE), + FLGSTR(WS_EX_DLGMODALFRAME), +#ifdef WS_EX_LEFT + FLGSTR(WS_EX_LEFT), +#endif + FLGSTR(WS_EX_LEFTSCROLLBAR), +#ifdef WS_EX_LTRREADING + FLGSTR(WS_EX_LTRREADING), +#endif +#ifdef WS_EX_MDICHILD + FLGSTR(WS_EX_MDICHILD), +#endif +#ifdef WS_EX_NOACTIVATE + FLGSTR(WS_EX_NOACTIVATE), +#endif +#ifdef WS_EX_NOANIMATION + FLGSTR(WS_EX_NOANIMATION), +#endif + FLGSTR(WS_EX_NOPARENTNOTIFY), + FLGSTR(WS_EX_OVERLAPPEDWINDOW), +#ifdef WS_EX_PALETTEWINDOW + FLGSTR(WS_EX_PALETTEWINDOW), +#endif +#ifdef WS_EX_RIGHT + FLGSTR(WS_EX_RIGHT), +#endif +#ifdef WS_EX_RIGHTSCROLLBAR + FLGSTR(WS_EX_RIGHTSCROLLBAR), +#endif +#ifdef WS_EX_RTLREADING + FLGSTR(WS_EX_RTLREADING), +#endif + FLGSTR(WS_EX_STATICEDGE), + FLGSTR(WS_EX_TOOLWINDOW), + FLGSTR(WS_EX_TOPMOST), +#ifdef WS_EX_TRANSPARENT + FLGSTR(WS_EX_TRANSPARENT), +#endif + FLGSTR(WS_EX_WINDOWEDGE), +#ifdef WS_EX_CAPTIONOKBTN + FLGSTR(WS_EX_CAPTIONOKBTN), +#endif + FLAG_STRING()); + + QString className; + if (lpcs->lpszClass != 0) { + if (HIWORD(lpcs->lpszClass) == 0) // Atom + className = QString::number(LOWORD(lpcs->lpszClass), 16); + else // String + className = QString((QChar*)lpcs->lpszClass, + (int)wcslen(reinterpret_cast(lpcs->lpszClass))); + } + + QString windowName; + if (lpcs->lpszName != 0) + windowName = QString((QChar*)lpcs->lpszName, + (int)wcslen(reinterpret_cast(lpcs->lpszName))); + + parameters.sprintf("x,y(%4d,%4d) w,h(%4d,%4d) className(%s) windowName(%s) parent(0x%p) style(%s) exStyle(%s)", + lpcs->x, lpcs->y, lpcs->cx, lpcs->cy, className.toLatin1().data(), + windowName.toLatin1().data(), (void *)lpcs->hwndParent, + styles.toLatin1().data(), exStyles.toLatin1().data()); + } + break; +#endif +#ifdef WM_DESTROY + case WM_DESTROY: + parameters.sprintf("Destroy hwnd (0x%p)", (void *)msg.hwnd); + break; +#endif +#ifdef WM_IME_NOTIFY + case WM_IME_NOTIFY: + { + QString imnCommand = valueCheck(wParam, + FLGSTR(IMN_CHANGECANDIDATE), + FLGSTR(IMN_CLOSECANDIDATE), + FLGSTR(IMN_CLOSESTATUSWINDOW), + FLGSTR(IMN_GUIDELINE), + FLGSTR(IMN_OPENCANDIDATE), + FLGSTR(IMN_OPENSTATUSWINDOW), + FLGSTR(IMN_SETCANDIDATEPOS), + FLGSTR(IMN_SETCOMPOSITIONFONT), + FLGSTR(IMN_SETCOMPOSITIONWINDOW), + FLGSTR(IMN_SETCONVERSIONMODE), + FLGSTR(IMN_SETOPENSTATUS), + FLGSTR(IMN_SETSENTENCEMODE), + FLGSTR(IMN_SETSTATUSWINDOWPOS), + FLAG_STRING()); + parameters.sprintf("Command(%s : 0x%p)", imnCommand.toLatin1().data(), (void *)lParam); + } + break; +#endif +#ifdef WM_IME_SETCONTEXT + case WM_IME_SETCONTEXT: + { + bool fSet = (BOOL)wParam; + DWORD fShow = (DWORD)lParam; + QString showFlgs = flagCheck(fShow, +#ifdef ISC_SHOWUICOMPOSITIONWINDOW + FLGSTR(ISC_SHOWUICOMPOSITIONWINDOW), +#endif +#ifdef ISC_SHOWUIGUIDWINDOW + FLGSTR(ISC_SHOWUIGUIDWINDOW), +#endif +#ifdef ISC_SHOWUISOFTKBD + FLGSTR(ISC_SHOWUISOFTKBD), +#endif + FLGSTR(ISC_SHOWUICANDIDATEWINDOW), + FLGSTR(ISC_SHOWUICANDIDATEWINDOW << 1), + FLGSTR(ISC_SHOWUICANDIDATEWINDOW << 2), + FLGSTR(ISC_SHOWUICANDIDATEWINDOW << 3), + FLAG_STRING()); + parameters.sprintf("Input context(%s) Show flags(%s)", (fSet? "Active" : "Inactive"), showFlgs.toLatin1().data()); + } + break; +#endif +#ifdef WM_KILLFOCUS + case WM_KILLFOCUS: + parameters.sprintf("Hwnd gaining keyboard focus (0x%p)", (void *)wParam); + break; +#endif +#ifdef WM_CHAR + case WM_CHAR: +#endif +#ifdef WM_IME_CHAR + case WM_IME_CHAR: +#endif +#ifdef WM_KEYDOWN + case WM_KEYDOWN: +#endif +#ifdef WM_KEYUP + case WM_KEYUP: + { + int nVirtKey = (int)wParam; + long lKeyData = (long)lParam; + int repCount = (lKeyData & 0xffff); // Bit 0-15 + int scanCode = (lKeyData & 0xf0000) >> 16; // Bit 16-23 + bool contextCode = (lKeyData && 0x20000000); // Bit 29 + bool prevState = (lKeyData && 0x40000000); // Bit 30 + bool transState = (lKeyData && 0x80000000); // Bit 31 + parameters.sprintf("Virual-key(0x%x) Scancode(%d) Rep(%d) Contextcode(%d), Prev state(%d), Trans state(%d)", + nVirtKey, scanCode, repCount, contextCode, prevState, transState); + } + break; +#endif +#ifdef WM_NCACTIVATE + case WM_NCACTIVATE: + { + parameters = (msg.wParam? QLatin1String("Active Titlebar") : QLatin1String("Inactive Titlebar")); + } + break; +#endif +#ifdef WM_MOUSEACTIVATE + case WM_MOUSEACTIVATE: + { + QString mouseMsg = QString::fromLatin1(findWMstr(HIWORD(lParam))); + parameters.sprintf("TLW(0x%p) HittestCode(0x%x) MouseMsg(%s)", (void *)wParam, LOWORD(lParam), mouseMsg.toLatin1().data()); + } + break; +#endif +#ifdef WM_MOUSELEAVE + case WM_MOUSELEAVE: + break; // wParam & lParam not used +#endif +#ifdef WM_MOUSEHOVER + case WM_MOUSEHOVER: +#endif +#ifdef WM_MOUSEWHEEL + case WM_MOUSEWHEEL: +#endif +#ifdef WM_MOUSEHWHEEL + case WM_MOUSEHWHEEL: +#endif +#ifdef WM_LBUTTONDBLCLK + case WM_LBUTTONDBLCLK: +#endif +#ifdef WM_LBUTTONDOWN + case WM_LBUTTONDOWN: +#endif +#ifdef WM_LBUTTONUP + case WM_LBUTTONUP: +#endif +#ifdef WM_MBUTTONDBLCLK + case WM_MBUTTONDBLCLK: +#endif +#ifdef WM_MBUTTONDOWN + case WM_MBUTTONDOWN: +#endif +#ifdef WM_MBUTTONUP + case WM_MBUTTONUP: +#endif +#ifdef WM_RBUTTONDBLCLK + case WM_RBUTTONDBLCLK: +#endif +#ifdef WM_RBUTTONDOWN + case WM_RBUTTONDOWN: +#endif +#ifdef WM_RBUTTONUP + case WM_RBUTTONUP: +#endif +#ifdef WM_MOUSEMOVE + case WM_MOUSEMOVE: + { + QString vrtKeys = flagCheck(wParam, + FLGSTR(MK_CONTROL), + FLGSTR(MK_LBUTTON), + FLGSTR(MK_MBUTTON), + FLGSTR(MK_RBUTTON), + FLGSTR(MK_SHIFT), +#ifdef MK_XBUTTON1 + FLGSTR(MK_XBUTTON1), +#endif +#ifdef MK_XBUTTON2 + FLGSTR(MK_XBUTTON2), +#endif + FLAG_STRING()); + parameters.sprintf("x,y(%4d,%4d) Virtual Keys(%s)", GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), vrtKeys.toLatin1().data()); + } + break; +#endif +#ifdef WM_MOVE + case WM_MOVE: + parameters.sprintf("x,y(%4d,%4d)", LOWORD(lParam), HIWORD(lParam)); + break; +#endif +#if defined(WM_PAINT) && defined(WM_ERASEBKGND) + case WM_ERASEBKGND: + case WM_PAINT: + parameters.sprintf("hdc(0x%p)", (void *)wParam); + break; +#endif +#ifdef WM_QUERYNEWPALETTE + case WM_QUERYNEWPALETTE: + break; // lParam & wParam are unused +#endif +#ifdef WM_SETCURSOR + case WM_SETCURSOR: + { + QString mouseMsg = QString::fromLatin1(findWMstr(HIWORD(lParam))); + parameters.sprintf("HitTestCode(0x%x) MouseMsg(%s)", LOWORD(lParam), mouseMsg.toLatin1().data()); + } + break; +#endif +#ifdef WM_SETFOCUS + case WM_SETFOCUS: + parameters.sprintf("Lost Focus (0x%p)", (void *)wParam); + break; +#endif +#ifdef WM_SETTEXT + case WM_SETTEXT: + parameters.sprintf("Set Text (%s)", QString((QChar*)lParam, (int)wcslen(reinterpret_cast(lParam))).toLatin1().data()); //Unicode string + break; +#endif +#ifdef WM_SIZE + case WM_SIZE: + { + QString showMode = valueCheck(wParam, + FLGSTR(SIZE_MAXHIDE), + FLGSTR(SIZE_MAXIMIZED), + FLGSTR(SIZE_MAXSHOW), + FLGSTR(SIZE_MINIMIZED), + FLGSTR(SIZE_RESTORED), + FLAG_STRING()); + + parameters.sprintf("w,h(%4d,%4d) showmode(%s)", LOWORD(lParam), HIWORD(lParam), showMode.toLatin1().data()); + } + break; +#endif +#ifdef WM_WINDOWPOSCHANGED + case WM_WINDOWPOSCHANGED: + { + LPWINDOWPOS winPos = (LPWINDOWPOS)lParam; + if (!winPos) + break; + QString hwndAfter = valueCheck(quint64(winPos->hwndInsertAfter), + FLAG_STRING((qptrdiff)HWND_BOTTOM, "HWND_BOTTOM"), + FLAG_STRING((qptrdiff)HWND_NOTOPMOST, "HWND_NOTOPMOST"), + FLAG_STRING((qptrdiff)HWND_TOP, "HWND_TOP"), + FLAG_STRING((qptrdiff)HWND_TOPMOST, "HWND_TOPMOST"), + FLAG_STRING()); + if (hwndAfter.isEmpty()) + hwndAfter = QString::number((quintptr)winPos->hwndInsertAfter, 16); + QString flags = flagCheck(winPos->flags, + FLGSTR(SWP_DRAWFRAME), + FLGSTR(SWP_FRAMECHANGED), + FLGSTR(SWP_HIDEWINDOW), + FLGSTR(SWP_NOACTIVATE), +#ifdef SWP_NOCOPYBITS + FLGSTR(SWP_NOCOPYBITS), +#endif + FLGSTR(SWP_NOMOVE), + FLGSTR(SWP_NOOWNERZORDER), + FLGSTR(SWP_NOREDRAW), + FLGSTR(SWP_NOREPOSITION), +#ifdef SWP_NOSENDCHANGING + FLGSTR(SWP_NOSENDCHANGING), +#endif + FLGSTR(SWP_NOSIZE), + FLGSTR(SWP_NOZORDER), + FLGSTR(SWP_SHOWWINDOW), + FLAG_STRING()); + parameters.sprintf("x,y(%4d,%4d) w,h(%4d,%4d) flags(%s) hwndAfter(%s)", winPos->x, winPos->y, winPos->cx, winPos->cy, flags.toLatin1().data(), hwndAfter.toLatin1().data()); + } + break; +#endif + default: + parameters.sprintf("wParam(0x%p) lParam(0x%p)", (void *)wParam, (void *)lParam); + break; + } + // Yes, we want to give the WM_ names 20 chars of space before showing the + // decoded message, since some of the common messages are quite long, and + // we don't want the decoded information to vary in output position + QString message = QString::fromLatin1("%1: ").arg(wmmsg, 20); + message += rawParameters; + message += parameters; + return message; +} + +#endif + +QDebug operator<<(QDebug dbg, const MSG &msg) +{ + dbg << decodeMSG(msg); + return dbg.nospace(); +} +#endif + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcorecmdlineargs_p.h b/src/corelib/kernel/qcorecmdlineargs_p.h new file mode 100644 index 0000000000..cdde7828f7 --- /dev/null +++ b/src/corelib/kernel/qcorecmdlineargs_p.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QCORECMDLINEARGS_P_H +#define QCORECMDLINEARGS_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/qstring.h" +#include "QtCore/qstringlist.h" + +QT_BEGIN_NAMESPACE + +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + +QT_BEGIN_INCLUDE_NAMESPACE +#include "QtCore/qvector.h" +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) +# include "qt_windows.h" +#endif +QT_END_INCLUDE_NAMESPACE + +// template implementation of the parsing algorithm +// this is used from qcoreapplication_win.cpp and the tools (rcc, uic...) + +template +static QVector qWinCmdLine(Char *cmdParam, int length, int &argc) +{ + QVector argv(8); + Char *p = cmdParam; + Char *p_end = p + length; + + argc = 0; + + while (*p && p < p_end) { // parse cmd line arguments + while (QChar((short)(*p)).isSpace()) // skip white space + p++; + if (*p && p < p_end) { // arg starts + int quote; + Char *start, *r; + if (*p == Char('\"') || *p == Char('\'')) { // " or ' quote + quote = *p; + start = ++p; + } else { + quote = 0; + start = p; + } + r = start; + while (*p && p < p_end) { + if (quote) { + if (*p == quote) { + p++; + if (QChar((short)(*p)).isSpace()) + break; + quote = 0; + } + } + if (*p == '\\') { // escape char? + p++; + if (*p == Char('\"') || *p == Char('\'')) + ; // yes + else + p--; // treat \ literally + } else { + if (!quote && (*p == Char('\"') || *p == Char('\''))) { // " or ' quote + quote = *p++; + continue; + } else if (QChar((short)(*p)).isSpace() && !quote) + break; + } + if (*p) + *r++ = *p++; + } + if (*p && p < p_end) + p++; + *r = Char('\0'); + + if (argc >= (int)argv.size()-1) // expand array + argv.resize(argv.size()*2); + argv[argc++] = start; + } + } + argv[argc] = 0; + + return argv; +} + +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) +static inline QStringList qWinCmdArgs(QString cmdLine) // not const-ref: this might be modified +{ + QStringList args; + + int argc = 0; + QVector argv = qWinCmdLine((wchar_t *)cmdLine.utf16(), cmdLine.length(), argc); + for (int a = 0; a < argc; ++a) { + args << QString::fromWCharArray(argv[a]); + } + + return args; +} + +static inline QStringList qCmdLineArgs(int argc, char *argv[]) +{ + Q_UNUSED(argc) + Q_UNUSED(argv) + QString cmdLine = QString::fromWCharArray(GetCommandLine()); + return qWinCmdArgs(cmdLine); +} +#endif +#else // !Q_OS_WIN || !Q_OS_SYMBIAN + +static inline QStringList qCmdLineArgs(int argc, char *argv[]) +{ + QStringList args; + for (int i = 0; i != argc; ++i) + args += QString::fromLocal8Bit(argv[i]); + return args; +} + +#endif // Q_OS_WIN || Q_OS_SYMBIAN + +QT_END_NAMESPACE + +#endif // QCORECMDLINEARGS_WIN_P_H diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp new file mode 100644 index 0000000000..fbb08faaf9 --- /dev/null +++ b/src/corelib/kernel/qcoreevent.cpp @@ -0,0 +1,612 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qcoreevent.h" +#include "qcoreapplication.h" +#include "qcoreapplication_p.h" + +#include "qmutex.h" +#include "qset.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QEvent + \brief The QEvent class is the base class of all + event classes. Event objects contain event parameters. + + \ingroup events + + Qt's main event loop (QCoreApplication::exec()) fetches native + window system events from the event queue, translates them into + QEvents, and sends the translated events to \l{QObject}s. + + In general, events come from the underlying window system + (spontaneous() returns true), but it is also possible to manually + send events using QCoreApplication::sendEvent() and + QCoreApplication::postEvent() (spontaneous() returns false). + + QObjects receive events by having their QObject::event() function + called. The function can be reimplemented in subclasses to + customize event handling and add additional event types; + QWidget::event() is a notable example. By default, events are + dispatched to event handlers like QObject::timerEvent() and + QWidget::mouseMoveEvent(). QObject::installEventFilter() allows an + object to intercept events destined for another object. + + The basic QEvent contains only an event type parameter and an + "accept" flag. The accept flag set with accept(), and cleared + with ignore(). It is set by default, but don't rely on this as + subclasses may choose to clear it in their constructor. + + Subclasses of QEvent contain additional parameters that describe + the particular event. + + \sa QObject::event(), QObject::installEventFilter(), + QWidget::event(), QCoreApplication::sendEvent(), + QCoreApplication::postEvent(), QCoreApplication::processEvents() +*/ + + +/*! + \enum QEvent::Type + + This enum type defines the valid event types in Qt. The event + types and the specialized classes for each type are as follows: + + \value None Not an event. + \value AccessibilityDescription Used to query accessibility description texts (QAccessibleEvent). + \value AccessibilityHelp Used to query accessibility help texts (QAccessibleEvent). + \value AccessibilityPrepare Accessibility information is requested. + \value ActionAdded A new action has been added (QActionEvent). + \value ActionChanged An action has been changed (QActionEvent). + \value ActionRemoved An action has been removed (QActionEvent). + \value ActivationChange A widget's top-level window activation state has changed. + \value ApplicationActivate The application has been made available to the user. + \value ApplicationActivated This enum has been deprecated. Use ApplicationActivate instead. + \value ApplicationDeactivate The application has been suspended, and is unavailable to the user. + \value ApplicationFontChange The default application font has changed. + \value ApplicationLayoutDirectionChange The default application layout direction has changed. + \value ApplicationPaletteChange The default application palette has changed. + \value ApplicationWindowIconChange The application's icon has changed. + \value ChildAdded An object gets a child (QChildEvent). + \value ChildInserted An object gets a child (QChildEvent). Qt3Support only, use ChildAdded instead. + \value ChildPolished A widget child gets polished (QChildEvent). + \value ChildRemoved An object loses a child (QChildEvent). + \value Clipboard The clipboard contents have changed (QClipboardEvent). + \value Close Widget was closed (QCloseEvent). + \value CloseSoftwareInputPanel A widget wants to close the software input panel (SIP). + \value ContentsRectChange The margins of the widget's content rect changed. + \value ContextMenu Context popup menu (QContextMenuEvent). + \value CursorChange The widget's cursor has changed. + \value DeferredDelete The object will be deleted after it has cleaned up. + \value DragEnter The cursor enters a widget during a drag and drop operation (QDragEnterEvent). + \value DragLeave The cursor leaves a widget during a drag and drop operation (QDragLeaveEvent). + \value DragMove A drag and drop operation is in progress (QDragMoveEvent). + \value Drop A drag and drop operation is completed (QDropEvent). + \value EnabledChange Widget's enabled state has changed. + \value Enter Mouse enters widget's boundaries. + \value EnterEditFocus An editor widget gains focus for editing. + \value EnterWhatsThisMode Send to toplevel widgets when the application enters "What's This?" mode. + \value FileOpen File open request (QFileOpenEvent). + \value FocusIn Widget gains keyboard focus (QFocusEvent). + \value FocusOut Widget loses keyboard focus (QFocusEvent). + \value FontChange Widget's font has changed. + \value GrabKeyboard Item gains keyboard grab (QGraphicsItem only). + \value GrabMouse Item gains mouse grab (QGraphicsItem only). + \value GraphicsSceneContextMenu Context popup menu over a graphics scene (QGraphicsSceneContextMenuEvent). + \value GraphicsSceneDragEnter The cursor enters a graphics scene during a drag and drop operation (QGraphicsSceneDragDropEvent). + \value GraphicsSceneDragLeave The cursor leaves a graphics scene during a drag and drop operation (QGraphicsSceneDragDropEvent). + \value GraphicsSceneDragMove A drag and drop operation is in progress over a scene (QGraphicsSceneDragDropEvent). + \value GraphicsSceneDrop A drag and drop operation is completed over a scene (QGraphicsSceneDragDropEvent). + \value GraphicsSceneHelp The user requests help for a graphics scene (QHelpEvent). + \value GraphicsSceneHoverEnter The mouse cursor enters a hover item in a graphics scene (QGraphicsSceneHoverEvent). + \value GraphicsSceneHoverLeave The mouse cursor leaves a hover item in a graphics scene (QGraphicsSceneHoverEvent). + \value GraphicsSceneHoverMove The mouse cursor moves inside a hover item in a graphics scene (QGraphicsSceneHoverEvent). + \value GraphicsSceneMouseDoubleClick Mouse press again (double click) in a graphics scene (QGraphicsSceneMouseEvent). + \value GraphicsSceneMouseMove Move mouse in a graphics scene (QGraphicsSceneMouseEvent). + \value GraphicsSceneMousePress Mouse press in a graphics scene (QGraphicsSceneMouseEvent). + \value GraphicsSceneMouseRelease Mouse release in a graphics scene (QGraphicsSceneMouseEvent). + \value GraphicsSceneMove Widget was moved (QGraphicsSceneMoveEvent). + \value GraphicsSceneResize Widget was resized (QGraphicsSceneResizeEvent). + \value GraphicsSceneWheel Mouse wheel rolled in a graphics scene (QGraphicsSceneWheelEvent). + \value Hide Widget was hidden (QHideEvent). + \value HideToParent A child widget has been hidden. + \value HoverEnter The mouse cursor enters a hover widget (QHoverEvent). + \value HoverLeave The mouse cursor leaves a hover widget (QHoverEvent). + \value HoverMove The mouse cursor moves inside a hover widget (QHoverEvent). + \value IconDrag The main icon of a window has been dragged away (QIconDragEvent). + \value IconTextChange Widget's icon text has been changed. + \value InputMethod An input method is being used (QInputMethodEvent). + \value KeyPress Key press (QKeyEvent). + \value KeyRelease Key release (QKeyEvent). + \value LanguageChange The application translation changed. + \value LayoutDirectionChange The direction of layouts changed. + \value LayoutRequest Widget layout needs to be redone. + \value Leave Mouse leaves widget's boundaries. + \value LeaveEditFocus An editor widget loses focus for editing. + \value LeaveWhatsThisMode Send to toplevel widgets when the application leaves "What's This?" mode. + \value LocaleChange The system locale has changed. + \value NonClientAreaMouseButtonDblClick A mouse double click occurred outside the client area. + \value NonClientAreaMouseButtonPress A mouse button press occurred outside the client area. + \value NonClientAreaMouseButtonRelease A mouse button release occurred outside the client area. + \value NonClientAreaMouseMove A mouse move occurred outside the client area. + \value MacSizeChange The user changed his widget sizes (Mac OS X only). + \value MenubarUpdated The window's menu bar has been updated. + \value MetaCall An asynchronous method invocation via QMetaObject::invokeMethod(). + \value ModifiedChange Widgets modification state has been changed. + \value MouseButtonDblClick Mouse press again (QMouseEvent). + \value MouseButtonPress Mouse press (QMouseEvent). + \value MouseButtonRelease Mouse release (QMouseEvent). + \value MouseMove Mouse move (QMouseEvent). + \value MouseTrackingChange The mouse tracking state has changed. + \value Move Widget's position changed (QMoveEvent). + \value Paint Screen update necessary (QPaintEvent). + \value PaletteChange Palette of the widget changed. + \value ParentAboutToChange The widget parent is about to change. + \value ParentChange The widget parent has changed. + \value Polish The widget is polished. + \value PolishRequest The widget should be polished. + \value QueryWhatsThis The widget should accept the event if it has "What's This?" help. + \value RequestSoftwareInputPanel A widget wants to open a software input panel (SIP). + \value Resize Widget's size changed (QResizeEvent). + \value Shortcut Key press in child for shortcut key handling (QShortcutEvent). + \value ShortcutOverride Key press in child, for overriding shortcut key handling (QKeyEvent). + \value Show Widget was shown on screen (QShowEvent). + \value ShowToParent A child widget has been shown. + \value SockAct Socket activated, used to implement QSocketNotifier. + \value StateMachineSignal A signal delivered to a state machine (QStateMachine::SignalEvent). + \value StateMachineWrapped The event is a wrapper for, i.e., contains, another event (QStateMachine::WrappedEvent). + \value StatusTip A status tip is requested (QStatusTipEvent). + \value StyleChange Widget's style has been changed. + \value TabletMove Wacom tablet move (QTabletEvent). + \value TabletPress Wacom tablet press (QTabletEvent). + \value TabletRelease Wacom tablet release (QTabletEvent). + \value OkRequest Ok button in decoration pressed. Supported only for Windows CE. + \value TabletEnterProximity Wacom tablet enter proximity event (QTabletEvent), sent to QApplication. + \value TabletLeaveProximity Wacom tablet leave proximity event (QTabletEvent), sent to QApplication. + \value Timer Regular timer events (QTimerEvent). + \value ToolBarChange The toolbar button is toggled on Mac OS X. + \value ToolTip A tooltip was requested (QHelpEvent). + \value ToolTipChange The widget's tooltip has changed. + \value UngrabKeyboard Item loses keyboard grab (QGraphicsItem only). + \value UngrabMouse Item loses mouse grab (QGraphicsItem only). + \value UpdateLater The widget should be queued to be repainted at a later time. + \value UpdateRequest The widget should be repainted. + \value WhatsThis The widget should reveal "What's This?" help (QHelpEvent). + \value WhatsThisClicked A link in a widget's "What's This?" help was clicked. + \value Wheel Mouse wheel rolled (QWheelEvent). + \value WinEventAct A Windows-specific activation event has occurred. + \value WindowActivate Window was activated. + \value WindowBlocked The window is blocked by a modal dialog. + \value WindowDeactivate Window was deactivated. + \value WindowIconChange The window's icon has changed. + \value WindowStateChange The \l{QWidget::windowState()}{window's state} (minimized, maximized or full-screen) has changed (QWindowStateChangeEvent). + \value WindowTitleChange The window title has changed. + \value WindowUnblocked The window is unblocked after a modal dialog exited. + \value ZOrderChange The widget's z-order has changed. This event is never sent to top level windows. + \value KeyboardLayoutChange The keyboard layout has changed. + \value DynamicPropertyChange A dynamic property was added, changed or removed from the object. + \value TouchBegin Beginning of a sequence of touch-screen and/or track-pad events (QTouchEvent) + \value TouchUpdate Touch-screen event (QTouchEvent) + \value TouchEnd End of touch-event sequence (QTouchEvent) + \value WinIdChange The window system identifer for this native widget has changed + \value Gesture A gesture was triggered (QGestureEvent) + \value GestureOverride A gesture override was triggered (QGestureEvent) + \value ScrollPrepare The object needs to fill in its geometry information (QScrollPrepareEvent) + \value Scroll The object needs to scroll to the supplied position (QScrollEvent) + + User events should have values between \c User and \c{MaxUser}: + + \value User User-defined event. + \value MaxUser Last user event ID. + + For convenience, you can use the registerEventType() function to + register and reserve a custom event type for your + application. Doing so will allow you to avoid accidentally + re-using a custom event type already in use elsewhere in your + application. + + \omitvalue Accel + \omitvalue AccelAvailable + \omitvalue AccelOverride + \omitvalue AcceptDropsChange + \omitvalue ActivateControl + \omitvalue CaptionChange + \omitvalue ChildInsertedRequest + \omitvalue ChildInserted + \omitvalue Create + \omitvalue DeactivateControl + \omitvalue Destroy + \omitvalue DragResponse + \omitvalue EmbeddingControl + \omitvalue HelpRequest + \omitvalue IconChange + \omitvalue LayoutHint + \omitvalue Quit + \omitvalue Reparent + \omitvalue ShowWindowRequest + \omitvalue Speech + \omitvalue Style + \omitvalue ThreadChange + \omitvalue ZeroTimerEvent + \omitvalue ApplicationActivated + \omitvalue ApplicationDeactivated + \omitvalue MacGLWindowChange + \omitvalue MacGLClearDrawable + \omitvalue NetworkReplyUpdated + \omitvalue FutureCallOut + \omitvalue UpdateSoftKeys + \omitvalue NativeGesture +*/ + +/*! + Contructs an event object of type \a type. +*/ +QEvent::QEvent(Type type) + : d(0), t(type), posted(false), spont(false), m_accept(true) +{} + +/*! + Destroys the event. If it was \link + QCoreApplication::postEvent() posted \endlink, + it will be removed from the list of events to be posted. +*/ + +QEvent::~QEvent() +{ + if (posted && QCoreApplication::instance()) + QCoreApplicationPrivate::removePostedEvent(this); +} + + +/*! + \property QEvent::accepted + the accept flag of the event object + + Setting the accept parameter indicates that the event receiver + wants the event. Unwanted events might be propagated to the parent + widget. By default, isAccepted() is set to true, but don't rely on + this as subclasses may choose to clear it in their constructor. + + For convenience, the accept flag can also be set with accept(), + and cleared with ignore(). +*/ + +/*! + \fn void QEvent::accept() + + Sets the accept flag of the event object, the equivalent of + calling setAccepted(true). + + Setting the accept parameter indicates that the event receiver + wants the event. Unwanted events might be propagated to the parent + widget. + + \sa ignore() +*/ + + +/*! + \fn void QEvent::ignore() + + Clears the accept flag parameter of the event object, the + equivalent of calling setAccepted(false). + + Clearing the accept parameter indicates that the event receiver + does not want the event. Unwanted events might be propagated to the + parent widget. + + \sa accept() +*/ + + +/*! + \fn QEvent::Type QEvent::type() const + + Returns the event type. +*/ + +/*! + \fn bool QEvent::spontaneous() const + + Returns true if the event originated outside the application (a + system event); otherwise returns false. + + The return value of this function is not defined for paint events. +*/ + +class QEventUserEventRegistration +{ +public: + QMutex mutex; + QSet set; +}; +Q_GLOBAL_STATIC(QEventUserEventRegistration, userEventRegistrationHelper) + +/*! + \since 4.4 + \threadsafe + + Registers and returns a custom event type. The \a hint provided + will be used if it is available, otherwise it will return a value + between QEvent::User and QEvent::MaxUser that has not yet been + registered. The \a hint is ignored if its value is not between + QEvent::User and QEvent::MaxUser. +*/ +int QEvent::registerEventType(int hint) +{ + QEventUserEventRegistration *userEventRegistration + = userEventRegistrationHelper(); + if (!userEventRegistration) + return -1; + + QMutexLocker locker(&userEventRegistration->mutex); + + // if the type hint hasn't been registered yet, take it + if (hint >= QEvent::User && hint <= QEvent::MaxUser && !userEventRegistration->set.contains(hint)) { + userEventRegistration->set.insert(hint); + return hint; + } + + // find a free event type, starting at MaxUser and decreasing + int id = QEvent::MaxUser; + while (userEventRegistration->set.contains(id) && id >= QEvent::User) + --id; + if (id >= QEvent::User) { + userEventRegistration->set.insert(id); + return id; + } + return -1; +} + +/*! + \class QTimerEvent + \brief The QTimerEvent class contains parameters that describe a + timer event. + + \ingroup events + + Timer events are sent at regular intervals to objects that have + started one or more timers. Each timer has a unique identifier. A + timer is started with QObject::startTimer(). + + The QTimer class provides a high-level programming interface that + uses signals instead of events. It also provides single-shot timers. + + The event handler QObject::timerEvent() receives timer events. + + \sa QTimer, QObject::timerEvent(), QObject::startTimer(), + QObject::killTimer() +*/ + +/*! + Constructs a timer event object with the timer identifier set to + \a timerId. +*/ +QTimerEvent::QTimerEvent(int timerId) + : QEvent(Timer), id(timerId) +{} + +/*! \internal +*/ +QTimerEvent::~QTimerEvent() +{ +} + +/*! + \fn int QTimerEvent::timerId() const + + Returns the unique timer identifier, which is the same identifier + as returned from QObject::startTimer(). +*/ + +/*! + \class QChildEvent + \brief The QChildEvent class contains event parameters for child object + events. + + \ingroup events + + Child events are sent immediately to objects when children are + added or removed. + + In both cases you can only rely on the child being a QObject (or, + if QObject::isWidgetType() returns true, a QWidget). This is + because in the QEvent::ChildAdded case the child is not yet fully + constructed; in the QEvent::ChildRemoved case it might have + already been destructed. + + The handler for these events is QObject::childEvent(). +*/ + +/*! + Constructs a child event object of a particular \a type for the + \a child. + + \a type can be QEvent::ChildAdded, QEvent::ChildRemoved, + QEvent::ChildPolished, or QEvent::ChildRemoved. + + \sa child() +*/ +QChildEvent::QChildEvent(Type type, QObject *child) + : QEvent(type), c(child) +{} + +/*! \internal +*/ +QChildEvent::~QChildEvent() +{ +} + +/*! + \fn QObject *QChildEvent::child() const + + Returns the child object that was added or removed. +*/ + +/*! + \fn bool QChildEvent::added() const + + Returns true if type() is QEvent::ChildAdded; otherwise returns + false. +*/ + +/*! + \fn bool QChildEvent::removed() const + + Returns true if type() is QEvent::ChildRemoved; otherwise returns + false. +*/ + +/*! + \fn bool QChildEvent::polished() const + + Returns true if type() is QEvent::ChildPolished; otherwise returns + false. +*/ + +/*! + \class QCustomEvent + \brief The QCustomEvent class provides support for custom events. + + \compat + + QCustomEvent has a \c{void *} that can be used to store custom + data. + + In Qt 3, QObject::customEvent() took a QCustomEvent pointer. We + found out that this approach was unsatisfactory, because + there was often no safe way of deleting the data held in the + \c{void *}. + + In Qt 4, QObject::customEvent() takes a plain QEvent pointer. + You can add custom data by subclassing. + + \sa QObject::customEvent(), QCoreApplication::notify() +*/ + +/*! + \fn QCustomEvent::QCustomEvent(int type, void *data) + + Constructs a custom event object with the event \a type and a + pointer to \a data. The value of \a type must be at least as + large as QEvent::User. By default, the data pointer is set to 0. +*/ +#ifdef QT3_SUPPORT +QCustomEvent::QCustomEvent(int type, void *data) + : QEvent(static_cast(type)) +{ + d = reinterpret_cast(data); +} + +/*! \internal +*/ +QCustomEvent::~QCustomEvent() +{ +} +#endif +/*! + \fn void QCustomEvent::setData(void *data) + + \compat + + Sets the generic data pointer to \a data. + + \sa data() +*/ + +/*! + \fn void *QCustomEvent::data() const + + \compat + + Returns a pointer to the generic event data. + + \sa setData() +*/ + +/*! + \fn bool QChildEvent::inserted() const + + \compat + + A child has been inserted if the event's type() is ChildInserted. +*/ + +/*! + \class QDynamicPropertyChangeEvent + \since 4.2 + \brief The QDynamicPropertyChangeEvent class contains event parameters for dynamic + property change events. + + \ingroup events + + Dynamic property change events are sent to objects when properties are + dynamically added, changed or removed using QObject::setProperty(). +*/ + +/*! + Constructs a dynamic property change event object with the property name set to + \a name. +*/ +QDynamicPropertyChangeEvent::QDynamicPropertyChangeEvent(const QByteArray &name) + : QEvent(QEvent::DynamicPropertyChange), n(name) +{ +} + +/*! + \internal +*/ +QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent() +{ +} + +/*! + \fn QByteArray QDynamicPropertyChangeEvent::propertyName() const + + Returns the name of the dynamic property that was added, changed or + removed. + + \sa QObject::setProperty(), QObject::dynamicPropertyNames() +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h new file mode 100644 index 0000000000..c9d311ae35 --- /dev/null +++ b/src/corelib/kernel/qcoreevent.h @@ -0,0 +1,396 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QCOREEVENT_H +#define QCOREEVENT_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEventPrivate; +class Q_CORE_EXPORT QEvent // event base class +{ + Q_GADGET + QDOC_PROPERTY(bool accepted READ isAccepted WRITE setAccepted) + Q_ENUMS(Type) +public: + enum Type { + /* + If you get a strange compiler error on the line with None, + it's probably because you're also including X11 headers, + which #define the symbol None. Put the X11 includes after + the Qt includes to solve this problem. + */ + None = 0, // invalid event + Timer = 1, // timer event + MouseButtonPress = 2, // mouse button pressed + MouseButtonRelease = 3, // mouse button released + MouseButtonDblClick = 4, // mouse button double click + MouseMove = 5, // mouse move + KeyPress = 6, // key pressed + KeyRelease = 7, // key released + FocusIn = 8, // keyboard focus received + FocusOut = 9, // keyboard focus lost + Enter = 10, // mouse enters widget + Leave = 11, // mouse leaves widget + Paint = 12, // paint widget + Move = 13, // move widget + Resize = 14, // resize widget + Create = 15, // after widget creation + Destroy = 16, // during widget destruction + Show = 17, // widget is shown + Hide = 18, // widget is hidden + Close = 19, // request to close widget + Quit = 20, // request to quit application + ParentChange = 21, // widget has been reparented + ParentAboutToChange = 131, // sent just before the parent change is done +#ifdef QT3_SUPPORT + Reparent = ParentChange, +#endif + ThreadChange = 22, // object has changed threads + WindowActivate = 24, // window was activated + WindowDeactivate = 25, // window was deactivated + ShowToParent = 26, // widget is shown to parent + HideToParent = 27, // widget is hidden to parent + Wheel = 31, // wheel event + WindowTitleChange = 33, // window title changed + WindowIconChange = 34, // icon changed + ApplicationWindowIconChange = 35, // application icon changed + ApplicationFontChange = 36, // application font changed + ApplicationLayoutDirectionChange = 37, // application layout direction changed + ApplicationPaletteChange = 38, // application palette changed + PaletteChange = 39, // widget palette changed + Clipboard = 40, // internal clipboard event + Speech = 42, // reserved for speech input + MetaCall = 43, // meta call event + SockAct = 50, // socket activation + WinEventAct = 132, // win event activation + DeferredDelete = 52, // deferred delete event + DragEnter = 60, // drag moves into widget + DragMove = 61, // drag moves in widget + DragLeave = 62, // drag leaves or is cancelled + Drop = 63, // actual drop + DragResponse = 64, // drag accepted/rejected + ChildAdded = 68, // new child widget + ChildPolished = 69, // polished child widget +#ifdef QT3_SUPPORT + ChildInsertedRequest = 67, // send ChildInserted compatibility events to receiver + ChildInserted = 70, // compatibility child inserted + LayoutHint = 72, // compatibility relayout request +#endif + ChildRemoved = 71, // deleted child widget + ShowWindowRequest = 73, // widget's window should be mapped + PolishRequest = 74, // widget should be polished + Polish = 75, // widget is polished + LayoutRequest = 76, // widget should be relayouted + UpdateRequest = 77, // widget should be repainted + UpdateLater = 78, // request update() later + + EmbeddingControl = 79, // ActiveX embedding + ActivateControl = 80, // ActiveX activation + DeactivateControl = 81, // ActiveX deactivation + ContextMenu = 82, // context popup menu + InputMethod = 83, // input method + AccessibilityPrepare = 86, // accessibility information is requested + TabletMove = 87, // Wacom tablet event + LocaleChange = 88, // the system locale changed + LanguageChange = 89, // the application language changed + LayoutDirectionChange = 90, // the layout direction changed + Style = 91, // internal style event + TabletPress = 92, // tablet press + TabletRelease = 93, // tablet release + OkRequest = 94, // CE (Ok) button pressed + HelpRequest = 95, // CE (?) button pressed + + IconDrag = 96, // proxy icon dragged + + FontChange = 97, // font has changed + EnabledChange = 98, // enabled state has changed + ActivationChange = 99, // window activation has changed + StyleChange = 100, // style has changed + IconTextChange = 101, // icon text has changed + ModifiedChange = 102, // modified state has changed + MouseTrackingChange = 109, // mouse tracking state has changed + + WindowBlocked = 103, // window is about to be blocked modally + WindowUnblocked = 104, // windows modal blocking has ended + WindowStateChange = 105, + + ToolTip = 110, + WhatsThis = 111, + StatusTip = 112, + + ActionChanged = 113, + ActionAdded = 114, + ActionRemoved = 115, + + FileOpen = 116, // file open request + + Shortcut = 117, // shortcut triggered + ShortcutOverride = 51, // shortcut override request + +#ifdef QT3_SUPPORT + Accel = 30, // accelerator event + AccelAvailable = 32, // accelerator available event + AccelOverride = ShortcutOverride, // accelerator override event +#endif + + WhatsThisClicked = 118, + +#ifdef QT3_SUPPORT + CaptionChange = WindowTitleChange, + IconChange = WindowIconChange, +#endif + ToolBarChange = 120, // toolbar visibility toggled + + ApplicationActivate = 121, // application has been changed to active + ApplicationActivated = ApplicationActivate, // deprecated + ApplicationDeactivate = 122, // application has been changed to inactive + ApplicationDeactivated = ApplicationDeactivate, // deprecated + + QueryWhatsThis = 123, // query what's this widget help + EnterWhatsThisMode = 124, + LeaveWhatsThisMode = 125, + + ZOrderChange = 126, // child widget has had its z-order changed + + HoverEnter = 127, // mouse cursor enters a hover widget + HoverLeave = 128, // mouse cursor leaves a hover widget + HoverMove = 129, // mouse cursor move inside a hover widget + + AccessibilityHelp = 119, // accessibility help text request + AccessibilityDescription = 130, // accessibility description text request + + // last event id used = 132 + +#ifdef QT_KEYPAD_NAVIGATION + EnterEditFocus = 150, // enter edit mode in keypad navigation + LeaveEditFocus = 151, // enter edit mode in keypad navigation +#endif + AcceptDropsChange = 152, + + MenubarUpdated = 153, // Support event for Q3MainWindow, which needs to + // knwow when QMenubar is updated. + + ZeroTimerEvent = 154, // Used for Windows Zero timer events + + GraphicsSceneMouseMove = 155, // GraphicsView + GraphicsSceneMousePress = 156, + GraphicsSceneMouseRelease = 157, + GraphicsSceneMouseDoubleClick = 158, + GraphicsSceneContextMenu = 159, + GraphicsSceneHoverEnter = 160, + GraphicsSceneHoverMove = 161, + GraphicsSceneHoverLeave = 162, + GraphicsSceneHelp = 163, + GraphicsSceneDragEnter = 164, + GraphicsSceneDragMove = 165, + GraphicsSceneDragLeave = 166, + GraphicsSceneDrop = 167, + GraphicsSceneWheel = 168, + + KeyboardLayoutChange = 169, // keyboard layout changed + + DynamicPropertyChange = 170, // A dynamic property was changed through setProperty/property + + TabletEnterProximity = 171, + TabletLeaveProximity = 172, + + NonClientAreaMouseMove = 173, + NonClientAreaMouseButtonPress = 174, + NonClientAreaMouseButtonRelease = 175, + NonClientAreaMouseButtonDblClick = 176, + + MacSizeChange = 177, // when the Qt::WA_Mac{Normal,Small,Mini}Size changes + + ContentsRectChange = 178, // sent by QWidget::setContentsMargins (internal) + + MacGLWindowChange = 179, // Internal! the window of the GLWidget has changed + + FutureCallOut = 180, + + GraphicsSceneResize = 181, + GraphicsSceneMove = 182, + + CursorChange = 183, + ToolTipChange = 184, + + NetworkReplyUpdated = 185, // Internal for QNetworkReply + + GrabMouse = 186, + UngrabMouse = 187, + GrabKeyboard = 188, + UngrabKeyboard = 189, + MacGLClearDrawable = 191, // Internal Cocoa, the window has changed, so we must clear + + StateMachineSignal = 192, + StateMachineWrapped = 193, + + TouchBegin = 194, + TouchUpdate = 195, + TouchEnd = 196, + +#ifndef QT_NO_GESTURES + NativeGesture = 197, // Internal for platform gesture support +#endif + RequestSoftwareInputPanel = 199, + CloseSoftwareInputPanel = 200, + + UpdateSoftKeys = 201, // Internal for compressing soft key updates + + WinIdChange = 203, +#ifndef QT_NO_GESTURES + Gesture = 198, + GestureOverride = 202, +#endif + ScrollPrepare = 204, + Scroll = 205, + + // 512 reserved for Qt Jambi's MetaCall event + // 513 reserved for Qt Jambi's DeleteOnMainThread event + + User = 1000, // first user event id + MaxUser = 65535 // last user event id + }; + + QEvent(Type type); + virtual ~QEvent(); + inline Type type() const { return static_cast(t); } + inline bool spontaneous() const { return spont; } + + inline void setAccepted(bool accepted) { m_accept = accepted; } + inline bool isAccepted() const { return m_accept; } + + inline void accept() { m_accept = true; } + inline void ignore() { m_accept = false; } + + static int registerEventType(int hint = -1); + +protected: + QEventPrivate *d; + ushort t; + +private: + ushort posted : 1; + ushort spont : 1; + ushort m_accept : 1; + ushort reserved : 13; + + friend class QCoreApplication; + friend class QCoreApplicationPrivate; + friend class QThreadData; + friend class QApplication; + friend class QApplicationPrivate; + friend class Q3AccelManager; + friend class QShortcutMap; + friend class QETWidget; + friend class QGraphicsView; + friend class QGraphicsViewPrivate; + friend class QGraphicsScene; + friend class QGraphicsScenePrivate; +#ifndef QT_NO_GESTURES + friend class QGestureManager; +#endif +}; + +class Q_CORE_EXPORT QTimerEvent : public QEvent +{ +public: + QTimerEvent( int timerId ); + ~QTimerEvent(); + int timerId() const { return id; } +protected: + int id; +}; + +class QObject; + +class Q_CORE_EXPORT QChildEvent : public QEvent +{ +public: + QChildEvent( Type type, QObject *child ); + ~QChildEvent(); + QObject *child() const { return c; } + bool added() const { return type() == ChildAdded; } +#ifdef QT3_SUPPORT + QT3_SUPPORT bool inserted() const { return type() == ChildInserted; } +#endif + bool polished() const { return type() == ChildPolished; } + bool removed() const { return type() == ChildRemoved; } +protected: + QObject *c; +}; + +#ifdef QT3_SUPPORT +class Q_CORE_EXPORT QCustomEvent : public QEvent +{ +public: + QT3_SUPPORT_CONSTRUCTOR QCustomEvent(int type, void *data = 0); + ~QCustomEvent(); + QT3_SUPPORT void *data() const { return d; } + QT3_SUPPORT void setData(void* aData) { d = reinterpret_cast(aData); } +}; +#endif + +class Q_CORE_EXPORT QDynamicPropertyChangeEvent : public QEvent +{ +public: + QDynamicPropertyChangeEvent(const QByteArray &name); + ~QDynamicPropertyChangeEvent(); + + inline QByteArray propertyName() const { return n; } + +private: + QByteArray n; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QCOREEVENT_H diff --git a/src/corelib/kernel/qcoreglobaldata.cpp b/src/corelib/kernel/qcoreglobaldata.cpp new file mode 100644 index 0000000000..d90b46cb26 --- /dev/null +++ b/src/corelib/kernel/qcoreglobaldata.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qcoreglobaldata_p.h" + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QCoreGlobalData, globalInstance) + +QCoreGlobalData *QCoreGlobalData::instance() +{ + return globalInstance(); +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcoreglobaldata_p.h b/src/corelib/kernel/qcoreglobaldata_p.h new file mode 100644 index 0000000000..aeb7bd8840 --- /dev/null +++ b/src/corelib/kernel/qcoreglobaldata_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QCOREGLOBALDATA_P_H +#define QCOREGLOBALDATA_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/qmap.h" +#include "QtCore/qstringlist.h" +#include "QtCore/qreadwritelock.h" + +QT_BEGIN_NAMESPACE + +struct QCoreGlobalData { + QMap dirSearchPaths; + QReadWriteLock dirSearchPathsLock; + + static QCoreGlobalData *instance(); +}; + + +QT_END_NAMESPACE +#endif // QCOREGLOBALDATA_P_H + diff --git a/src/corelib/kernel/qcrashhandler.cpp b/src/corelib/kernel/qcrashhandler.cpp new file mode 100644 index 0000000000..fbdbac0b5c --- /dev/null +++ b/src/corelib/kernel/qcrashhandler.cpp @@ -0,0 +1,423 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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$ +** +****************************************************************************/ + +/************************************************************************* + * + * stacktrace.c 1.2 1998/12/21 + * + * Copyright (c) 1998 by Bjorn Reese + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + ************************************************************************/ + +#include "qplatformdefs.h" +#include "private/qcrashhandler_p.h" +#include "qbytearray.h" // for qvsnprintf() + +#ifndef QT_NO_CRASHHANDLER + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QtCrashHandler QSegfaultHandler::callback = 0; + +#if defined(__GLIBC__) && (__GLIBC__ >= 2) && !defined(__UCLIBC__) && !defined(QT_LINUXBASE) +QT_BEGIN_INCLUDE_NAMESPACE +# include "qstring.h" +# include +QT_END_INCLUDE_NAMESPACE + +static void print_backtrace(FILE *outb) +{ + void *stack[128]; + int stack_size = backtrace(stack, sizeof(stack) / sizeof(void *)); + char **stack_symbols = backtrace_symbols(stack, stack_size); + fprintf(outb, "Stack [%d]:\n", stack_size); + if(FILE *cppfilt = popen("c++filt", "rw")) { + dup2(fileno(outb), fileno(cppfilt)); + for(int i = stack_size-1; i>=0; --i) + fwrite(stack_symbols[i], 1, strlen(stack_symbols[i]), cppfilt); + pclose(cppfilt); + } else { + for(int i = stack_size-1; i>=0; --i) + fprintf(outb, "#%d %p [%s]\n", i, stack[i], stack_symbols[i]); + } +} +static void init_backtrace(char **, int) +{ +} + +#else /* Don't use the GLIBC callback */ +/* Code sourced from: */ +QT_BEGIN_INCLUDE_NAMESPACE +#include +#include +#include +#include +#include +#if defined(Q_OS_IRIX) && defined(USE_LIBEXC) +# include +#endif +QT_END_INCLUDE_NAMESPACE + + +static char *globalProgName = NULL; +static bool backtrace_command(FILE *outb, const char *format, ...) +{ + + bool ret = false; + char buffer[50]; + + /* + * Please note that vsnprintf() is not ASync safe (ie. cannot safely + * be used from a signal handler.) If this proves to be a problem + * then the cmd string can be built by more basic functions such as + * strcpy, strcat, and a home-made integer-to-ascii function. + */ + va_list args; + char cmd[512]; + va_start(args, format); + qvsnprintf(cmd, 512, format, args); + va_end(args); + + char *foo = cmd; +#if 0 + foo = "echo hi"; +#endif + if(FILE *inb = popen(foo, "r")) { + while(!feof(inb)) { + int len = fread(buffer, 1, sizeof(buffer), inb); + if(!len) + break; + if(!ret) { + fwrite("Output from ", 1, strlen("Output from "), outb); + strtok(cmd, " "); + fwrite(cmd, 1, strlen(cmd), outb); + fwrite("\n", 1, 1, outb); + ret = true; + } + fwrite(buffer, 1, len, outb); + } + fclose(inb); + } + return ret; +} + +static void init_backtrace(char **argv, int argc) +{ + if(argc >= 1) + globalProgName = argv[0]; +} + +static void print_backtrace(FILE *outb) +{ + /* + * In general dbx seems to do a better job than gdb. + * + * Different dbx implementations require different flags/commands. + */ +#if defined(Q_OS_AIX) + if(backtrace_command(outb, "dbx -a %d 2>/dev/null </dev/null </dev/null <&1 </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null </dev/null <&1 < </dev/null </dev/null < + +#ifndef QT_NO_CRASHHANDLER + +QT_BEGIN_NAMESPACE + +typedef void (*QtCrashHandler)(); + +class Q_CORE_EXPORT QSegfaultHandler +{ + friend void qt_signal_handler(int); + static QtCrashHandler callback; +public: + static void initialize(char **, int); + + inline static void installCrashHandler(QtCrashHandler h) { callback = h; } + inline static QtCrashHandler crashHandler() { return callback; } + +private: +}; + +QT_END_NAMESPACE + +#endif // QT_NO_CRASHHANDLER + +#endif // QCRASHHANDLER_P_H diff --git a/src/corelib/kernel/qeventdispatcher_glib.cpp b/src/corelib/kernel/qeventdispatcher_glib.cpp new file mode 100644 index 0000000000..4e19414231 --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_glib.cpp @@ -0,0 +1,601 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qeventdispatcher_glib_p.h" +#include "qeventdispatcher_unix_p.h" + +#include +#include + +#include "qcoreapplication.h" +#include "qsocketnotifier.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +struct GPollFDWithQSocketNotifier +{ + GPollFD pollfd; + QSocketNotifier *socketNotifier; +}; + +struct GSocketNotifierSource +{ + GSource source; + QList pollfds; +}; + +static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout) +{ + if (timeout) + *timeout = -1; + return false; +} + +static gboolean socketNotifierSourceCheck(GSource *source) +{ + GSocketNotifierSource *src = reinterpret_cast(source); + + bool pending = false; + for (int i = 0; !pending && i < src->pollfds.count(); ++i) { + GPollFDWithQSocketNotifier *p = src->pollfds.at(i); + + if (p->pollfd.revents & G_IO_NVAL) { + // disable the invalid socket notifier + static const char *t[] = { "Read", "Write", "Exception" }; + qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...", + p->pollfd.fd, t[int(p->socketNotifier->type())]); + // ### note, modifies src->pollfds! + p->socketNotifier->setEnabled(false); + } + + pending = ((p->pollfd.revents & p->pollfd.events) != 0); + } + + return pending; +} + +static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpointer) +{ + QEvent event(QEvent::SockAct); + + GSocketNotifierSource *src = reinterpret_cast(source); + for (int i = 0; i < src->pollfds.count(); ++i) { + GPollFDWithQSocketNotifier *p = src->pollfds.at(i); + + if ((p->pollfd.revents & p->pollfd.events) != 0) + QCoreApplication::sendEvent(p->socketNotifier, &event); + } + + return true; // ??? don't remove, right? +} + +static GSourceFuncs socketNotifierSourceFuncs = { + socketNotifierSourcePrepare, + socketNotifierSourceCheck, + socketNotifierSourceDispatch, + NULL, + NULL, + NULL +}; + +struct GTimerSource +{ + GSource source; + QTimerInfoList timerList; + QEventLoop::ProcessEventsFlags processEventsFlags; + bool runWithIdlePriority; +}; + +static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout) +{ + timeval tv = { 0l, 0l }; + if (!(src->processEventsFlags & QEventLoop::X11ExcludeTimers) && src->timerList.timerWait(tv)) + *timeout = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + else + *timeout = -1; + + return (*timeout == 0); +} + +static gboolean timerSourceCheckHelper(GTimerSource *src) +{ + if (src->timerList.isEmpty() + || (src->processEventsFlags & QEventLoop::X11ExcludeTimers)) + return false; + + if (src->timerList.updateCurrentTime() < src->timerList.first()->timeout) + return false; + + return true; +} + +static gboolean timerSourcePrepare(GSource *source, gint *timeout) +{ + gint dummy; + if (!timeout) + timeout = &dummy; + + GTimerSource *src = reinterpret_cast(source); + if (src->runWithIdlePriority) { + if (timeout) + *timeout = -1; + return false; + } + + return timerSourcePrepareHelper(src, timeout); +} + +static gboolean timerSourceCheck(GSource *source) +{ + GTimerSource *src = reinterpret_cast(source); + if (src->runWithIdlePriority) + return false; + return timerSourceCheckHelper(src); +} + +static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer) +{ + GTimerSource *timerSource = reinterpret_cast(source); + if (timerSource->processEventsFlags & QEventLoop::X11ExcludeTimers) + return true; + timerSource->runWithIdlePriority = true; + (void) timerSource->timerList.activateTimers(); + return true; // ??? don't remove, right again? +} + +static GSourceFuncs timerSourceFuncs = { + timerSourcePrepare, + timerSourceCheck, + timerSourceDispatch, + NULL, + NULL, + NULL +}; + +struct GIdleTimerSource +{ + GSource source; + GTimerSource *timerSource; +}; + +static gboolean idleTimerSourcePrepare(GSource *source, gint *timeout) +{ + GIdleTimerSource *idleTimerSource = reinterpret_cast(source); + GTimerSource *timerSource = idleTimerSource->timerSource; + if (!timerSource->runWithIdlePriority) { + // Yield to the normal priority timer source + if (timeout) + *timeout = -1; + return false; + } + + return timerSourcePrepareHelper(timerSource, timeout); +} + +static gboolean idleTimerSourceCheck(GSource *source) +{ + GIdleTimerSource *idleTimerSource = reinterpret_cast(source); + GTimerSource *timerSource = idleTimerSource->timerSource; + if (!timerSource->runWithIdlePriority) { + // Yield to the normal priority timer source + return false; + } + return timerSourceCheckHelper(timerSource); +} + +static gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer) +{ + GTimerSource *timerSource = reinterpret_cast(source)->timerSource; + (void) timerSourceDispatch(&timerSource->source, 0, 0); + return true; +} + +static GSourceFuncs idleTimerSourceFuncs = { + idleTimerSourcePrepare, + idleTimerSourceCheck, + idleTimerSourceDispatch, + NULL, + NULL, + NULL +}; + +struct GPostEventSource +{ + GSource source; + QAtomicInt serialNumber; + int lastSerialNumber; + QEventDispatcherGlibPrivate *d; +}; + +static gboolean postEventSourcePrepare(GSource *s, gint *timeout) +{ + QThreadData *data = QThreadData::current(); + if (!data) + return false; + + gint dummy; + if (!timeout) + timeout = &dummy; + *timeout = data->canWait ? -1 : 0; + + GPostEventSource *source = reinterpret_cast(s); + return (!data->canWait + || (source->serialNumber != source->lastSerialNumber)); +} + +static gboolean postEventSourceCheck(GSource *source) +{ + return postEventSourcePrepare(source, 0); +} + +static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer) +{ + GPostEventSource *source = reinterpret_cast(s); + source->lastSerialNumber = source->serialNumber; + QCoreApplication::sendPostedEvents(); + source->d->runTimersOnceWithNormalPriority(); + return true; // i dunno, george... +} + +static GSourceFuncs postEventSourceFuncs = { + postEventSourcePrepare, + postEventSourceCheck, + postEventSourceDispatch, + NULL, + NULL, + NULL +}; + + +QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context) + : mainContext(context) +{ + if (qgetenv("QT_NO_THREADED_GLIB").isEmpty()) { + static int dummyValue = 0; // only used for its address + QMutexLocker locker(QMutexPool::instance()->get(&dummyValue)); + if (!g_thread_supported()) + g_thread_init(NULL); + } + + if (mainContext) { + g_main_context_ref(mainContext); + } else { + QCoreApplication *app = QCoreApplication::instance(); + if (app && QThread::currentThread() == app->thread()) { + mainContext = g_main_context_default(); + g_main_context_ref(mainContext); + } else { + mainContext = g_main_context_new(); + } + } + +#if GLIB_CHECK_VERSION (2, 22, 0) + g_main_context_push_thread_default (mainContext); +#endif + + // setup post event source + postEventSource = reinterpret_cast(g_source_new(&postEventSourceFuncs, + sizeof(GPostEventSource))); + postEventSource->serialNumber = 1; + postEventSource->d = this; + g_source_set_can_recurse(&postEventSource->source, true); + g_source_attach(&postEventSource->source, mainContext); + + // setup socketNotifierSource + socketNotifierSource = + reinterpret_cast(g_source_new(&socketNotifierSourceFuncs, + sizeof(GSocketNotifierSource))); + (void) new (&socketNotifierSource->pollfds) QList(); + g_source_set_can_recurse(&socketNotifierSource->source, true); + g_source_attach(&socketNotifierSource->source, mainContext); + + // setup normal and idle timer sources + timerSource = reinterpret_cast(g_source_new(&timerSourceFuncs, + sizeof(GTimerSource))); + (void) new (&timerSource->timerList) QTimerInfoList(); + timerSource->processEventsFlags = QEventLoop::AllEvents; + timerSource->runWithIdlePriority = false; + g_source_set_can_recurse(&timerSource->source, true); + g_source_attach(&timerSource->source, mainContext); + + idleTimerSource = reinterpret_cast(g_source_new(&idleTimerSourceFuncs, + sizeof(GIdleTimerSource))); + idleTimerSource->timerSource = timerSource; + g_source_set_can_recurse(&idleTimerSource->source, true); + g_source_set_priority(&idleTimerSource->source, G_PRIORITY_DEFAULT_IDLE); + g_source_attach(&idleTimerSource->source, mainContext); +} + +void QEventDispatcherGlibPrivate::runTimersOnceWithNormalPriority() +{ + timerSource->runWithIdlePriority = false; +} + +QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent) + : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent) +{ +} + +QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent) + : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate(mainContext)), parent) +{ } + +QEventDispatcherGlib::~QEventDispatcherGlib() +{ + Q_D(QEventDispatcherGlib); + + // destroy all timer sources + qDeleteAll(d->timerSource->timerList); + d->timerSource->timerList.~QTimerInfoList(); + g_source_destroy(&d->timerSource->source); + g_source_unref(&d->timerSource->source); + d->timerSource = 0; + g_source_destroy(&d->idleTimerSource->source); + g_source_unref(&d->idleTimerSource->source); + d->idleTimerSource = 0; + + // destroy socket notifier source + for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) { + GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds[i]; + g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd); + delete p; + } + d->socketNotifierSource->pollfds.~QList(); + g_source_destroy(&d->socketNotifierSource->source); + g_source_unref(&d->socketNotifierSource->source); + d->socketNotifierSource = 0; + + // destroy post event source + g_source_destroy(&d->postEventSource->source); + g_source_unref(&d->postEventSource->source); + d->postEventSource = 0; + + Q_ASSERT(d->mainContext != 0); +#if GLIB_CHECK_VERSION (2, 22, 0) + g_main_context_pop_thread_default (d->mainContext); +#endif + g_main_context_unref(d->mainContext); + d->mainContext = 0; +} + +bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_D(QEventDispatcherGlib); + + const bool canWait = (flags & QEventLoop::WaitForMoreEvents); + if (canWait) + emit aboutToBlock(); + else + emit awake(); + + // tell postEventSourcePrepare() and timerSource about any new flags + QEventLoop::ProcessEventsFlags savedFlags = d->timerSource->processEventsFlags; + d->timerSource->processEventsFlags = flags; + + if (!(flags & QEventLoop::EventLoopExec)) { + // force timers to be sent at normal priority + d->timerSource->runWithIdlePriority = false; + } + + bool result = g_main_context_iteration(d->mainContext, canWait); + while (!result && canWait) + result = g_main_context_iteration(d->mainContext, canWait); + + d->timerSource->processEventsFlags = savedFlags; + + if (canWait) + emit awake(); + + return result; +} + +bool QEventDispatcherGlib::hasPendingEvents() +{ + Q_D(QEventDispatcherGlib); + return g_main_context_pending(d->mainContext); +} + +void QEventDispatcherGlib::registerSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int sockfd = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (sockfd < 0) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() + || thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherGlib); + + + GPollFDWithQSocketNotifier *p = new GPollFDWithQSocketNotifier; + p->pollfd.fd = sockfd; + switch (type) { + case QSocketNotifier::Read: + p->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR; + break; + case QSocketNotifier::Write: + p->pollfd.events = G_IO_OUT | G_IO_ERR; + break; + case QSocketNotifier::Exception: + p->pollfd.events = G_IO_PRI | G_IO_ERR; + break; + } + p->socketNotifier = notifier; + + d->socketNotifierSource->pollfds.append(p); + + g_source_add_poll(&d->socketNotifierSource->source, &p->pollfd); +} + +void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); +#ifndef QT_NO_DEBUG + int sockfd = notifier->socket(); + if (sockfd < 0) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() + || thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherGlib); + + for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) { + GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i); + if (p->socketNotifier == notifier) { + // found it + g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd); + + d->socketNotifierSource->pollfds.removeAt(i); + delete p; + + return; + } + } +} + +void QEventDispatcherGlib::registerTimer(int timerId, int interval, QObject *object) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1 || interval < 0 || !object) { + qWarning("QEventDispatcherGlib::registerTimer: invalid arguments"); + return; + } else if (object->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::startTimer: timers cannot be started from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherGlib); + d->timerSource->timerList.registerTimer(timerId, interval, object); +} + +bool QEventDispatcherGlib::unregisterTimer(int timerId) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1) { + qWarning("QEventDispatcherGlib::unregisterTimer: invalid argument"); + return false; + } else if (thread() != QThread::currentThread()) { + qWarning("QObject::killTimer: timers cannot be stopped from another thread"); + return false; + } +#endif + + Q_D(QEventDispatcherGlib); + return d->timerSource->timerList.unregisterTimer(timerId); +} + +bool QEventDispatcherGlib::unregisterTimers(QObject *object) +{ +#ifndef QT_NO_DEBUG + if (!object) { + qWarning("QEventDispatcherGlib::unregisterTimers: invalid argument"); + return false; + } else if (object->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::killTimers: timers cannot be stopped from another thread"); + return false; + } +#endif + + Q_D(QEventDispatcherGlib); + return d->timerSource->timerList.unregisterTimers(object); +} + +QList QEventDispatcherGlib::registeredTimers(QObject *object) const +{ + if (!object) { + qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument"); + return QList(); + } + + Q_D(const QEventDispatcherGlib); + return d->timerSource->timerList.registeredTimers(object); +} + +void QEventDispatcherGlib::interrupt() +{ + wakeUp(); +} + +void QEventDispatcherGlib::wakeUp() +{ + Q_D(QEventDispatcherGlib); + d->postEventSource->serialNumber.ref(); + g_main_context_wakeup(d->mainContext); +} + +void QEventDispatcherGlib::flush() +{ +} + +bool QEventDispatcherGlib::versionSupported() +{ +#if !defined(GLIB_MAJOR_VERSION) || !defined(GLIB_MINOR_VERSION) || !defined(GLIB_MICRO_VERSION) + return false; +#else + return ((GLIB_MAJOR_VERSION << 16) + (GLIB_MINOR_VERSION << 8) + GLIB_MICRO_VERSION) >= 0x020301; +#endif +} + +QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent) + : QAbstractEventDispatcher(dd, parent) +{ +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qeventdispatcher_glib_p.h b/src/corelib/kernel/qeventdispatcher_glib_p.h new file mode 100644 index 0000000000..65eff72fca --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_glib_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QEVENTDISPATCHER_GLIB_P_H +#define QEVENTDISPATCHER_GLIB_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qabstracteventdispatcher.h" +#include "qabstracteventdispatcher_p.h" + +#include + +typedef struct _GMainContext GMainContext; + +QT_BEGIN_NAMESPACE + +class QEventDispatcherGlibPrivate; + +class Q_CORE_EXPORT QEventDispatcherGlib : public QAbstractEventDispatcher +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QEventDispatcherGlib) + +public: + explicit QEventDispatcherGlib(QObject *parent = 0); + explicit QEventDispatcherGlib(GMainContext *context, QObject *parent = 0); + ~QEventDispatcherGlib(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + bool hasPendingEvents(); + + void registerSocketNotifier(QSocketNotifier *socketNotifier); + void unregisterSocketNotifier(QSocketNotifier *socketNotifier); + + void registerTimer(int timerId, int interval, QObject *object); + bool unregisterTimer(int timerId); + bool unregisterTimers(QObject *object); + QList registeredTimers(QObject *object) const; + + void wakeUp(); + void interrupt(); + void flush(); + + static bool versionSupported(); + +protected: + QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent); +}; + +struct GPostEventSource; +struct GSocketNotifierSource; +struct GTimerSource; +struct GIdleTimerSource; + +class Q_CORE_EXPORT QEventDispatcherGlibPrivate : public QAbstractEventDispatcherPrivate +{ + +public: + QEventDispatcherGlibPrivate(GMainContext *context = 0); + GMainContext *mainContext; + GPostEventSource *postEventSource; + GSocketNotifierSource *socketNotifierSource; + GTimerSource *timerSource; + GIdleTimerSource *idleTimerSource; + + void runTimersOnceWithNormalPriority(); +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_GLIB_P_H diff --git a/src/corelib/kernel/qeventdispatcher_symbian.cpp b/src/corelib/kernel/qeventdispatcher_symbian.cpp new file mode 100644 index 0000000000..e0eeb0819a --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_symbian.cpp @@ -0,0 +1,1310 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qeventdispatcher_symbian_p.h" +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +#ifdef SYMBIAN_GRAPHICS_WSERV_QT_EFFECTS +// when the system UI is Qt based, priority drop is not needed as CPU starved processes will not be killed. +#undef QT_SYMBIAN_PRIORITY_DROP +#else +#define QT_SYMBIAN_PRIORITY_DROP +#endif + +#define WAKE_UP_PRIORITY CActive::EPriorityStandard +#define TIMER_PRIORITY CActive::EPriorityHigh +#define NULLTIMER_PRIORITY CActive::EPriorityLow +#define COMPLETE_DEFERRED_ACTIVE_OBJECTS_PRIORITY CActive::EPriorityIdle + +static inline int qt_pipe_write(int socket, const char *data, qint64 len) +{ + return ::write(socket, data, len); +} +#if defined(write) +# undef write +#endif + +static inline int qt_pipe_close(int socket) +{ + return ::close(socket); +} +#if defined(close) +# undef close +#endif + +static inline int qt_pipe_fcntl(int socket, int command) +{ + return ::fcntl(socket, command); +} +static inline int qt_pipe2_fcntl(int socket, int command, int option) +{ + return ::fcntl(socket, command, option); +} +#if defined(fcntl) +# undef fcntl +#endif + +static inline int qt_socket_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ + return ::select(nfds, readfds, writefds, exceptfds, timeout); +} + +// This simply interrupts the select and locks the mutex until destroyed. +class QSelectMutexGrabber +{ +public: + QSelectMutexGrabber(int writeFd, int readFd, QMutex *mutex) + : m_mutex(mutex) + { + if (m_mutex->tryLock()) + return; + + char dummy = 0; + qt_pipe_write(writeFd, &dummy, 1); + + m_mutex->lock(); + + char buffer; + while (::read(readFd, &buffer, 1) > 0) {} + } + + ~QSelectMutexGrabber() + { + m_mutex->unlock(); + } + +private: + QMutex *m_mutex; +}; + +/* + * This class is designed to aid in implementing event handling in a more round robin fashion. We + * cannot change active objects that we do not own, but the active objects that Qt owns will use + * this as a base class with convenience functions. + * + * Here is how it works: On every RunL, the deriving class should call maybeQueueForLater(). + * This will return whether the active object has been queued, or whether it should run immediately. + * Queued objects will run again after other events have been processed. + * + * The QCompleteDeferredAOs class is a special object that runs after all others, which will + * reactivate the objects that were previously not run. + */ +QActiveObject::QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher) + : CActive(priority), + m_dispatcher(dispatcher), + m_hasAlreadyRun(false), + m_hasRunAgain(false), + m_iterationCount(1) +{ +} + +QActiveObject::~QActiveObject() +{ + if (m_hasRunAgain) + m_dispatcher->removeDeferredActiveObject(this); +} + +bool QActiveObject::maybeQueueForLater() +{ + Q_ASSERT(!m_hasRunAgain); + + if (!m_hasAlreadyRun || m_dispatcher->iterationCount() != m_iterationCount) { + // First occurrence of this event in this iteration. + m_hasAlreadyRun = true; + m_iterationCount = m_dispatcher->iterationCount(); + return false; + } else { + // The event has already occurred. + m_dispatcher->addDeferredActiveObject(this); + m_hasRunAgain = true; + return true; + } +} + +bool QActiveObject::maybeDeferSocketEvent() +{ + Q_ASSERT(!m_hasRunAgain); + Q_ASSERT(m_dispatcher); + if (!m_dispatcher->areSocketEventsBlocked()) { + return false; + } + m_hasRunAgain = true; + m_dispatcher->addDeferredSocketActiveObject(this); + return true; +} + +void QActiveObject::reactivateAndComplete() +{ + TInt error = iStatus.Int(); + iStatus = KRequestPending; + SetActive(); + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, error); + + m_hasRunAgain = false; + m_hasAlreadyRun = false; +} + +QWakeUpActiveObject::QWakeUpActiveObject(QEventDispatcherSymbian *dispatcher) + : QActiveObject(WAKE_UP_PRIORITY, dispatcher) +{ + m_hostThreadId = RThread().Id(); + CActiveScheduler::Add(this); + iStatus = KRequestPending; + SetActive(); +} + +QWakeUpActiveObject::~QWakeUpActiveObject() +{ + Cancel(); +} + +void QWakeUpActiveObject::DoCancel() +{ + if (iStatus.Int() == KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } else if (IsActive() && m_hostThreadId != RThread().Id()) { + // This is being cancelled in the adopted monitor thread, which can happen if an adopted thread with + // an event loop has exited. The event loop creates an event dispatcher with this active object, which may be complete but not run on exit. + // We force a cancellation in this thread, because a) the object cannot be deleted while active and b) without a cancellation + // the thread semaphore will be one count down. + // It is possible for this problem to affect other active objects. They symptom would be that finished signals + // from adopted threads are not sent, or they arrive much later than they should. + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + } +} + +void QWakeUpActiveObject::RunL() +{ + if (maybeQueueForLater()) + return; + + iStatus = KRequestPending; + SetActive(); + QT_TRYCATCH_LEAVING(m_dispatcher->wakeUpWasCalled()); +} + +QTimerActiveObject::QTimerActiveObject(QEventDispatcherSymbian *dispatcher, SymbianTimerInfo *timerInfo) + : QActiveObject((timerInfo->interval) ? TIMER_PRIORITY : NULLTIMER_PRIORITY , dispatcher), + m_timerInfo(timerInfo), m_expectedTimeSinceLastEvent(0) +{ + // start the timeout timer to ensure initialisation + m_timeoutTimer.start(); +} + +QTimerActiveObject::~QTimerActiveObject() +{ + Cancel(); + m_rTimer.Close(); //close of null handle is safe +} + +void QTimerActiveObject::DoCancel() +{ + if (m_timerInfo->interval > 0) { + m_rTimer.Cancel(); + } else { + if (iStatus.Int() == KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } + } +} + +void QTimerActiveObject::RunL() +{ + int error = KErrNone; + if (iStatus == KErrNone) { + QT_TRYCATCH_ERROR(error, Run()); + } else { + error = iStatus.Int(); + } + // All Symbian error codes are negative. + if (error < 0) { + CActiveScheduler::Current()->Error(error); // stop and report here, as this timer will be deleted on scope exit + } +} + +#define MAX_SYMBIAN_TIMEOUT_MS 2000000 +void QTimerActiveObject::StartTimer() +{ + if (m_timerInfo->msLeft > MAX_SYMBIAN_TIMEOUT_MS) { + //There is loss of accuracy anyway due to needing to restart the timer every 33 minutes, + //so the 1/64s res of After() is acceptable for these very long timers. + m_rTimer.After(iStatus, MAX_SYMBIAN_TIMEOUT_MS * 1000); + m_timerInfo->msLeft -= MAX_SYMBIAN_TIMEOUT_MS; + } else { + // this algorithm implements drift correction for repeating timers + // calculate how late we are for this event + int timeSinceLastEvent = m_timeoutTimer.restart(); + int overshoot = timeSinceLastEvent - m_expectedTimeSinceLastEvent; + if (overshoot > m_timerInfo->msLeft) { + // we skipped a whole timeout, restart from here + overshoot = 0; + } + // calculate when the next event should happen + int waitTime = m_timerInfo->msLeft - overshoot; + m_expectedTimeSinceLastEvent = waitTime; + // limit the actual ms wait time to avoid wild corrections + // this will cause the real event time to slowly drift back to the expected event time + // measurements show that Symbian timers always fire 1 or 2 ms late + const int limit = 4; + waitTime = qMax(m_timerInfo->msLeft - limit, waitTime); + m_rTimer.HighRes(iStatus, waitTime * 1000); + m_timerInfo->msLeft = 0; + } + SetActive(); +} + +void QTimerActiveObject::Run() +{ + //restart timer immediately, if the timeout has been split because it overflows max for platform. + if (m_timerInfo->msLeft > 0) { + StartTimer(); + return; + } + + if (maybeQueueForLater()) + return; + + if (m_timerInfo->interval > 0) { + // Start a new timer immediately so that we don't lose time. + m_timerInfo->msLeft = m_timerInfo->interval; + StartTimer(); + + m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId); + } else { + // However, we only complete zero timers after the event has finished, + // in order to prevent busy looping when doing nested loops. + + // Keep the refpointer around in order to avoid deletion until the end of this function. + SymbianTimerInfoPtr timerInfoPtr(m_timerInfo); + + m_timerInfo->dispatcher->timerFired(m_timerInfo->timerId); + + iStatus = KRequestPending; + SetActive(); + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } +} + +void QTimerActiveObject::Start() +{ + CActiveScheduler::Add(this); + m_timerInfo->msLeft = m_timerInfo->interval; + if (m_timerInfo->interval > 0) { + if (!m_rTimer.Handle()) { + qt_symbian_throwIfError(m_rTimer.CreateLocal()); + } + m_timeoutTimer.start(); + m_expectedTimeSinceLastEvent = 0; + StartTimer(); + } else { + iStatus = KRequestPending; + SetActive(); + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } +} + +SymbianTimerInfo::SymbianTimerInfo() + : timerAO(0) +{ +} + +SymbianTimerInfo::~SymbianTimerInfo() +{ + delete timerAO; +} + +QCompleteDeferredAOs::QCompleteDeferredAOs(QEventDispatcherSymbian *dispatcher) + : CActive(COMPLETE_DEFERRED_ACTIVE_OBJECTS_PRIORITY), + m_dispatcher(dispatcher) +{ + CActiveScheduler::Add(this); + iStatus = KRequestPending; + SetActive(); +} + +QCompleteDeferredAOs::~QCompleteDeferredAOs() +{ + Cancel(); +} + +void QCompleteDeferredAOs::complete() +{ + if (iStatus.Int() == KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } +} + +void QCompleteDeferredAOs::DoCancel() +{ + if (iStatus.Int() == KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } +} + +void QCompleteDeferredAOs::RunL() +{ + iStatus = KRequestPending; + SetActive(); + + QT_TRYCATCH_LEAVING(m_dispatcher->reactivateDeferredActiveObjects()); +} + +QSelectThread::QSelectThread() + : m_quit(false) +{ + if (::pipe(m_pipeEnds) != 0) { + qWarning("Select thread was unable to open a pipe, errno: %i", errno); + } else { + int flags0 = qt_pipe_fcntl(m_pipeEnds[0], F_GETFL); + int flags1 = qt_pipe_fcntl(m_pipeEnds[1], F_GETFL); + // We should check the error code here, but Open C has a bug that returns + // failure even though the operation was successful. + qt_pipe2_fcntl(m_pipeEnds[0], F_SETFL, flags0 | O_NONBLOCK); + qt_pipe2_fcntl(m_pipeEnds[1], F_SETFL, flags1 | O_NONBLOCK); + } +} + +QSelectThread::~QSelectThread() +{ + qt_pipe_close(m_pipeEnds[1]); + qt_pipe_close(m_pipeEnds[0]); +} + +void QSelectThread::run() +{ + Q_D(QThread); + + m_mutex.lock(); + + while (!m_quit) { + fd_set readfds; + fd_set writefds; + fd_set exceptionfds; + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptionfds); + + int maxfd = 0; + maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Read, &readfds)); + maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Write, &writefds)); + maxfd = qMax(maxfd, updateSocketSet(QSocketNotifier::Exception, &exceptionfds)); + maxfd = qMax(maxfd, m_pipeEnds[0]); + maxfd++; + + FD_SET(m_pipeEnds[0], &readfds); + + int ret; + int savedSelectErrno; + ret = qt_socket_select(maxfd, &readfds, &writefds, &exceptionfds, 0); + savedSelectErrno = errno; + + if(ret == 0) { + // do nothing + } else if (ret < 0) { + switch (savedSelectErrno) { + case EBADF: + case EINVAL: + case ENOMEM: + case EFAULT: + qWarning("::select() returned an error: %i", savedSelectErrno); + break; + case ECONNREFUSED: + case EPIPE: + qWarning("::select() returned an error: %i (go through sockets)", savedSelectErrno); + // prepare to go through all sockets + // mark in fd sets both: + // good ones + // ones that return -1 in select + // after loop update notifiers for all of them + + // as we don't have "exception" notifier type + // we should force monitoring fd_set of this + // type as well + + // clean @ start + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptionfds); + for (QHash::const_iterator i = m_AOStatuses.begin(); + i != m_AOStatuses.end(); ++i) { + + fd_set onefds; + FD_ZERO(&onefds); + FD_SET(i.key()->socket(), &onefds); + + fd_set excfds; + FD_ZERO(&excfds); + FD_SET(i.key()->socket(), &excfds); + + maxfd = i.key()->socket() + 1; + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + ret = 0; + + if(i.key()->type() == QSocketNotifier::Read) { + ret = ::select(maxfd, &onefds, 0, &excfds, &timeout); + if(ret != 0) FD_SET(i.key()->socket(), &readfds); + } else if(i.key()->type() == QSocketNotifier::Write) { + ret = ::select(maxfd, 0, &onefds, &excfds, &timeout); + if(ret != 0) FD_SET(i.key()->socket(), &writefds); + } + + } // end for + + // traversed all, so update + updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds); + updateActivatedNotifiers(QSocketNotifier::Read, &readfds); + updateActivatedNotifiers(QSocketNotifier::Write, &writefds); + + break; + case EINTR: // Should never occur on Symbian, but this is future proof! + default: + qWarning("::select() returned an unknown error: %i", savedSelectErrno); + + break; + } + } else { + updateActivatedNotifiers(QSocketNotifier::Exception, &exceptionfds); + updateActivatedNotifiers(QSocketNotifier::Read, &readfds); + updateActivatedNotifiers(QSocketNotifier::Write, &writefds); + } + + if (FD_ISSET(m_pipeEnds[0], &readfds)) + m_waitCond.wait(&m_mutex); + } + + m_mutex.unlock(); +} + +void QSelectThread::requestSocketEvents ( QSocketNotifier *notifier, TRequestStatus *status ) +{ + Q_D(QThread); + + if (!isRunning()) { + start(); + } + + Q_ASSERT(QThread::currentThread() == this->thread()); + + QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex); + + Q_ASSERT(!m_AOStatuses.contains(notifier)); + + m_AOStatuses.insert(notifier, status); + + m_waitCond.wakeAll(); +} + +void QSelectThread::cancelSocketEvents ( QSocketNotifier *notifier ) +{ + Q_ASSERT(QThread::currentThread() == this->thread()); + + QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex); + + m_AOStatuses.remove(notifier); + + m_waitCond.wakeAll(); +} + +void QSelectThread::restart() +{ + Q_ASSERT(QThread::currentThread() == this->thread()); + + QSelectMutexGrabber lock(m_pipeEnds[1], m_pipeEnds[0], &m_mutex); + + m_waitCond.wakeAll(); +} + +int QSelectThread::updateSocketSet(QSocketNotifier::Type type, fd_set *fds) +{ + int maxfd = 0; + if(m_AOStatuses.isEmpty()) { + /* + * Wonder if should return -1 + * to signal that no descriptors + * added to fds + */ + return maxfd; + } + for ( QHash::const_iterator i = m_AOStatuses.begin(); + i != m_AOStatuses.end(); ++i) { + if (i.key()->type() == type) { + FD_SET(i.key()->socket(), fds); + maxfd = qMax(maxfd, i.key()->socket()); + } else if(type == QSocketNotifier::Exception) { + /* + * We are registering existing sockets + * always to exception set + * + * Doing double FD_SET shouldn't + * matter + */ + FD_SET(i.key()->socket(), fds); + maxfd = qMax(maxfd, i.key()->socket()); + } + } + + return maxfd; +} + +void QSelectThread::updateActivatedNotifiers(QSocketNotifier::Type type, fd_set *fds) +{ + Q_D(QThread); + if(m_AOStatuses.isEmpty()) { + return; + } + QList toRemove; + for (QHash::const_iterator i = m_AOStatuses.begin(); + i != m_AOStatuses.end(); ++i) { + if (i.key()->type() == type && FD_ISSET(i.key()->socket(), fds)) { + toRemove.append(i.key()); + TRequestStatus *status = i.value(); + // Thread data is still owned by the main thread. + QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone); + } else if(type == QSocketNotifier::Exception && FD_ISSET(i.key()->socket(), fds)) { + /* + * check if socket is in exception set + * then signal RequestComplete for it + */ + qWarning("exception on %d [will close the socket handle - hack]", i.key()->socket()); + // quick fix; there is a bug + // when doing read on socket + // errors not preoperly mapped + // after offline-ing the device + // on some devices we do get exception + ::close(i.key()->socket()); + toRemove.append(i.key()); + TRequestStatus *status = i.value(); + QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone); + } + } + + for (int c = 0; c < toRemove.size(); ++c) { + m_AOStatuses.remove(toRemove[c]); + } +} + +void QSelectThread::stop() +{ + m_quit = true; + restart(); + wait(); +} + +QSocketActiveObject::QSocketActiveObject(QEventDispatcherSymbian *dispatcher, QSocketNotifier *notifier) + : QActiveObject(CActive::EPriorityStandard, dispatcher), + m_notifier(notifier), + m_inSocketEvent(false), + m_deleteLater(false) +{ + CActiveScheduler::Add(this); + iStatus = KRequestPending; + SetActive(); +} + +QSocketActiveObject::~QSocketActiveObject() +{ + Cancel(); +} + +void QSocketActiveObject::DoCancel() +{ + if (iStatus.Int() == KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, KErrNone); + } +} + +void QSocketActiveObject::RunL() +{ + if (maybeDeferSocketEvent()) + return; + if (maybeQueueForLater()) + return; + + QT_TRYCATCH_LEAVING(run()); +} + +void QSocketActiveObject::run() +{ + QEvent e(QEvent::SockAct); + m_inSocketEvent = true; + QCoreApplication::sendEvent(m_notifier, &e); + m_inSocketEvent = false; + + if (m_deleteLater) { + delete this; + } else { + iStatus = KRequestPending; + SetActive(); + m_dispatcher->reactivateSocketNotifier(m_notifier); + } +} + +void QSocketActiveObject::deleteLater() +{ + if (m_inSocketEvent) { + m_deleteLater = true; + } else { + delete this; + } +} + +#ifdef QT_SYMBIAN_PRIORITY_DROP +class QIdleDetectorThread +{ +public: + QIdleDetectorThread() + : m_state(STATE_RUN), m_stop(false), m_running(false) + { + start(); + } + + ~QIdleDetectorThread() + { + stop(); + } + + void start() + { + QMutexLocker lock(&m_mutex); + if (m_running) + return; + m_stop = false; + m_state = STATE_RUN; + TInt err = m_idleDetectorThread.Create(KNullDesC(), &idleDetectorThreadFunc, 1024, &User::Allocator(), this); + if (err != KErrNone) + return; // Fail silently on error. Next kick will try again. Exception might stop the event being processed + m_idleDetectorThread.SetPriority(EPriorityAbsoluteBackgroundNormal); + m_idleDetectorThread.Resume(); + m_running = true; + // get a callback from QCoreApplication destruction to stop this thread + qAddPostRoutine(StopIdleDetectorThread); + } + + void stop() + { + QMutexLocker lock(&m_mutex); + if (!m_running) + return; + // close down the idle thread because if corelib is loaded temporarily, this would leak threads into the host process + m_stop = true; + m_kick.release(); + m_idleDetectorThread.SetPriority(EPriorityNormal); + TRequestStatus s; + m_idleDetectorThread.Logon(s); + User::WaitForRequest(s); + m_idleDetectorThread.Close(); + m_running = false; + } + + void kick() + { + start(); + m_state = STATE_KICKED; + m_kick.release(); + } + + bool hasRun() + { + return m_state == STATE_RUN; + } + +private: + static TInt idleDetectorThreadFunc(TAny* self) + { + User::RenameThread(_L("IdleDetectorThread")); + static_cast(self)->IdleLoop(); + return KErrNone; + } + + void IdleLoop() + { + while (!m_stop) { + m_kick.acquire(); + m_state = STATE_RUN; + } + } + + static void StopIdleDetectorThread(); + +private: + enum IdleStates {STATE_KICKED, STATE_RUN} m_state; + bool m_stop; + bool m_running; + RThread m_idleDetectorThread; + QSemaphore m_kick; + QMutex m_mutex; +}; + +Q_GLOBAL_STATIC(QIdleDetectorThread, idleDetectorThread); + +void QIdleDetectorThread::StopIdleDetectorThread() +{ + idleDetectorThread()->stop(); +} + +const int maxBusyTime = 2000; // maximum time we allow idle detector to be blocked before worrying, in milliseconds +const int baseDelay = 1000; // minimum delay time used when backing off to allow idling, in microseconds +#endif + +QEventDispatcherSymbian::QEventDispatcherSymbian(QObject *parent) + : QAbstractEventDispatcher(parent), + m_selectThread(0), + m_activeScheduler(0), + m_wakeUpAO(0), + m_completeDeferredAOs(0), + m_interrupt(false), + m_wakeUpDone(0), + m_iterationCount(0), + m_insideTimerEvent(false), + m_noSocketEvents(false) +{ +#ifdef QT_SYMBIAN_PRIORITY_DROP + m_delay = baseDelay; + m_avgEventTime = 0; + idleDetectorThread(); +#endif +} + +QEventDispatcherSymbian::~QEventDispatcherSymbian() +{ +} + +void QEventDispatcherSymbian::startingUp() +{ + if( !CActiveScheduler::Current() ) { + m_activeScheduler = q_check_ptr(new CQtActiveScheduler()); // CBase derived class needs to be checked on new + CActiveScheduler::Install(m_activeScheduler); + } + m_wakeUpAO = q_check_ptr(new QWakeUpActiveObject(this)); + m_completeDeferredAOs = q_check_ptr(new QCompleteDeferredAOs(this)); + // We already might have posted events, wakeup once to process them + wakeUp(); +} + +QSelectThread& QEventDispatcherSymbian::selectThread() { + if (!m_selectThread) + m_selectThread = new QSelectThread; + return *m_selectThread; +} + +void QEventDispatcherSymbian::closingDown() +{ + if (m_selectThread && m_selectThread->isRunning()) { + m_selectThread->stop(); + } + delete m_selectThread; + m_selectThread = 0; + + delete m_completeDeferredAOs; + delete m_wakeUpAO; + if (m_activeScheduler) { + delete m_activeScheduler; + } +} + +bool QEventDispatcherSymbian::processEvents ( QEventLoop::ProcessEventsFlags flags ) +{ + bool handledAnyEvent = false; + bool oldNoSocketEventsValue = m_noSocketEvents; + bool oldInsideTimerEventValue = m_insideTimerEvent; + + m_insideTimerEvent = false; + + QT_TRY { + Q_D(QAbstractEventDispatcher); + + // It is safe if this counter overflows. The main importance is that each + // iteration count is different from the last. + m_iterationCount++; + + RThread &thread = d->threadData->symbian_thread_handle; + + bool block; + if (flags & QEventLoop::WaitForMoreEvents) { + block = true; + emit aboutToBlock(); + } else { + block = false; + } + + if (flags & QEventLoop::ExcludeSocketNotifiers) { + m_noSocketEvents = true; + } else { + m_noSocketEvents = false; + handledAnyEvent = sendDeferredSocketEvents(); + } + + bool handledSymbianEvent = false; + m_interrupt = false; + +#ifdef QT_SYMBIAN_PRIORITY_DROP + QElapsedTimer eventTimer; +#endif + + while (1) { + if (block) { + // This is where Qt will spend most of its time. + CActiveScheduler::Current()->WaitForAnyRequest(); + } else { + if (thread.RequestCount() == 0) { +#ifdef QT_SYMBIAN_PRIORITY_DROP + if (idleDetectorThread()->hasRun()) { + m_lastIdleRequestTimer.start(); + idleDetectorThread()->kick(); + } else if (m_lastIdleRequestTimer.elapsed() > maxBusyTime) { + User::AfterHighRes(m_delay); + } +#endif + break; + } + // This one should return without delay. + CActiveScheduler::Current()->WaitForAnyRequest(); + } + +#ifdef QT_SYMBIAN_PRIORITY_DROP + if (idleDetectorThread()->hasRun()) { + if (m_delay > baseDelay) + m_delay -= baseDelay; + m_lastIdleRequestTimer.start(); + idleDetectorThread()->kick(); + } else if (m_lastIdleRequestTimer.elapsed() > maxBusyTime) { + User::AfterHighRes(m_delay); + // allow delay to be up to 1/4 of execution time + if (!idleDetectorThread()->hasRun() && m_delay*3 < m_avgEventTime) + m_delay += baseDelay; + } + eventTimer.start(); +#endif + + TInt error; + handledSymbianEvent = CActiveScheduler::RunIfReady(error, KMinTInt); + if (error) { + qWarning("CActiveScheduler::RunIfReady() returned error: %i\n", error); + CActiveScheduler::Current()->Error(error); + } + +#ifdef QT_SYMBIAN_PRIORITY_DROP + int eventDur = eventTimer.elapsed()*1000; + // average is calcualted as a 5% decaying exponential average + m_avgEventTime = (m_avgEventTime * 95 + eventDur * 5) / 100; +#endif + + if (!handledSymbianEvent) { + qFatal("QEventDispatcherSymbian::processEvents(): Caught Symbian stray signal"); + } + handledAnyEvent = true; + if (m_interrupt) { + break; + } + block = false; + }; + + emit awake(); + } QT_CATCH (const std::exception& ex) { +#ifndef QT_NO_EXCEPTIONS + CActiveScheduler::Current()->Error(qt_symbian_exception2Error(ex)); +#endif + } + + m_noSocketEvents = oldNoSocketEventsValue; + m_insideTimerEvent = oldInsideTimerEventValue; + + return handledAnyEvent; +} + +void QEventDispatcherSymbian::timerFired(int timerId) +{ + QHash::iterator i = m_timerList.find(timerId); + if (i == m_timerList.end()) { + // The timer has been deleted. Ignore this event. + return; + } + + SymbianTimerInfoPtr timerInfo = *i; + + // Prevent infinite timer recursion. + if (timerInfo->inTimerEvent) { + return; + } + + timerInfo->inTimerEvent = true; + bool oldInsideTimerEventValue = m_insideTimerEvent; + m_insideTimerEvent = true; + + QTimerEvent event(timerInfo->timerId); + QCoreApplication::sendEvent(timerInfo->receiver, &event); + + m_insideTimerEvent = oldInsideTimerEventValue; + timerInfo->inTimerEvent = false; + + return; +} + +void QEventDispatcherSymbian::wakeUpWasCalled() +{ + // The reactivation should happen in RunL, right before the call to this function. + // This is because m_wakeUpDone is the "signal" that the object can be completed + // once more. + // Also, by dispatching the posted events after resetting m_wakeUpDone, we guarantee + // that no posted event notification will be lost. If we did it the other way + // around, it would be possible for another thread to post an event right after + // the sendPostedEvents was done, but before the object was ready to be completed + // again. This could deadlock the application if there are no other posted events. + m_wakeUpDone.fetchAndStoreOrdered(0); + sendPostedEvents(); +} + +void QEventDispatcherSymbian::interrupt() +{ + m_interrupt = true; + wakeUp(); +} + +void QEventDispatcherSymbian::wakeUp() +{ + Q_D(QAbstractEventDispatcher); + + if (m_wakeUpAO && m_wakeUpDone.testAndSetAcquire(0, 1)) { + TRequestStatus *status = &m_wakeUpAO->iStatus; + QEventDispatcherSymbian::RequestComplete(d->threadData->symbian_thread_handle, status, KErrNone); + } +} + +bool QEventDispatcherSymbian::sendPostedEvents() +{ + Q_D(QAbstractEventDispatcher); + + // moveToThread calls this and canWait == true -> Events will never get processed + // if we check for d->threadData->canWait + // + // QCoreApplication::postEvent sets canWait = false, but after the object and events + // are moved to a new thread, the canWait in new thread is true i.e. not changed to reflect + // the flag on old thread. That's why events in a new thread will not get processed. + // This migth be actually bug in moveToThread functionality, but because other platforms + // do not check canWait in wakeUp (where we essentially are now) - decided to remove it from + // here as well. + + //if (!d->threadData->canWait) { + QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + return true; + //} + //return false; +} + +inline void QEventDispatcherSymbian::addDeferredActiveObject(QActiveObject *object) +{ + queueDeferredActiveObjectsCompletion(); + m_deferredActiveObjects.append(object); +} + +inline void QEventDispatcherSymbian::removeDeferredActiveObject(QActiveObject *object) +{ + m_deferredActiveObjects.removeAll(object); + m_deferredSocketEvents.removeAll(object); +} + +inline void QEventDispatcherSymbian::addDeferredSocketActiveObject(QActiveObject *object) +{ + m_deferredSocketEvents.append(object); +} + +void QEventDispatcherSymbian::queueDeferredActiveObjectsCompletion() +{ + m_completeDeferredAOs->complete(); +} + +void QEventDispatcherSymbian::reactivateDeferredActiveObjects() +{ + while (!m_deferredActiveObjects.isEmpty()) { + QActiveObject *object = m_deferredActiveObjects.takeFirst(); + object->reactivateAndComplete(); + } + + // We do this because we want to return from processEvents. This is because + // each invocation of processEvents should only run each active object once. + // The active scheduler should run them continously, however. + m_interrupt = true; +} + +bool QEventDispatcherSymbian::sendDeferredSocketEvents() +{ + bool sentAnyEvents = false; + while (!m_deferredSocketEvents.isEmpty()) { + sentAnyEvents = true; + QActiveObject *object = m_deferredSocketEvents.takeFirst(); + object->reactivateAndComplete(); + } + + return sentAnyEvents; +} + +void QEventDispatcherSymbian::flush() +{ +} + +bool QEventDispatcherSymbian::hasPendingEvents() +{ + Q_D(QAbstractEventDispatcher); + return (d->threadData->symbian_thread_handle.RequestCount() != 0 + || !d->threadData->canWait || !m_deferredSocketEvents.isEmpty()); +} + +void QEventDispatcherSymbian::registerSocketNotifier ( QSocketNotifier * notifier ) +{ + //note - this is only for "open C" file descriptors + //for native sockets, an active object in the symbian socket engine handles this + QSocketActiveObject *socketAO = new QSocketActiveObject(this, notifier); + Q_CHECK_PTR(socketAO); + m_notifiers.insert(notifier, socketAO); + selectThread().requestSocketEvents(notifier, &socketAO->iStatus); +} + +void QEventDispatcherSymbian::unregisterSocketNotifier ( QSocketNotifier * notifier ) +{ + //note - this is only for "open C" file descriptors + //for native sockets, an active object in the symbian socket engine handles this + if (m_selectThread) + m_selectThread->cancelSocketEvents(notifier); + if (m_notifiers.contains(notifier)) { + QSocketActiveObject *sockObj = *m_notifiers.find(notifier); + m_deferredSocketEvents.removeAll(sockObj); + sockObj->deleteLater(); + m_notifiers.remove(notifier); + } +} + +void QEventDispatcherSymbian::reactivateSocketNotifier(QSocketNotifier *notifier) +{ + selectThread().requestSocketEvents(notifier, &m_notifiers[notifier]->iStatus); +} + +void QEventDispatcherSymbian::registerTimer ( int timerId, int interval, QObject * object ) +{ + if (interval < 0) { + qWarning("Timer interval < 0"); + interval = 0; + } + + SymbianTimerInfoPtr timer(new SymbianTimerInfo); + timer->timerId = timerId; + timer->interval = interval; + timer->inTimerEvent = false; + timer->receiver = object; + timer->dispatcher = this; + timer->timerAO = q_check_ptr(new QTimerActiveObject(this, timer.data())); + m_timerList.insert(timerId, timer); + + timer->timerAO->Start(); + + if (m_insideTimerEvent) + // If we are inside a timer event, we need to prevent event starvation + // by preventing newly created timers from running in the same event processing + // iteration. Do this by calling the maybeQueueForLater() function to "fake" that we have + // already run once. This will cause the next run to be added to the deferred + // queue instead. + timer->timerAO->maybeQueueForLater(); +} + +bool QEventDispatcherSymbian::unregisterTimer ( int timerId ) +{ + if (!m_timerList.contains(timerId)) { + return false; + } + + SymbianTimerInfoPtr timerInfo = m_timerList.take(timerId); + + if (!QObjectPrivate::get(timerInfo->receiver)->inThreadChangeEvent) + QAbstractEventDispatcherPrivate::releaseTimerId(timerId); + + return true; +} + +bool QEventDispatcherSymbian::unregisterTimers ( QObject * object ) +{ + if (m_timerList.isEmpty()) + return false; + + bool unregistered = false; + for (QHash::iterator i = m_timerList.begin(); i != m_timerList.end(); ) { + if ((*i)->receiver == object) { + i = m_timerList.erase(i); + unregistered = true; + } else { + ++i; + } + } + + return unregistered; +} + +QList QEventDispatcherSymbian::registeredTimers ( QObject * object ) const +{ + QList list; + for (QHash::const_iterator i = m_timerList.begin(); i != m_timerList.end(); ++i) { + if ((*i)->receiver == object) { + list.push_back(TimerInfo((*i)->timerId, (*i)->interval)); + } + } + + return list; +} + +/* + * This active scheduler class implements a simple report and continue policy, for Symbian OS leaves + * or exceptions from Qt that fall back to the scheduler. + * It will be used in cases where there is no existing active scheduler installed. + * Apps which link to qts60main.lib will have the UI active scheduler installed in the main thread + * instead of this one. But this would be used in other threads in the UI. + * An app could replace this behaviour by installing an alternative active scheduler. + */ +void CQtActiveScheduler::Error(TInt aError) const +{ + QT_TRY { + qWarning("Error from active scheduler %d", aError); + } + QT_CATCH (const std::bad_alloc&) {} // ignore alloc fails, nothing more can be done +} + +bool QActiveObject::wait(CActive* ao, int ms) +{ + if (!ao->IsActive()) + return true; //request already complete + bool timedout = false; + if (ms > 0) { + TRequestStatus tstat; + RTimer t; + if (KErrNone != t.CreateLocal()) + return false; + t.HighRes(tstat, ms*1000); + User::WaitForRequest(tstat, ao->iStatus); + if (tstat != KRequestPending) { + timedout = true; + } else { + t.Cancel(); + //balance thread semaphore + User::WaitForRequest(tstat); + } + t.Close(); + } else { + User::WaitForRequest(ao->iStatus); + } + if (timedout) + return false; + + //evil cast to allow calling of protected virtual + ((QActiveObject*)ao)->RunL(); + + //clear active & pending flags + ao->iStatus = TRequestStatus(); + + return true; +} + +bool QActiveObject::wait(QList aos, int ms) +{ + QVector stati; + stati.reserve(aos.count() + 1); + foreach (CActive* ao, aos) { + if (!ao->IsActive()) + return true; //request already complete + stati.append(&(ao->iStatus)); + } + bool timedout = false; + TRequestStatus tstat; + RTimer t; + if (ms > 0) { + if (KErrNone != t.CreateLocal()) + return false; + t.HighRes(tstat, ms*1000); + stati.append(&tstat); + } + User::WaitForNRequest(stati.data(), stati.count()); + if (ms > 0) { + if (tstat != KRequestPending) { + timedout = true; + } else { + t.Cancel(); + //balance thread semaphore + User::WaitForRequest(tstat); + } + t.Close(); + } + if (timedout) + return false; + + foreach (CActive* ao, aos) { + if (ao->iStatus != KRequestPending) { + //evil cast to allow calling of protected virtual + ((QActiveObject*)ao)->RunL(); + + //clear active & pending flags + ao->iStatus = TRequestStatus(); + break; //only call one + } + } + return true; +} + +QT_END_NAMESPACE + +#include "moc_qeventdispatcher_symbian_p.cpp" diff --git a/src/corelib/kernel/qeventdispatcher_symbian_p.h b/src/corelib/kernel/qeventdispatcher_symbian_p.h new file mode 100644 index 0000000000..6fdd4b248d --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_symbian_p.h @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QEVENTDISPATCHER_SYMBIAN_P_H +#define QEVENTDISPATCHER_SYMBIAN_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + + +class QEventDispatcherSymbian; +class QTimerActiveObject; + +class Q_CORE_EXPORT QActiveObject : public CActive +{ +public: + QActiveObject(TInt priority, QEventDispatcherSymbian *dispatcher); + ~QActiveObject(); + + bool maybeQueueForLater(); + bool maybeDeferSocketEvent(); + + void reactivateAndComplete(); + + static bool wait(CActive* ao, int ms); + static bool wait(QList aos, int ms); +protected: + QEventDispatcherSymbian *m_dispatcher; + +private: + bool m_hasAlreadyRun : 1; + bool m_hasRunAgain : 1; + int m_iterationCount; +}; + +class QWakeUpActiveObject : public QActiveObject +{ +public: + QWakeUpActiveObject(QEventDispatcherSymbian *dispatcher); + ~QWakeUpActiveObject(); + + void Complete(); + +protected: + void DoCancel(); + void RunL(); + +private: + TThreadId m_hostThreadId; +}; + +struct SymbianTimerInfo : public QSharedData +{ + SymbianTimerInfo(); + ~SymbianTimerInfo(); + + int timerId; + int interval; + int msLeft; + bool inTimerEvent; + QObject *receiver; + QTimerActiveObject *timerAO; + QEventDispatcherSymbian *dispatcher; +}; + +typedef QExplicitlySharedDataPointer SymbianTimerInfoPtr; + +// This is a bit of a proxy class. See comments in SetActive and Start for details. +class QTimerActiveObject : public QActiveObject +{ +public: + QTimerActiveObject(QEventDispatcherSymbian *dispatcher, SymbianTimerInfo *timerInfo); + ~QTimerActiveObject(); + + void Start(); + +protected: + void DoCancel(); + void RunL(); + +private: + void Run(); + void StartTimer(); + +private: + SymbianTimerInfo *m_timerInfo; + QElapsedTimer m_timeoutTimer; + int m_expectedTimeSinceLastEvent; + RTimer m_rTimer; +}; + +class QCompleteDeferredAOs : public CActive +{ +public: + QCompleteDeferredAOs(QEventDispatcherSymbian *dispatcher); + ~QCompleteDeferredAOs(); + + void complete(); + +protected: + void DoCancel(); + void RunL(); + +private: + QEventDispatcherSymbian *m_dispatcher; +}; + +class QSocketActiveObject : public QActiveObject +{ +public: + QSocketActiveObject(QEventDispatcherSymbian *dispatcher, QSocketNotifier *notifier); + ~QSocketActiveObject(); + + void deleteLater(); + +protected: + void DoCancel(); + void RunL(); + void run(); + +private: + QSocketNotifier *m_notifier; + bool m_inSocketEvent; + bool m_deleteLater; + + friend class QEventDispatcherSymbian; +}; + +class QSelectThread : public QThread +{ + Q_DECLARE_PRIVATE(QThread) + +public: + QSelectThread(); + ~QSelectThread(); + + void requestSocketEvents ( QSocketNotifier *notifier, TRequestStatus *status ); + void cancelSocketEvents ( QSocketNotifier *notifier ); + void restart(); + void stop(); + +protected: + void run(); + +private: + int updateSocketSet(QSocketNotifier::Type type, fd_set *fds); + void updateActivatedNotifiers(QSocketNotifier::Type type, fd_set *fds); + +private: + int m_pipeEnds[2]; + QHash m_AOStatuses; + QMutex m_mutex; + QWaitCondition m_waitCond; + bool m_quit; +}; + +class Q_CORE_EXPORT CQtActiveScheduler : public CActiveScheduler +{ +public: // from CActiveScheduler + virtual void Error(TInt aError) const; +}; + +class Q_CORE_EXPORT QEventDispatcherSymbian : public QAbstractEventDispatcher +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QAbstractEventDispatcher) + +public: + QEventDispatcherSymbian(QObject *parent = 0); + ~QEventDispatcherSymbian(); + + void flush(); + bool hasPendingEvents(); + void interrupt(); + bool processEvents ( QEventLoop::ProcessEventsFlags flags ); + void registerSocketNotifier ( QSocketNotifier * notifier ); + void registerTimer ( int timerId, int interval, QObject * object ); + QList registeredTimers ( QObject * object ) const; + void unregisterSocketNotifier ( QSocketNotifier * notifier ); + bool unregisterTimer ( int timerId ); + bool unregisterTimers ( QObject * object ); + void wakeUp(); + + void startingUp(); + void closingDown(); + + void timerFired(int timerId); + void wakeUpWasCalled(); + void reactivateSocketNotifier(QSocketNotifier *notifier); + + void addDeferredActiveObject(QActiveObject *object); + void removeDeferredActiveObject(QActiveObject *object); + void queueDeferredActiveObjectsCompletion(); + // Can be overridden to activate local active objects too, but do call baseclass! + virtual void reactivateDeferredActiveObjects(); + + inline int iterationCount() const { return m_iterationCount; } + + void addDeferredSocketActiveObject(QActiveObject *object); + inline bool areSocketEventsBlocked() const { return m_noSocketEvents; } + + static void RequestComplete(TRequestStatus *&status, TInt reason); + static void RequestComplete(RThread &threadHandle, TRequestStatus *&status, TInt reason); + +private: + bool sendPostedEvents(); + bool sendDeferredSocketEvents(); + + QSelectThread& selectThread(); +private: + QSelectThread *m_selectThread; + + CQtActiveScheduler *m_activeScheduler; + + QHash m_timerList; + QHash m_notifiers; + + QWakeUpActiveObject *m_wakeUpAO; + QCompleteDeferredAOs *m_completeDeferredAOs; + + volatile bool m_interrupt; + QAtomicInt m_wakeUpDone; + + unsigned char m_iterationCount; + bool m_insideTimerEvent; + bool m_noSocketEvents; + //deferred until socket events are enabled + QList m_deferredSocketEvents; + //deferred until idle + QList m_deferredActiveObjects; + + int m_delay; + int m_avgEventTime; + QElapsedTimer m_lastIdleRequestTimer; +}; + +#ifdef QT_DEBUG +# define VERIFY_PENDING_REQUEST_STATUS \ + Q_ASSERT(status->Int() == KRequestPending); +#else +# define VERIFY_PENDING_REQUEST_STATUS +#endif + +// Convenience functions for doing some sanity checking on our own complete code. +// Unless QT_DEBUG is defined, it is exactly equivalent to the Symbian version. +inline void QEventDispatcherSymbian::RequestComplete(TRequestStatus *&status, TInt reason) +{ + VERIFY_PENDING_REQUEST_STATUS + User::RequestComplete(status, reason); +} +inline void QEventDispatcherSymbian::RequestComplete(RThread &threadHandle, TRequestStatus *&status, TInt reason) +{ + VERIFY_PENDING_REQUEST_STATUS + threadHandle.RequestComplete(status, reason); +} + +#undef VERIFY_PENDING_REQUEST_STATUS + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_SYMBIAN_P_H diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp new file mode 100644 index 0000000000..dceb51d181 --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_unix.cpp @@ -0,0 +1,979 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qcoreapplication.h" +#include "qpair.h" +#include "qsocketnotifier.h" +#include "qthread.h" +#include "qelapsedtimer.h" + +#include "qeventdispatcher_unix_p.h" +#include +#include +#include + +#include +#include +#include + +// VxWorks doesn't correctly set the _POSIX_... options +#if defined(Q_OS_VXWORKS) +# if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK <= 0) +# undef _POSIX_MONOTONIC_CLOCK +# define _POSIX_MONOTONIC_CLOCK 1 +# endif +# include +# include +#endif + +#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) || defined(QT_BOOTSTRAPPED) +# include +#endif + +QT_BEGIN_NAMESPACE + +Q_CORE_EXPORT bool qt_disable_lowpriority_timers=false; + +/***************************************************************************** + UNIX signal handling + *****************************************************************************/ + +static sig_atomic_t signal_received; +static sig_atomic_t signals_fired[NSIG]; + +static void signalHandler(int sig) +{ + signals_fired[sig] = 1; + signal_received = 1; +} + + +#if defined(Q_OS_INTEGRITY) || defined(Q_OS_VXWORKS) +static void initThreadPipeFD(int fd) +{ + int ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (ret == -1) + perror("QEventDispatcherUNIXPrivate: Unable to init thread pipe"); + + int flags = fcntl(fd, F_GETFL); + if (flags == -1) + perror("QEventDispatcherUNIXPrivate: Unable to get flags on thread pipe"); + + ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (ret == -1) + perror("QEventDispatcherUNIXPrivate: Unable to set flags on thread pipe"); +} +#endif + +QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate() +{ + extern Qt::HANDLE qt_application_thread_id; + mainThread = (QThread::currentThreadId() == qt_application_thread_id); + bool pipefail = false; + + // initialize the common parts of the event loop +#if defined(Q_OS_NACL) + // do nothing. +#elif defined(Q_OS_INTEGRITY) + // INTEGRITY doesn't like a "select" on pipes, so use socketpair instead + if (socketpair(AF_INET, SOCK_STREAM, 0, thread_pipe) == -1) { + perror("QEventDispatcherUNIXPrivate(): Unable to create socket pair"); + pipefail = true; + } else { + initThreadPipeFD(thread_pipe[0]); + initThreadPipeFD(thread_pipe[1]); + } +#elif defined(Q_OS_VXWORKS) + char name[20]; + qsnprintf(name, sizeof(name), "/pipe/qt_%08x", int(taskIdCurrent)); + + // make sure there is no pipe with this name + pipeDevDelete(name, true); + // create the pipe + if (pipeDevCreate(name, 128 /*maxMsg*/, 1 /*maxLength*/) != OK) { + perror("QEventDispatcherUNIXPrivate(): Unable to create thread pipe device"); + pipefail = true; + } else { + if ((thread_pipe[0] = open(name, O_RDWR, 0)) < 0) { + perror("QEventDispatcherUNIXPrivate(): Unable to create thread pipe"); + pipefail = true; + } else { + initThreadPipeFD(thread_pipe[0]); + thread_pipe[1] = thread_pipe[0]; + } + } +#else + if (qt_safe_pipe(thread_pipe, O_NONBLOCK) == -1) { + perror("QEventDispatcherUNIXPrivate(): Unable to create thread pipe"); + pipefail = true; + } +#endif + + if (pipefail) + qFatal("QEventDispatcherUNIXPrivate(): Can not continue without a thread pipe"); + + sn_highest = -1; + + interrupt = false; +} + +QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate() +{ +#if defined(Q_OS_NACL) + // do nothing. +#elif defined(Q_OS_VXWORKS) + close(thread_pipe[0]); + + char name[20]; + qsnprintf(name, sizeof(name), "/pipe/qt_%08x", int(taskIdCurrent)); + + pipeDevDelete(name, true); +#else + // cleanup the common parts of the event loop + close(thread_pipe[0]); + close(thread_pipe[1]); +#endif + + // cleanup timers + qDeleteAll(timerList); +} + +int QEventDispatcherUNIXPrivate::doSelect(QEventLoop::ProcessEventsFlags flags, timeval *timeout) +{ + Q_Q(QEventDispatcherUNIX); + + // needed in QEventDispatcherUNIX::select() + timerList.updateCurrentTime(); + + int nsel; + do { + if (mainThread) { + while (signal_received) { + signal_received = 0; + for (int i = 0; i < NSIG; ++i) { + if (signals_fired[i]) { + signals_fired[i] = 0; + emit QCoreApplication::instance()->unixSignal(i); + } + } + } + } + + // Process timers and socket notifiers - the common UNIX stuff + int highest = 0; + if (! (flags & QEventLoop::ExcludeSocketNotifiers) && (sn_highest >= 0)) { + // return the highest fd we can wait for input on + sn_vec[0].select_fds = sn_vec[0].enabled_fds; + sn_vec[1].select_fds = sn_vec[1].enabled_fds; + sn_vec[2].select_fds = sn_vec[2].enabled_fds; + highest = sn_highest; + } else { + FD_ZERO(&sn_vec[0].select_fds); + FD_ZERO(&sn_vec[1].select_fds); + FD_ZERO(&sn_vec[2].select_fds); + } + + FD_SET(thread_pipe[0], &sn_vec[0].select_fds); + highest = qMax(highest, thread_pipe[0]); + + nsel = q->select(highest + 1, + &sn_vec[0].select_fds, + &sn_vec[1].select_fds, + &sn_vec[2].select_fds, + timeout); + } while (nsel == -1 && (errno == EINTR || errno == EAGAIN)); + + if (nsel == -1) { + if (errno == EBADF) { + // it seems a socket notifier has a bad fd... find out + // which one it is and disable it + fd_set fdset; + timeval tm; + tm.tv_sec = tm.tv_usec = 0l; + + for (int type = 0; type < 3; ++type) { + QSockNotType::List &list = sn_vec[type].list; + if (list.size() == 0) + continue; + + for (int i = 0; i < list.size(); ++i) { + QSockNot *sn = list[i]; + + FD_ZERO(&fdset); + FD_SET(sn->fd, &fdset); + + int ret = -1; + do { + switch (type) { + case 0: // read + ret = select(sn->fd + 1, &fdset, 0, 0, &tm); + break; + case 1: // write + ret = select(sn->fd + 1, 0, &fdset, 0, &tm); + break; + case 2: // except + ret = select(sn->fd + 1, 0, 0, &fdset, &tm); + break; + } + } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); + + if (ret == -1 && errno == EBADF) { + // disable the invalid socket notifier + static const char *t[] = { "Read", "Write", "Exception" }; + qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...", + sn->fd, t[type]); + sn->obj->setEnabled(false); + } + } + } + } else { + // EINVAL... shouldn't happen, so let's complain to stderr + // and hope someone sends us a bug report + perror("select"); + } + } + + // some other thread woke us up... consume the data on the thread pipe so that + // select doesn't immediately return next time + int nevents = 0; + if (nsel > 0 && FD_ISSET(thread_pipe[0], &sn_vec[0].select_fds)) { +#if defined(Q_OS_VXWORKS) + char c[16]; + ::read(thread_pipe[0], c, sizeof(c)); + ::ioctl(thread_pipe[0], FIOFLUSH, 0); +#else + char c[16]; + while (::read(thread_pipe[0], c, sizeof(c)) > 0) + ; +#endif + if (!wakeUps.testAndSetRelease(1, 0)) { + // hopefully, this is dead code + qWarning("QEventDispatcherUNIX: internal error, wakeUps.testAndSetRelease(1, 0) failed!"); + } + ++nevents; + } + + // activate socket notifiers + if (! (flags & QEventLoop::ExcludeSocketNotifiers) && nsel > 0 && sn_highest >= 0) { + // if select says data is ready on any socket, then set the socket notifier + // to pending + for (int i=0; i<3; i++) { + QSockNotType::List &list = sn_vec[i].list; + for (int j = 0; j < list.size(); ++j) { + QSockNot *sn = list[j]; + if (FD_ISSET(sn->fd, &sn_vec[i].select_fds)) + q->setSocketNotifierPending(sn->obj); + } + } + } + return (nevents + q->activateSocketNotifiers()); +} + +/* + * Internal functions for manipulating timer data structures. The + * timerBitVec array is used for keeping track of timer identifiers. + */ + +QTimerInfoList::QTimerInfoList() +{ +#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_NACL) + if (!QElapsedTimer::isMonotonic()) { + // not using monotonic timers, initialize the timeChanged() machinery + previousTime = qt_gettime(); + + tms unused; + previousTicks = times(&unused); + + ticksPerSecond = sysconf(_SC_CLK_TCK); + msPerTick = 1000/ticksPerSecond; + } else { + // detected monotonic timers + previousTime.tv_sec = previousTime.tv_usec = 0; + previousTicks = 0; + ticksPerSecond = 0; + msPerTick = 0; + } +#endif + + firstTimerInfo = 0; +} + +timeval QTimerInfoList::updateCurrentTime() +{ + return (currentTime = qt_gettime()); +} + +#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_INTEGRITY)) || defined(QT_BOOTSTRAPPED) + +template <> +timeval qAbs(const timeval &t) +{ + timeval tmp = t; + if (tmp.tv_sec < 0) { + tmp.tv_sec = -tmp.tv_sec - 1; + tmp.tv_usec -= 1000000; + } + if (tmp.tv_sec == 0 && tmp.tv_usec < 0) { + tmp.tv_usec = -tmp.tv_usec; + } + return normalizedTimeval(tmp); +} + +/* + Returns true if the real time clock has changed by more than 10% + relative to the processor time since the last time this function was + called. This presumably means that the system time has been changed. + + If /a delta is nonzero, delta is set to our best guess at how much the system clock was changed. +*/ +bool QTimerInfoList::timeChanged(timeval *delta) +{ +#ifdef Q_OS_NACL + Q_UNUSED(delta) + return false; // Calling "times" crashes. +#endif + struct tms unused; + clock_t currentTicks = times(&unused); + + clock_t elapsedTicks = currentTicks - previousTicks; + timeval elapsedTime = currentTime - previousTime; + + timeval elapsedTimeTicks; + elapsedTimeTicks.tv_sec = elapsedTicks / ticksPerSecond; + elapsedTimeTicks.tv_usec = (((elapsedTicks * 1000) / ticksPerSecond) % 1000) * 1000; + + timeval dummy; + if (!delta) + delta = &dummy; + *delta = elapsedTime - elapsedTimeTicks; + + previousTicks = currentTicks; + previousTime = currentTime; + + // If tick drift is more than 10% off compared to realtime, we assume that the clock has + // been set. Of course, we have to allow for the tick granularity as well. + timeval tickGranularity; + tickGranularity.tv_sec = 0; + tickGranularity.tv_usec = msPerTick * 1000; + return elapsedTimeTicks < ((qAbs(*delta) - tickGranularity) * 10); +} + +void QTimerInfoList::repairTimersIfNeeded() +{ + if (QElapsedTimer::isMonotonic()) + return; + timeval delta; + if (timeChanged(&delta)) + timerRepair(delta); +} + +#else // !(_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(QT_BOOTSTRAPPED) + +void QTimerInfoList::repairTimersIfNeeded() +{ +} + +#endif + +/* + insert timer info into list +*/ +void QTimerInfoList::timerInsert(QTimerInfo *ti) +{ + int index = size(); + while (index--) { + register const QTimerInfo * const t = at(index); + if (!(ti->timeout < t->timeout)) + break; + } + insert(index+1, ti); +} + +/* + repair broken timer +*/ +void QTimerInfoList::timerRepair(const timeval &diff) +{ + // repair all timers + for (int i = 0; i < size(); ++i) { + register QTimerInfo *t = at(i); + t->timeout = t->timeout + diff; + } +} + +/* + Returns the time to wait for the next timer, or null if no timers + are waiting. +*/ +bool QTimerInfoList::timerWait(timeval &tm) +{ + timeval currentTime = updateCurrentTime(); + repairTimersIfNeeded(); + + // Find first waiting timer not already active + QTimerInfo *t = 0; + for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) { + if (!(*it)->activateRef) { + t = *it; + break; + } + } + + if (!t) + return false; + + if (currentTime < t->timeout) { + // time to wait + tm = t->timeout - currentTime; + } else { + // no time to wait + tm.tv_sec = 0; + tm.tv_usec = 0; + } + + return true; +} + +void QTimerInfoList::registerTimer(int timerId, int interval, QObject *object) +{ + QTimerInfo *t = new QTimerInfo; + t->id = timerId; + t->interval.tv_sec = interval / 1000; + t->interval.tv_usec = (interval % 1000) * 1000; + t->timeout = updateCurrentTime() + t->interval; + t->obj = object; + t->activateRef = 0; + + timerInsert(t); +} + +bool QTimerInfoList::unregisterTimer(int timerId) +{ + // set timer inactive + for (int i = 0; i < count(); ++i) { + register QTimerInfo *t = at(i); + if (t->id == timerId) { + // found it + removeAt(i); + if (t == firstTimerInfo) + firstTimerInfo = 0; + if (t->activateRef) + *(t->activateRef) = 0; + + // release the timer id + if (!QObjectPrivate::get(t->obj)->inThreadChangeEvent) + QAbstractEventDispatcherPrivate::releaseTimerId(timerId); + + delete t; + return true; + } + } + // id not found + return false; +} + +bool QTimerInfoList::unregisterTimers(QObject *object) +{ + if (isEmpty()) + return false; + for (int i = 0; i < count(); ++i) { + register QTimerInfo *t = at(i); + if (t->obj == object) { + // object found + removeAt(i); + if (t == firstTimerInfo) + firstTimerInfo = 0; + if (t->activateRef) + *(t->activateRef) = 0; + + // release the timer id + if (!QObjectPrivate::get(t->obj)->inThreadChangeEvent) + QAbstractEventDispatcherPrivate::releaseTimerId(t->id); + + delete t; + // move back one so that we don't skip the new current item + --i; + } + } + return true; +} + +QList > QTimerInfoList::registeredTimers(QObject *object) const +{ + QList > list; + for (int i = 0; i < count(); ++i) { + register const QTimerInfo * const t = at(i); + if (t->obj == object) + list << QPair(t->id, t->interval.tv_sec * 1000 + t->interval.tv_usec / 1000); + } + return list; +} + +/* + Activate pending timers, returning how many where activated. +*/ +int QTimerInfoList::activateTimers() +{ + if (qt_disable_lowpriority_timers || isEmpty()) + return 0; // nothing to do + + int n_act = 0, maxCount = 0; + firstTimerInfo = 0; + + timeval currentTime = updateCurrentTime(); + repairTimersIfNeeded(); + + + // Find out how many timer have expired + for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) { + if (currentTime < (*it)->timeout) + break; + maxCount++; + } + + //fire the timers. + while (maxCount--) { + if (isEmpty()) + break; + + QTimerInfo *currentTimerInfo = first(); + if (currentTime < currentTimerInfo->timeout) + break; // no timer has expired + + if (!firstTimerInfo) { + firstTimerInfo = currentTimerInfo; + } else if (firstTimerInfo == currentTimerInfo) { + // avoid sending the same timer multiple times + break; + } else if (currentTimerInfo->interval < firstTimerInfo->interval + || currentTimerInfo->interval == firstTimerInfo->interval) { + firstTimerInfo = currentTimerInfo; + } + + // remove from list + removeFirst(); + + // determine next timeout time + currentTimerInfo->timeout += currentTimerInfo->interval; + if (currentTimerInfo->timeout < currentTime) + currentTimerInfo->timeout = currentTime + currentTimerInfo->interval; + + // reinsert timer + timerInsert(currentTimerInfo); + if (currentTimerInfo->interval.tv_usec > 0 || currentTimerInfo->interval.tv_sec > 0) + n_act++; + + if (!currentTimerInfo->activateRef) { + // send event, but don't allow it to recurse + currentTimerInfo->activateRef = ¤tTimerInfo; + + QTimerEvent e(currentTimerInfo->id); + QCoreApplication::sendEvent(currentTimerInfo->obj, &e); + + if (currentTimerInfo) + currentTimerInfo->activateRef = 0; + } + } + + firstTimerInfo = 0; + return n_act; +} + +QEventDispatcherUNIX::QEventDispatcherUNIX(QObject *parent) + : QAbstractEventDispatcher(*new QEventDispatcherUNIXPrivate, parent) +{ } + +QEventDispatcherUNIX::QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObject *parent) + : QAbstractEventDispatcher(dd, parent) +{ } + +QEventDispatcherUNIX::~QEventDispatcherUNIX() +{ + Q_D(QEventDispatcherUNIX); + d->threadData->eventDispatcher = 0; +} + +int QEventDispatcherUNIX::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timeval *timeout) +{ + return qt_safe_select(nfds, readfds, writefds, exceptfds, timeout); +} + +/*! + \internal +*/ +void QEventDispatcherUNIX::registerTimer(int timerId, int interval, QObject *obj) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1 || interval < 0 || !obj) { + qWarning("QEventDispatcherUNIX::registerTimer: invalid arguments"); + return; + } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::startTimer: timers cannot be started from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherUNIX); + d->timerList.registerTimer(timerId, interval, obj); +} + +/*! + \internal +*/ +bool QEventDispatcherUNIX::unregisterTimer(int timerId) +{ +#ifndef QT_NO_DEBUG + if (timerId < 1) { + qWarning("QEventDispatcherUNIX::unregisterTimer: invalid argument"); + return false; + } else if (thread() != QThread::currentThread()) { + qWarning("QObject::killTimer: timers cannot be stopped from another thread"); + return false; + } +#endif + + Q_D(QEventDispatcherUNIX); + return d->timerList.unregisterTimer(timerId); +} + +/*! + \internal +*/ +bool QEventDispatcherUNIX::unregisterTimers(QObject *object) +{ +#ifndef QT_NO_DEBUG + if (!object) { + qWarning("QEventDispatcherUNIX::unregisterTimers: invalid argument"); + return false; + } else if (object->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::killTimers: timers cannot be stopped from another thread"); + return false; + } +#endif + + Q_D(QEventDispatcherUNIX); + return d->timerList.unregisterTimers(object); +} + +QList +QEventDispatcherUNIX::registeredTimers(QObject *object) const +{ + if (!object) { + qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument"); + return QList(); + } + + Q_D(const QEventDispatcherUNIX); + return d->timerList.registeredTimers(object); +} + +/***************************************************************************** + Socket notifier type + *****************************************************************************/ +QSockNotType::QSockNotType() +{ + FD_ZERO(&select_fds); + FD_ZERO(&enabled_fds); + FD_ZERO(&pending_fds); +} + +QSockNotType::~QSockNotType() +{ + for (int i = 0; i < list.size(); ++i) + delete list[i]; +} + +/***************************************************************************** + QEventDispatcher implementations for UNIX + *****************************************************************************/ + +void QEventDispatcherUNIX::registerSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int sockfd = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (sockfd < 0 + || unsigned(sockfd) >= FD_SETSIZE) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() + || thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherUNIX); + QSockNotType::List &list = d->sn_vec[type].list; + fd_set *fds = &d->sn_vec[type].enabled_fds; + QSockNot *sn; + + sn = new QSockNot; + sn->obj = notifier; + sn->fd = sockfd; + sn->queue = &d->sn_vec[type].pending_fds; + + int i; + for (i = 0; i < list.size(); ++i) { + QSockNot *p = list[i]; + if (p->fd < sockfd) + break; + if (p->fd == sockfd) { + static const char *t[] = { "Read", "Write", "Exception" }; + qWarning("QSocketNotifier: Multiple socket notifiers for " + "same socket %d and type %s", sockfd, t[type]); + } + } + list.insert(i, sn); + + FD_SET(sockfd, fds); + d->sn_highest = qMax(d->sn_highest, sockfd); +} + +void QEventDispatcherUNIX::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int sockfd = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (sockfd < 0 + || unsigned(sockfd) >= FD_SETSIZE) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() + || thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherUNIX); + QSockNotType::List &list = d->sn_vec[type].list; + fd_set *fds = &d->sn_vec[type].enabled_fds; + QSockNot *sn = 0; + int i; + for (i = 0; i < list.size(); ++i) { + sn = list[i]; + if(sn->obj == notifier && sn->fd == sockfd) + break; + } + if (i == list.size()) // not found + return; + + FD_CLR(sockfd, fds); // clear fd bit + FD_CLR(sockfd, sn->queue); + d->sn_pending_list.removeAll(sn); // remove from activation list + list.removeAt(i); // remove notifier found above + delete sn; + + if (d->sn_highest == sockfd) { // find highest fd + d->sn_highest = -1; + for (int i=0; i<3; i++) { + if (!d->sn_vec[i].list.isEmpty()) + d->sn_highest = qMax(d->sn_highest, // list is fd-sorted + d->sn_vec[i].list[0]->fd); + } + } +} + +void QEventDispatcherUNIX::setSocketNotifierPending(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int sockfd = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (sockfd < 0 + || unsigned(sockfd) >= FD_SETSIZE) { + qWarning("QSocketNotifier: Internal error"); + return; + } + Q_ASSERT(notifier->thread() == thread() && thread() == QThread::currentThread()); +#endif + + Q_D(QEventDispatcherUNIX); + QSockNotType::List &list = d->sn_vec[type].list; + QSockNot *sn = 0; + int i; + for (i = 0; i < list.size(); ++i) { + sn = list[i]; + if(sn->obj == notifier && sn->fd == sockfd) + break; + } + if (i == list.size()) // not found + return; + + // We choose a random activation order to be more fair under high load. + // If a constant order is used and a peer early in the list can + // saturate the IO, it might grab our attention completely. + // Also, if we're using a straight list, the callback routines may + // delete other entries from the list before those other entries are + // processed. + if (! FD_ISSET(sn->fd, sn->queue)) { + if (d->sn_pending_list.isEmpty()) { + d->sn_pending_list.append(sn); + } else { + d->sn_pending_list.insert((qrand() & 0xff) % + (d->sn_pending_list.size()+1), sn); + } + FD_SET(sn->fd, sn->queue); + } +} + +int QEventDispatcherUNIX::activateTimers() +{ + Q_ASSERT(thread() == QThread::currentThread()); + Q_D(QEventDispatcherUNIX); + return d->timerList.activateTimers(); +} + +int QEventDispatcherUNIX::activateSocketNotifiers() +{ + Q_D(QEventDispatcherUNIX); + if (d->sn_pending_list.isEmpty()) + return 0; + + // activate entries + int n_act = 0; + QEvent event(QEvent::SockAct); + while (!d->sn_pending_list.isEmpty()) { + QSockNot *sn = d->sn_pending_list.takeFirst(); + if (FD_ISSET(sn->fd, sn->queue)) { + FD_CLR(sn->fd, sn->queue); + QCoreApplication::sendEvent(sn->obj, &event); + ++n_act; + } + } + return n_act; +} + +bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_D(QEventDispatcherUNIX); + d->interrupt = false; + + // we are awake, broadcast it + emit awake(); + QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + + int nevents = 0; + const bool canWait = (d->threadData->canWait + && !d->interrupt + && (flags & QEventLoop::WaitForMoreEvents)); + + if (canWait) + emit aboutToBlock(); + + if (!d->interrupt) { + // return the maximum time we can wait for an event. + timeval *tm = 0; + timeval wait_tm = { 0l, 0l }; + if (!(flags & QEventLoop::X11ExcludeTimers)) { + if (d->timerList.timerWait(wait_tm)) + tm = &wait_tm; + } + + if (!canWait) { + if (!tm) + tm = &wait_tm; + + // no time to wait + tm->tv_sec = 0l; + tm->tv_usec = 0l; + } + + nevents = d->doSelect(flags, tm); + + // activate timers + if (! (flags & QEventLoop::X11ExcludeTimers)) { + nevents += activateTimers(); + } + } + // return true if we handled events, false otherwise + return (nevents > 0); +} + +bool QEventDispatcherUNIX::hasPendingEvents() +{ + extern uint qGlobalPostedEventsCount(); // from qapplication.cpp + return qGlobalPostedEventsCount(); +} + +void QEventDispatcherUNIX::wakeUp() +{ + Q_D(QEventDispatcherUNIX); + if (d->wakeUps.testAndSetAcquire(0, 1)) { + char c = 0; + qt_safe_write( d->thread_pipe[1], &c, 1 ); + } +} + +void QEventDispatcherUNIX::interrupt() +{ + Q_D(QEventDispatcherUNIX); + d->interrupt = true; + wakeUp(); +} + +void QEventDispatcherUNIX::flush() +{ } + + + + +void QCoreApplication::watchUnixSignal(int sig, bool watch) +{ + if (sig < NSIG) { + struct sigaction sa; + sigemptyset(&(sa.sa_mask)); + sa.sa_flags = 0; + if (watch) + sa.sa_handler = signalHandler; + else + sa.sa_handler = SIG_DFL; + sigaction(sig, &sa, 0); + } +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h new file mode 100644 index 0000000000..96a3e4a276 --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_unix_p.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QEVENTDISPATCHER_UNIX_P_H +#define QEVENTDISPATCHER_UNIX_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/qabstracteventdispatcher.h" +#include "QtCore/qlist.h" +#include "private/qabstracteventdispatcher_p.h" +#include "private/qcore_unix_p.h" +#include "private/qpodlist_p.h" +#include "QtCore/qvarlengtharray.h" + +#if defined(Q_OS_VXWORKS) +# include +#else +# include +# if (!defined(Q_OS_HPUX) || defined(__ia64)) && !defined(Q_OS_NACL) +# include +# endif +#endif + +QT_BEGIN_NAMESPACE + +// internal timer info +struct QTimerInfo { + int id; // - timer identifier + timeval interval; // - timer interval + timeval timeout; // - when to sent event + QObject *obj; // - object to receive event + QTimerInfo **activateRef; // - ref from activateTimers +}; + +class QTimerInfoList : public QList +{ +#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)) || defined(QT_BOOTSTRAPPED) + timeval previousTime; + clock_t previousTicks; + int ticksPerSecond; + int msPerTick; + + bool timeChanged(timeval *delta); +#endif + + // state variables used by activateTimers() + QTimerInfo *firstTimerInfo; + +public: + QTimerInfoList(); + + timeval currentTime; + timeval updateCurrentTime(); + + // must call updateCurrentTime() first! + void repairTimersIfNeeded(); + + bool timerWait(timeval &); + void timerInsert(QTimerInfo *); + void timerRepair(const timeval &); + + void registerTimer(int timerId, int interval, QObject *object); + bool unregisterTimer(int timerId); + bool unregisterTimers(QObject *object); + QList > registeredTimers(QObject *object) const; + + int activateTimers(); +}; + +struct QSockNot +{ + QSocketNotifier *obj; + int fd; + fd_set *queue; +}; + +class QSockNotType +{ +public: + QSockNotType(); + ~QSockNotType(); + + typedef QPodList List; + + List list; + fd_set select_fds; + fd_set enabled_fds; + fd_set pending_fds; + +}; + +class QEventDispatcherUNIXPrivate; + +class Q_CORE_EXPORT QEventDispatcherUNIX : public QAbstractEventDispatcher +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QEventDispatcherUNIX) + +public: + explicit QEventDispatcherUNIX(QObject *parent = 0); + ~QEventDispatcherUNIX(); + + bool processEvents(QEventLoop::ProcessEventsFlags flags); + bool hasPendingEvents(); + + void registerSocketNotifier(QSocketNotifier *notifier); + void unregisterSocketNotifier(QSocketNotifier *notifier); + + void registerTimer(int timerId, int interval, QObject *object); + bool unregisterTimer(int timerId); + bool unregisterTimers(QObject *object); + QList registeredTimers(QObject *object) const; + + void wakeUp(); + void interrupt(); + void flush(); + +protected: + QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObject *parent = 0); + + void setSocketNotifierPending(QSocketNotifier *notifier); + + int activateTimers(); + int activateSocketNotifiers(); + + virtual int select(int nfds, + fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + timeval *timeout); +}; + +class Q_CORE_EXPORT QEventDispatcherUNIXPrivate : public QAbstractEventDispatcherPrivate +{ + Q_DECLARE_PUBLIC(QEventDispatcherUNIX) + +public: + QEventDispatcherUNIXPrivate(); + ~QEventDispatcherUNIXPrivate(); + + int doSelect(QEventLoop::ProcessEventsFlags flags, timeval *timeout); + + bool mainThread; + int thread_pipe[2]; + + // highest fd for all socket notifiers + int sn_highest; + // 3 socket notifier types - read, write and exception + QSockNotType sn_vec[3]; + + QTimerInfoList timerList; + + // pending socket notifiers list + QSockNotType::List sn_pending_list; + + QAtomicInt wakeUps; + bool interrupt; +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_UNIX_P_H diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp new file mode 100644 index 0000000000..6badb6a2f8 --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -0,0 +1,1158 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qeventdispatcher_win_p.h" + +#include "qcoreapplication.h" +#include "qhash.h" +#include +#include "qpair.h" +#include "qset.h" +#include "qsocketnotifier.h" +#include "qvarlengtharray.h" +#include "qwineventnotifier_p.h" + +#include "qabstracteventdispatcher_p.h" +#include "qcoreapplication_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +HINSTANCE qWinAppInst(); +extern uint qGlobalPostedEventsCount(); + +#ifndef TIME_KILL_SYNCHRONOUS +# define TIME_KILL_SYNCHRONOUS 0x0100 +#endif + +#ifndef QS_RAWINPUT +# ifdef Q_OS_WINCE +# define QS_RAWINPUT 0x0000 +# else +# define QS_RAWINPUT 0x0400 +# endif +#endif + +#ifndef WM_TOUCH +# define WM_TOUCH 0x0240 +#endif +#ifndef QT_NO_GESTURES +#ifndef WM_GESTURE +# define WM_GESTURE 0x0119 +#endif +#ifndef WM_GESTURENOTIFY +# define WM_GESTURENOTIFY 0x011A +#endif +#endif // QT_NO_GESTURES + +enum { + WM_QT_SOCKETNOTIFIER = WM_USER, + WM_QT_SENDPOSTEDEVENTS = WM_USER + 1, + SendPostedEventsWindowsTimerId = ~1u +}; + +#if defined(Q_OS_WINCE) +QT_BEGIN_INCLUDE_NAMESPACE +#include +// Asynchronous Winsocks ------------------------------------------ +#ifndef QT_NO_THREAD +#include +#include +#include +QT_END_INCLUDE_NAMESPACE + +//#define QCE_ASYNC_DEBUG + +namespace { + class SocketAsyncHandler; + + class SocketAsyncHandler : public QThread + { + public: + SocketAsyncHandler(); + ~SocketAsyncHandler(); + void run(); + void select(SOCKET sock, HWND handle, unsigned int msg, long ev); + void removeSelect(SOCKET sock); + void safeRemove(SOCKET sock); + private: + struct SockInfo { + HWND handle; + unsigned int msg; + long ev; + }; + QMap sockets; + QMutex mutex; + QWaitCondition cond; + bool supposedToDie; + }; + + SocketAsyncHandler::SocketAsyncHandler() + : supposedToDie(false) + { + } + + SocketAsyncHandler::~SocketAsyncHandler() + { + mutex.lock(); + supposedToDie = true; + mutex.unlock(); + cond.wakeOne(); + wait(); + while (sockets.size() > 0) + removeSelect(sockets.begin().key()); + } + + void SocketAsyncHandler::removeSelect(SOCKET sock) + { + if (!sockets.contains(sock)) + return; + sockets.remove(sock); + return; + } + + void SocketAsyncHandler::safeRemove(SOCKET sock) + { + QMutexLocker locker(&mutex); + removeSelect(sock); + } + + void SocketAsyncHandler::select(SOCKET sock, HWND handle, unsigned int msg, long ev) + { + QMutexLocker locker(&mutex); + + if (sockets.contains(sock)) + sockets.remove(sock); + + SockInfo info; + info.handle = handle; + info.msg = msg; + info.ev = ev; + sockets.insert(sock, info); + cond.wakeOne(); + } + + void SocketAsyncHandler::run() + { + do { + mutex.lock(); + + while (!supposedToDie && sockets.isEmpty()) { + cond.wait(&mutex); + } + + if (supposedToDie) { + mutex.unlock(); + break; + } + + // Copy current items to reduce lock time + // and to be able to use SendMessage + QMap currentSockets = sockets; + mutex.unlock(); + + fd_set readS, writeS, exS; + FD_ZERO(&readS); + FD_ZERO(&writeS); + FD_ZERO(&exS); + + int maxFd = 0; + + for (QMap::iterator it = currentSockets.begin(); it != currentSockets.end(); ++it) { + const SockInfo &info = it.value(); + int socket = it.key(); + maxFd = qMax(maxFd, socket); + + if ((info.ev & FD_READ) || (info.ev & FD_CLOSE) || (info.ev & FD_ACCEPT)) + FD_SET(socket, &readS); + if ((info.ev & FD_WRITE)|| (info.ev & FD_CONNECT)) + FD_SET(socket, &writeS); + if (info.ev & FD_OOB) + FD_SET(socket, &exS); + } + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 50000; + int result = ::select(maxFd + 1, &readS, &writeS, &exS, &timeout); + if (result > 0) { + HWND handle; + unsigned int tmpMsg; + SOCKET sock; + HRESULT ret; + for (QMap::const_iterator it = currentSockets.constBegin(); + it != currentSockets.constEnd(); ++it) { + handle = (*it).handle; + tmpMsg = (*it).msg; + sock = it.key(); + if (FD_ISSET(sock, &readS)) + ret = SendMessage(handle, tmpMsg, sock, FD_READ); + + if (FD_ISSET(sock, &writeS)) + ret = SendMessage(handle, tmpMsg, sock, FD_WRITE); + + if (FD_ISSET(sock, &exS)) + ret = SendMessage(handle, tmpMsg, sock, FD_OOB); + } + } + +#ifdef QCE_ASYNC_DEBUG + else if (result == 0) { //timeout + qDebug(" WSAAsync select timeout"); + } else if (result < 0) { // SocketError + // This might happen because of two reasons + // 1. We already closed a socket in between the copy and the select + // and thus select() returns an error + // 2. Something is really wrong, then + // ### Loop on all descriptors, try to select and remove the + // ### broken one. + qWarning("WSAAsync select error %d", WSAGetLastError()); + } +#endif + } while(true); + } +} // namespace + +Q_GLOBAL_STATIC(SocketAsyncHandler, qt_async_handler) + +int WSAAsyncSelect(SOCKET sock, HWND handle, unsigned int msg, long ev) +{ + if (sock == 0 || handle == 0 || handle == INVALID_HANDLE_VALUE) { + WSASetLastError(WSAEINVAL); + return SOCKET_ERROR; + } + + if (msg == 0 && ev == 0) + qt_async_handler()->safeRemove(sock); + else + qt_async_handler()->select(sock, handle, msg, ev); + + qt_async_handler()->start(QThread::LowPriority); + WSASetLastError(0); + return 0; +} +#else // QT_NO_THREAD +int WSAAsyncSelect(SOCKET, HWND, unsigned int, long) +{ + return SOCKET_ERROR; +} +#endif +#endif // Q_OS_WINCE + +class QEventDispatcherWin32Private; + +struct QSockNot { + QSocketNotifier *obj; + int fd; +}; +typedef QHash QSNDict; + +struct WinTimerInfo { // internal timer info + QObject *dispatcher; + int timerId; + int interval; + QObject *obj; // - object to receive events + bool inTimerEvent; + int fastTimerId; +}; + +class QZeroTimerEvent : public QTimerEvent +{ +public: + inline QZeroTimerEvent(int timerId) + : QTimerEvent(timerId) + { t = QEvent::ZeroTimerEvent; } +}; + +typedef QList WinTimerVec; // vector of TimerInfo structs +typedef QHash WinTimerDict; // fast dict of timers + +#if !defined(DWORD_PTR) && !defined(Q_WS_WIN64) +#define DWORD_PTR DWORD +#endif + +typedef MMRESULT(WINAPI *ptimeSetEvent)(UINT, UINT, LPTIMECALLBACK, DWORD_PTR, UINT); +typedef MMRESULT(WINAPI *ptimeKillEvent)(UINT); + +static ptimeSetEvent qtimeSetEvent = 0; +static ptimeKillEvent qtimeKillEvent = 0; + +LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp); + +static void resolveTimerAPI() +{ + static bool triedResolve = false; + if (!triedResolve) { +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve)); + if (triedResolve) + return; +#endif + triedResolve = true; +#if !defined(Q_OS_WINCE) + qtimeSetEvent = (ptimeSetEvent)QSystemLibrary::resolve(QLatin1String("winmm"), "timeSetEvent"); + qtimeKillEvent = (ptimeKillEvent)QSystemLibrary::resolve(QLatin1String("winmm"), "timeKillEvent"); +#else + qtimeSetEvent = (ptimeSetEvent)QSystemLibrary::resolve(QLatin1String("Mmtimer"), "timeSetEvent"); + qtimeKillEvent = (ptimeKillEvent)QSystemLibrary::resolve(QLatin1String("Mmtimer"), "timeKillEvent"); +#endif + } +} + + +class QEventDispatcherWin32Private : public QAbstractEventDispatcherPrivate +{ + Q_DECLARE_PUBLIC(QEventDispatcherWin32) +public: + QEventDispatcherWin32Private(); + ~QEventDispatcherWin32Private(); + + DWORD threadId; + + bool interrupt; + + // internal window handle used for socketnotifiers/timers/etc + HWND internalHwnd; + HHOOK getMessageHook; + + // for controlling when to send posted events + QAtomicInt serialNumber; + int lastSerialNumber, sendPostedEventsWindowsTimerId; + QAtomicInt wakeUps; + + // timers + WinTimerVec timerVec; + WinTimerDict timerDict; + void registerTimer(WinTimerInfo *t); + void unregisterTimer(WinTimerInfo *t, bool closingDown = false); + void sendTimerEvent(int timerId); + + // socket notifiers + QSNDict sn_read; + QSNDict sn_write; + QSNDict sn_except; + void doWsaAsyncSelect(int socket); + + QList winEventNotifierList; + void activateEventNotifier(QWinEventNotifier * wen); + + QList queuedUserInputEvents; + QList queuedSocketEvents; +}; + +QEventDispatcherWin32Private::QEventDispatcherWin32Private() + : threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0), getMessageHook(0), + serialNumber(0), lastSerialNumber(0), sendPostedEventsWindowsTimerId(0), wakeUps(0) +{ + resolveTimerAPI(); +} + +QEventDispatcherWin32Private::~QEventDispatcherWin32Private() +{ + if (internalHwnd) + DestroyWindow(internalHwnd); + QString className = QLatin1String("QEventDispatcherWin32_Internal_Widget") + QString::number(quintptr(qt_internal_proc)); + UnregisterClass((wchar_t*)className.utf16(), qWinAppInst()); +} + +void QEventDispatcherWin32Private::activateEventNotifier(QWinEventNotifier * wen) +{ + QEvent event(QEvent::WinEventAct); + QCoreApplication::sendEvent(wen, &event); +} + +// ### Qt 5: remove +Q_CORE_EXPORT bool winPeekMessage(MSG* msg, HWND hWnd, UINT wMsgFilterMin, + UINT wMsgFilterMax, UINT wRemoveMsg) +{ + return PeekMessage(msg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); +} + +// ### Qt 5: remove +Q_CORE_EXPORT bool winPostMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return PostMessage(hWnd, msg, wParam, lParam); +} + +// ### Qt 5: remove +Q_CORE_EXPORT bool winGetMessage(MSG* msg, HWND hWnd, UINT wMsgFilterMin, + UINT wMsgFilterMax) +{ + return GetMessage(msg, hWnd, wMsgFilterMin, wMsgFilterMax); +} + +// This function is called by a workerthread +void WINAPI QT_WIN_CALLBACK qt_fast_timer_proc(uint timerId, uint /*reserved*/, DWORD_PTR user, DWORD_PTR /*reserved*/, DWORD_PTR /*reserved*/) +{ + if (!timerId) // sanity check + return; + WinTimerInfo *t = (WinTimerInfo*)user; + Q_ASSERT(t); + QCoreApplication::postEvent(t->dispatcher, new QTimerEvent(t->timerId)); +} + +LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) +{ + if (message == WM_NCCREATE) + return true; + + MSG msg; + msg.hwnd = hwnd; + msg.message = message; + msg.wParam = wp; + msg.lParam = lp; + QCoreApplication *app = QCoreApplication::instance(); + long result; + if (!app) { + if (message == WM_TIMER) + KillTimer(hwnd, wp); + return 0; + } else if (app->filterEvent(&msg, &result)) { + return result; + } + +#ifdef GWLP_USERDATA + QEventDispatcherWin32 *q = (QEventDispatcherWin32 *) GetWindowLongPtr(hwnd, GWLP_USERDATA); +#else + QEventDispatcherWin32 *q = (QEventDispatcherWin32 *) GetWindowLong(hwnd, GWL_USERDATA); +#endif + QEventDispatcherWin32Private *d = 0; + if (q != 0) + d = q->d_func(); + + if (message == WM_QT_SOCKETNOTIFIER) { + // socket notifier message + int type = -1; + switch (WSAGETSELECTEVENT(lp)) { + case FD_READ: + case FD_CLOSE: + case FD_ACCEPT: + type = 0; + break; + case FD_WRITE: + case FD_CONNECT: + type = 1; + break; + case FD_OOB: + type = 2; + break; + } + if (type >= 0) { + Q_ASSERT(d != 0); + QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except }; + QSNDict *dict = sn_vec[type]; + + QSockNot *sn = dict ? dict->value(wp) : 0; + if (sn) { + QEvent event(QEvent::SockAct); + QCoreApplication::sendEvent(sn->obj, &event); + } + } + return 0; + } else if (message == WM_QT_SENDPOSTEDEVENTS + // we also use a Windows timer to send posted events when the message queue is full + || (message == WM_TIMER + && d->sendPostedEventsWindowsTimerId != 0 + && wp == (uint)d->sendPostedEventsWindowsTimerId)) { + int localSerialNumber = d->serialNumber; + if (localSerialNumber != d->lastSerialNumber) { + d->lastSerialNumber = localSerialNumber; + QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + } + return 0; + } else if (message == WM_TIMER) { + Q_ASSERT(d != 0); + d->sendTimerEvent(wp); + return 0; + } + + return DefWindowProc(hwnd, message, wp, lp); +} + +LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp) +{ + if (wp == PM_REMOVE) { + QEventDispatcherWin32 *q = qobject_cast(QAbstractEventDispatcher::instance()); + Q_ASSERT(q != 0); + if (q) { + MSG *msg = (MSG *) lp; + QEventDispatcherWin32Private *d = q->d_func(); + int localSerialNumber = d->serialNumber; + if (HIWORD(GetQueueStatus(QS_TIMER | QS_INPUT | QS_RAWINPUT)) == 0) { + // no more input or timer events in the message queue, we can allow posted events to be sent normally now + if (d->sendPostedEventsWindowsTimerId != 0) { + // stop the timer to send posted events, since we now allow the WM_QT_SENDPOSTEDEVENTS message + KillTimer(d->internalHwnd, d->sendPostedEventsWindowsTimerId); + d->sendPostedEventsWindowsTimerId = 0; + } + (void) d->wakeUps.fetchAndStoreRelease(0); + if (localSerialNumber != d->lastSerialNumber + // if this message IS the one that triggers sendPostedEvents(), no need to post it again + && (msg->hwnd != d->internalHwnd + || msg->message != WM_QT_SENDPOSTEDEVENTS)) { + PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0); + } + } else if (d->sendPostedEventsWindowsTimerId == 0 + && localSerialNumber != d->lastSerialNumber) { + // start a special timer to continue delivering posted events while + // there are still input and timer messages in the message queue + d->sendPostedEventsWindowsTimerId = SetTimer(d->internalHwnd, + SendPostedEventsWindowsTimerId, + 0, // we specify zero, but Windows uses USER_TIMER_MINIMUM + NULL); + // we don't check the return value of SetTimer()... if creating the timer failed, there's little + // we can do. we just have to accept that posted events will be starved + } + } + } +#ifdef Q_OS_WINCE + return 0; +#else + return CallNextHookEx(0, code, wp, lp); +#endif +} + +static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatcher) +{ + // make sure that multiple Qt's can coexist in the same process + QString className = QLatin1String("QEventDispatcherWin32_Internal_Widget") + QString::number(quintptr(qt_internal_proc)); + + WNDCLASS wc; + wc.style = 0; + wc.lpfnWndProc = qt_internal_proc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = qWinAppInst(); + wc.hIcon = 0; + wc.hCursor = 0; + wc.hbrBackground = 0; + wc.lpszMenuName = NULL; + wc.lpszClassName = reinterpret_cast (className.utf16()); + + RegisterClass(&wc); + HWND wnd = CreateWindow(wc.lpszClassName, // classname + wc.lpszClassName, // window name + 0, // style + 0, 0, 0, 0, // geometry + 0, // parent + 0, // menu handle + qWinAppInst(), // application + 0); // windows creation data. + + if (!wnd) { + qWarning("QEventDispatcher: Failed to create QEventDispatcherWin32 internal window: %d\n", (int)GetLastError()); + } + +#ifdef GWLP_USERDATA + SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)eventDispatcher); +#else + SetWindowLong(wnd, GWL_USERDATA, (LONG)eventDispatcher); +#endif + + return wnd; +} + +void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t) +{ + Q_ASSERT(internalHwnd); + + Q_Q(QEventDispatcherWin32); + + int ok = 0; + if (t->interval > 20 || !t->interval || !qtimeSetEvent) { + ok = 1; + if (!t->interval) // optimization for single-shot-zero-timer + QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId)); + else + ok = SetTimer(internalHwnd, t->timerId, (uint) t->interval, 0); + } else { + ok = t->fastTimerId = qtimeSetEvent(t->interval, 1, qt_fast_timer_proc, (DWORD_PTR)t, + TIME_CALLBACK_FUNCTION | TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); + if (ok == 0) { // fall back to normal timer if no more multimedia timers available + ok = SetTimer(internalHwnd, t->timerId, (uint) t->interval, 0); + } + } + + if (ok == 0) + qErrnoWarning("QEventDispatcherWin32::registerTimer: Failed to create a timer"); +} + +void QEventDispatcherWin32Private::unregisterTimer(WinTimerInfo *t, bool closingDown) +{ + // mark timer as unused + if (!QObjectPrivate::get(t->obj)->inThreadChangeEvent && !closingDown) + QAbstractEventDispatcherPrivate::releaseTimerId(t->timerId); + + if (t->interval == 0) { + QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId); + } else if (t->fastTimerId != 0) { + qtimeKillEvent(t->fastTimerId); + QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId); + } else if (internalHwnd) { + KillTimer(internalHwnd, t->timerId); + } + delete t; +} + +void QEventDispatcherWin32Private::sendTimerEvent(int timerId) +{ + WinTimerInfo *t = timerDict.value(timerId); + if (t && !t->inTimerEvent) { + // send event, but don't allow it to recurse + t->inTimerEvent = true; + + QTimerEvent e(t->timerId); + QCoreApplication::sendEvent(t->obj, &e); + + // timer could have been removed + t = timerDict.value(timerId); + if (t) { + t->inTimerEvent = false; + } + } +} + +void QEventDispatcherWin32Private::doWsaAsyncSelect(int socket) +{ + Q_ASSERT(internalHwnd); + int sn_event = 0; + if (sn_read.contains(socket)) + sn_event |= FD_READ | FD_CLOSE | FD_ACCEPT; + if (sn_write.contains(socket)) + sn_event |= FD_WRITE | FD_CONNECT; + if (sn_except.contains(socket)) + sn_event |= FD_OOB; + // BoundsChecker may emit a warning for WSAAsyncSelect when sn_event == 0 + // This is a BoundsChecker bug and not a Qt bug + WSAAsyncSelect(socket, internalHwnd, sn_event ? WM_QT_SOCKETNOTIFIER : 0, sn_event); +} + +void QEventDispatcherWin32::createInternalHwnd() +{ + Q_D(QEventDispatcherWin32); + + Q_ASSERT(!d->internalHwnd); + if (d->internalHwnd) + return; + d->internalHwnd = qt_create_internal_window(this); + +#ifndef Q_OS_WINCE + // setup GetMessage hook needed to drive our posted events + d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId()); + if (!d->getMessageHook) { + qFatal("Qt: INTERNALL ERROR: failed to install GetMessage hook"); + } +#endif + + // register all socket notifiers + QList sockets = (d->sn_read.keys().toSet() + + d->sn_write.keys().toSet() + + d->sn_except.keys().toSet()).toList(); + for (int i = 0; i < sockets.count(); ++i) + d->doWsaAsyncSelect(sockets.at(i)); + + // start all normal timers + for (int i = 0; i < d->timerVec.count(); ++i) + d->registerTimer(d->timerVec.at(i)); + + // trigger a call to sendPostedEvents() + wakeUp(); +} + +QEventDispatcherWin32::QEventDispatcherWin32(QObject *parent) + : QAbstractEventDispatcher(*new QEventDispatcherWin32Private, parent) +{ +} + +QEventDispatcherWin32::~QEventDispatcherWin32() +{ +} + +bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_D(QEventDispatcherWin32); + + if (!d->internalHwnd) + createInternalHwnd(); + + d->interrupt = false; + emit awake(); + + bool canWait; + bool retVal = false; + bool seenWM_QT_SENDPOSTEDEVENTS = false; + bool needWM_QT_SENDPOSTEDEVENTS = false; + do { + DWORD waitRet = 0; + HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1]; + QVarLengthArray processedTimers; + while (!d->interrupt) { + DWORD nCount = d->winEventNotifierList.count(); + Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); + + MSG msg; + bool haveMessage; + + if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) { + // process queued user input events + haveMessage = true; + msg = d->queuedUserInputEvents.takeFirst(); + } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) { + // process queued socket events + haveMessage = true; + msg = d->queuedSocketEvents.takeFirst(); + } else { + haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE); + if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents) + && ((msg.message >= WM_KEYFIRST + && msg.message <= WM_KEYLAST) + || (msg.message >= WM_MOUSEFIRST + && msg.message <= WM_MOUSELAST) + || msg.message == WM_MOUSEWHEEL + || msg.message == WM_MOUSEHWHEEL + || msg.message == WM_TOUCH +#ifndef QT_NO_GESTURES + || msg.message == WM_GESTURE + || msg.message == WM_GESTURENOTIFY +#endif + || msg.message == WM_CLOSE)) { + // queue user input events for later processing + haveMessage = false; + d->queuedUserInputEvents.append(msg); + } + if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers) + && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) { + // queue socket events for later processing + haveMessage = false; + d->queuedSocketEvents.append(msg); + } + } + if (!haveMessage) { + // no message - check for signalled objects + for (int i=0; i<(int)nCount; i++) + pHandles[i] = d->winEventNotifierList.at(i)->handle(); + waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE); + if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) { + // a new message has arrived, process it + continue; + } + } + if (haveMessage) { +#ifdef Q_OS_WINCE + // WinCE doesn't support hooks at all, so we have to call this by hand :( + (void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg); +#endif + + 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; + } + seenWM_QT_SENDPOSTEDEVENTS = true; + } else if (msg.message == WM_TIMER) { + // avoid live-lock by keeping track of the timers we've already sent + bool found = false; + for (int i = 0; !found && i < processedTimers.count(); ++i) { + const MSG processed = processedTimers.constData()[i]; + found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam); + } + if (found) + continue; + processedTimers.append(msg); + } else if (msg.message == WM_QUIT) { + if (QCoreApplication::instance()) + QCoreApplication::instance()->quit(); + return false; + } + + if (!filterEvent(&msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } else if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) { + d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); + } else { + // nothing todo so break + break; + } + retVal = true; + } + + // still nothing - wait for message or signalled objects + canWait = (!retVal + && !d->interrupt + && (flags & QEventLoop::WaitForMoreEvents)); + if (canWait) { + DWORD nCount = d->winEventNotifierList.count(); + Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); + for (int i=0; i<(int)nCount; i++) + pHandles[i] = d->winEventNotifierList.at(i)->handle(); + + emit aboutToBlock(); + waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); + emit awake(); + if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) { + d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); + retVal = true; + } + } + } while (canWait); + + if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) { + // when called "manually", always send posted events + QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + } + + if (needWM_QT_SENDPOSTEDEVENTS) + PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0); + + return retVal; +} + +bool QEventDispatcherWin32::hasPendingEvents() +{ + MSG msg; + return qGlobalPostedEventsCount() || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); +} + +void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int sockfd = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (sockfd < 0) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherWin32); + QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except }; + QSNDict *dict = sn_vec[type]; + + if (QCoreApplication::closingDown()) // ### d->exitloop? + return; // after sn_cleanup, don't reinitialize. + + if (dict->contains(sockfd)) { + const char *t[] = { "Read", "Write", "Exception" }; + /* Variable "socket" below is a function pointer. */ + qWarning("QSocketNotifier: Multiple socket notifiers for " + "same socket %d and type %s", sockfd, t[type]); + } + + QSockNot *sn = new QSockNot; + sn->obj = notifier; + sn->fd = sockfd; + dict->insert(sn->fd, sn); + + if (d->internalHwnd) + d->doWsaAsyncSelect(sockfd); +} + +void QEventDispatcherWin32::unregisterSocketNotifier(QSocketNotifier *notifier) +{ + Q_ASSERT(notifier); + int sockfd = notifier->socket(); + int type = notifier->type(); +#ifndef QT_NO_DEBUG + if (sockfd < 0) { + qWarning("QSocketNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); + return; + } +#endif + + Q_D(QEventDispatcherWin32); + QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except }; + QSNDict *dict = sn_vec[type]; + QSockNot *sn = dict->value(sockfd); + if (!sn) + return; + + dict->remove(sockfd); + delete sn; + + if (d->internalHwnd) + d->doWsaAsyncSelect(sockfd); +} + +void QEventDispatcherWin32::registerTimer(int timerId, int interval, QObject *object) +{ + if (timerId < 1 || interval < 0 || !object) { + qWarning("QEventDispatcherWin32::registerTimer: invalid arguments"); + return; + } else if (object->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QObject::startTimer: timers cannot be started from another thread"); + return; + } + + Q_D(QEventDispatcherWin32); + + register WinTimerInfo *t = new WinTimerInfo; + t->dispatcher = this; + t->timerId = timerId; + t->interval = interval; + t->obj = object; + t->inTimerEvent = false; + t->fastTimerId = 0; + + if (d->internalHwnd) + d->registerTimer(t); + + d->timerVec.append(t); // store in timer vector + d->timerDict.insert(t->timerId, t); // store timers in dict +} + +bool QEventDispatcherWin32::unregisterTimer(int timerId) +{ + if (timerId < 1) { + qWarning("QEventDispatcherWin32::unregisterTimer: invalid argument"); + return false; + } + QThread *currentThread = QThread::currentThread(); + if (thread() != currentThread) { + qWarning("QObject::killTimer: timers cannot be stopped from another thread"); + return false; + } + + Q_D(QEventDispatcherWin32); + if (d->timerVec.isEmpty() || timerId <= 0) + return false; + + WinTimerInfo *t = d->timerDict.value(timerId); + if (!t) + return false; + + d->timerDict.remove(t->timerId); + d->timerVec.removeAll(t); + d->unregisterTimer(t); + return true; +} + +bool QEventDispatcherWin32::unregisterTimers(QObject *object) +{ + if (!object) { + qWarning("QEventDispatcherWin32::unregisterTimers: invalid argument"); + return false; + } + QThread *currentThread = QThread::currentThread(); + if (object->thread() != thread() || thread() != currentThread) { + qWarning("QObject::killTimers: timers cannot be stopped from another thread"); + return false; + } + + Q_D(QEventDispatcherWin32); + if (d->timerVec.isEmpty()) + return false; + register WinTimerInfo *t; + for (int i=0; itimerVec.size(); i++) { + t = d->timerVec.at(i); + if (t && t->obj == object) { // object found + d->timerDict.remove(t->timerId); + d->timerVec.removeAt(i); + d->unregisterTimer(t); + --i; + } + } + return true; +} + +QList +QEventDispatcherWin32::registeredTimers(QObject *object) const +{ + if (!object) { + qWarning("QEventDispatcherWin32:registeredTimers: invalid argument"); + return QList(); + } + + Q_D(const QEventDispatcherWin32); + QList list; + for (int i = 0; i < d->timerVec.size(); ++i) { + const WinTimerInfo *t = d->timerVec.at(i); + if (t && t->obj == object) + list << TimerInfo(t->timerId, t->interval); + } + return list; +} + +bool QEventDispatcherWin32::registerEventNotifier(QWinEventNotifier *notifier) +{ + if (!notifier) { + qWarning("QWinEventNotifier: Internal error"); + return false; + } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QWinEventNotifier: event notifiers cannot be enabled from another thread"); + return false; + } + + Q_D(QEventDispatcherWin32); + + if (d->winEventNotifierList.contains(notifier)) + return true; + + if (d->winEventNotifierList.count() >= MAXIMUM_WAIT_OBJECTS - 2) { + qWarning("QWinEventNotifier: Cannot have more than %d enabled at one time", MAXIMUM_WAIT_OBJECTS - 2); + return false; + } + d->winEventNotifierList.append(notifier); + return true; +} + +void QEventDispatcherWin32::unregisterEventNotifier(QWinEventNotifier *notifier) +{ + if (!notifier) { + qWarning("QWinEventNotifier: Internal error"); + return; + } else if (notifier->thread() != thread() || thread() != QThread::currentThread()) { + qWarning("QWinEventNotifier: event notifiers cannot be disabled from another thread"); + return; + } + + Q_D(QEventDispatcherWin32); + + int i = d->winEventNotifierList.indexOf(notifier); + if (i != -1) + d->winEventNotifierList.takeAt(i); +} + +void QEventDispatcherWin32::activateEventNotifiers() +{ + Q_D(QEventDispatcherWin32); + //### this could break if events are removed/added in the activation + for (int i=0; iwinEventNotifierList.count(); i++) { +#if !defined(Q_OS_WINCE) + if (WaitForSingleObjectEx(d->winEventNotifierList.at(i)->handle(), 0, TRUE) == WAIT_OBJECT_0) + d->activateEventNotifier(d->winEventNotifierList.at(i)); +#else + if (WaitForSingleObject(d->winEventNotifierList.at(i)->handle(), 0) == WAIT_OBJECT_0) + d->activateEventNotifier(d->winEventNotifierList.at(i)); +#endif + } +} + +void QEventDispatcherWin32::wakeUp() +{ + Q_D(QEventDispatcherWin32); + d->serialNumber.ref(); + if (d->internalHwnd && d->wakeUps.testAndSetAcquire(0, 1)) { + // post a WM_QT_SENDPOSTEDEVENTS to this thread if there isn't one already pending + PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0); + } +} + +void QEventDispatcherWin32::interrupt() +{ + Q_D(QEventDispatcherWin32); + d->interrupt = true; + wakeUp(); +} + +void QEventDispatcherWin32::flush() +{ } + +void QEventDispatcherWin32::startingUp() +{ } + +void QEventDispatcherWin32::closingDown() +{ + Q_D(QEventDispatcherWin32); + + // clean up any socketnotifiers + while (!d->sn_read.isEmpty()) + unregisterSocketNotifier((*(d->sn_read.begin()))->obj); + while (!d->sn_write.isEmpty()) + unregisterSocketNotifier((*(d->sn_write.begin()))->obj); + while (!d->sn_except.isEmpty()) + unregisterSocketNotifier((*(d->sn_except.begin()))->obj); + + // clean up any timers + for (int i = 0; i < d->timerVec.count(); ++i) + d->unregisterTimer(d->timerVec.at(i), true); + d->timerVec.clear(); + d->timerDict.clear(); + +#ifndef Q_OS_WINCE + if (d->getMessageHook) + UnhookWindowsHookEx(d->getMessageHook); + d->getMessageHook = 0; +#endif +} + +bool QEventDispatcherWin32::event(QEvent *e) +{ + Q_D(QEventDispatcherWin32); + if (e->type() == QEvent::ZeroTimerEvent) { + QZeroTimerEvent *zte = static_cast(e); + WinTimerInfo *t = d->timerDict.value(zte->timerId()); + if (t) { + t->inTimerEvent = true; + + QTimerEvent te(zte->timerId()); + QCoreApplication::sendEvent(t->obj, &te); + + t = d->timerDict.value(zte->timerId()); + if (t) { + if (t->interval == 0 && t->inTimerEvent) { + // post the next zero timer event as long as the timer was not restarted + QCoreApplication::postEvent(this, new QZeroTimerEvent(zte->timerId())); + } + + t->inTimerEvent = false; + } + } + return true; + } else if (e->type() == QEvent::Timer) { + QTimerEvent *te = static_cast(e); + d->sendTimerEvent(te->timerId()); + } + return QAbstractEventDispatcher::event(e); +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h new file mode 100644 index 0000000000..2e3a5bca19 --- /dev/null +++ b/src/corelib/kernel/qeventdispatcher_win_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QEVENTDISPATCHER_WIN_P_H +#define QEVENTDISPATCHER_WIN_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/qabstracteventdispatcher.h" +#include "QtCore/qt_windows.h" + +QT_BEGIN_NAMESPACE + +class QWinEventNotifier; +class QEventDispatcherWin32Private; + +// forward declaration +LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp); + +class Q_CORE_EXPORT QEventDispatcherWin32 : public QAbstractEventDispatcher +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QEventDispatcherWin32) + + void createInternalHwnd(); + friend class QGuiEventDispatcherWin32; + +public: + explicit QEventDispatcherWin32(QObject *parent = 0); + ~QEventDispatcherWin32(); + + bool QT_ENSURE_STACK_ALIGNED_FOR_SSE processEvents(QEventLoop::ProcessEventsFlags flags); + bool hasPendingEvents(); + + void registerSocketNotifier(QSocketNotifier *notifier); + void unregisterSocketNotifier(QSocketNotifier *notifier); + + void registerTimer(int timerId, int interval, QObject *object); + bool unregisterTimer(int timerId); + bool unregisterTimers(QObject *object); + QList registeredTimers(QObject *object) const; + + bool registerEventNotifier(QWinEventNotifier *notifier); + void unregisterEventNotifier(QWinEventNotifier *notifier); + void activateEventNotifiers(); + + void wakeUp(); + void interrupt(); + void flush(); + + void startingUp(); + void closingDown(); + + bool event(QEvent *e); + +private: + friend LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp); + friend LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int, WPARAM, LPARAM); +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_WIN_P_H diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp new file mode 100644 index 0000000000..d213b0e968 --- /dev/null +++ b/src/corelib/kernel/qeventloop.cpp @@ -0,0 +1,329 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qeventloop.h" + +#include "qabstracteventdispatcher.h" +#include "qcoreapplication.h" +#include "qelapsedtimer.h" + +#include "qobject_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QEventLoopPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QEventLoop) +public: + inline QEventLoopPrivate() + : exit(true), inExec(false), returnCode(-1) + { } + bool exit, inExec; + int returnCode; +}; + +/*! + \class QEventLoop + \brief The QEventLoop class provides a means of entering and leaving an event loop. + + At any time, you can create a QEventLoop object and call exec() + on it to start a local event loop. From within the event loop, + calling exit() will force exec() to return. + + \sa QAbstractEventDispatcher +*/ + +/*! + \enum QEventLoop::ProcessEventsFlag + + This enum controls the types of events processed by the + processEvents() functions. + + \value AllEvents All events. Note that + \l{QEvent::DeferredDelete}{DeferredDelete} events are processed + specially. See QObject::deleteLater() for more details. + + \value ExcludeUserInputEvents Do not process user input events, + such as ButtonPress and KeyPress. Note that the events are not + discarded; they will be delivered the next time processEvents() is + called without the ExcludeUserInputEvents flag. + + \value ExcludeSocketNotifiers Do not process socket notifier + events. Note that the events are not discarded; they will be + delivered the next time processEvents() is called without the + ExcludeSocketNotifiers flag. + + \value WaitForMoreEvents Wait for events if no pending events are + available. + + \omitvalue X11ExcludeTimers + \omitvalue ExcludeUserInput + \omitvalue WaitForMore + \omitvalue EventLoopExec + \omitvalue DialogExec + \value DeferredDeletion deprecated - do not use. + + \sa processEvents() +*/ + +/*! + Constructs an event loop object with the given \a parent. +*/ +QEventLoop::QEventLoop(QObject *parent) + : QObject(*new QEventLoopPrivate, parent) +{ + Q_D(QEventLoop); + if (!QCoreApplication::instance()) { + qWarning("QEventLoop: Cannot be used without QApplication"); + } else if (!d->threadData->eventDispatcher) { + QThreadPrivate::createEventDispatcher(d->threadData); + } +} + +/*! + Destroys the event loop object. +*/ +QEventLoop::~QEventLoop() +{ } + + +/*! + Processes pending events that match \a flags until there are no + more events to process. Returns true if pending events were handled; + otherwise returns false. + + This function is especially useful if you have a long running + operation and want to show its progress without allowing user + input; i.e. by using the \l ExcludeUserInputEvents flag. + + This function is simply a wrapper for + QAbstractEventDispatcher::processEvents(). See the documentation + for that function for details. +*/ +bool QEventLoop::processEvents(ProcessEventsFlags flags) +{ + Q_D(QEventLoop); + if (!d->threadData->eventDispatcher) + return false; + if (flags & DeferredDeletion) + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + return d->threadData->eventDispatcher->processEvents(flags); +} + +/*! + Enters the main event loop and waits until exit() is called. + Returns the value that was passed to exit(). + + If \a flags are specified, only events of the types allowed by + the \a flags will be processed. + + It is necessary to call this function to start event handling. The + main event loop receives events from the window system and + dispatches these to the application widgets. + + Generally speaking, no user interaction can take place before + calling exec(). As a special case, modal widgets like QMessageBox + can be used before calling exec(), because modal widgets + use their own local event loop. + + To make your application perform idle processing (i.e. executing a + special function whenever there are no pending events), use a + QTimer with 0 timeout. More sophisticated idle processing schemes + can be achieved using processEvents(). + + \sa QApplication::quit(), exit(), processEvents() +*/ +int QEventLoop::exec(ProcessEventsFlags flags) +{ + Q_D(QEventLoop); + //we need to protect from race condition with QThread::exit + QMutexLocker locker(&static_cast(QObjectPrivate::get(d->threadData->thread))->mutex); + if (d->threadData->quitNow) + return -1; + + if (d->inExec) { + qWarning("QEventLoop::exec: instance %p has already called exec()", this); + return -1; + } + d->inExec = true; + d->exit = false; + ++d->threadData->loopLevel; + d->threadData->eventLoops.push(this); + locker.unlock(); + + // remove posted quit events when entering a new event loop + QCoreApplication *app = QCoreApplication::instance(); + if (app && app->thread() == thread()) + QCoreApplication::removePostedEvents(app, QEvent::Quit); + +#if defined(QT_NO_EXCEPTIONS) + while (!d->exit) + processEvents(flags | WaitForMoreEvents | EventLoopExec); +#else + try { + while (!d->exit) + processEvents(flags | WaitForMoreEvents | EventLoopExec); + } catch (...) { + qWarning("Qt has caught an exception thrown from an event handler. Throwing\n" + "exceptions from an event handler is not supported in Qt. You must\n" + "reimplement QApplication::notify() and catch all exceptions there.\n"); + + // copied from below + locker.relock(); + QEventLoop *eventLoop = d->threadData->eventLoops.pop(); + Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error"); + Q_UNUSED(eventLoop); // --release warning + d->inExec = false; + --d->threadData->loopLevel; + + throw; + } +#endif + + // copied above + locker.relock(); + QEventLoop *eventLoop = d->threadData->eventLoops.pop(); + Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error"); + Q_UNUSED(eventLoop); // --release warning + d->inExec = false; + --d->threadData->loopLevel; + + return d->returnCode; +} + +/*! + Process pending events that match \a flags for a maximum of \a + maxTime milliseconds, or until there are no more events to + process, whichever is shorter. + This function is especially useful if you have a long running + operation and want to show its progress without allowing user + input, i.e. by using the \l ExcludeUserInputEvents flag. + + \bold{Notes:} + \list + \o This function does not process events continuously; it + returns after all available events are processed. + \o Specifying the \l WaitForMoreEvents flag makes no sense + and will be ignored. + \endlist +*/ +void QEventLoop::processEvents(ProcessEventsFlags flags, int maxTime) +{ + Q_D(QEventLoop); + if (!d->threadData->eventDispatcher) + return; + + QElapsedTimer start; + start.start(); + if (flags & DeferredDeletion) + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + while (processEvents(flags & ~WaitForMoreEvents)) { + if (start.elapsed() > maxTime) + break; + if (flags & DeferredDeletion) + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } +} + +/*! + Tells the event loop to exit with a return code. + + After this function has been called, the event loop returns from + the call to exec(). The exec() function returns \a returnCode. + + By convention, a \a returnCode of 0 means success, and any non-zero + value indicates an error. + + Note that unlike the C library function of the same name, this + function \e does return to the caller -- it is event processing that + stops. + + \sa QCoreApplication::quit(), quit(), exec() +*/ +void QEventLoop::exit(int returnCode) +{ + Q_D(QEventLoop); + if (!d->threadData->eventDispatcher) + return; + + d->returnCode = returnCode; + d->exit = true; + d->threadData->eventDispatcher->interrupt(); +} + +/*! + Returns true if the event loop is running; otherwise returns + false. The event loop is considered running from the time when + exec() is called until exit() is called. + + \sa exec() exit() + */ +bool QEventLoop::isRunning() const +{ + Q_D(const QEventLoop); + return !d->exit; +} + +/*! + Wakes up the event loop. + + \sa QAbstractEventDispatcher::wakeUp() +*/ +void QEventLoop::wakeUp() +{ + Q_D(QEventLoop); + if (!d->threadData->eventDispatcher) + return; + d->threadData->eventDispatcher->wakeUp(); +} + +/*! + Tells the event loop to exit normally. + + Same as exit(0). + + \sa QCoreApplication::quit(), exit() +*/ +void QEventLoop::quit() +{ exit(0); } + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qeventloop.h b/src/corelib/kernel/qeventloop.h new file mode 100644 index 0000000000..c0eae714a4 --- /dev/null +++ b/src/corelib/kernel/qeventloop.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QEVENTLOOP_H +#define QEVENTLOOP_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEventLoopPrivate; + +class Q_CORE_EXPORT QEventLoop : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QEventLoop) + +public: + explicit QEventLoop(QObject *parent = 0); + ~QEventLoop(); + + enum ProcessEventsFlag { + AllEvents = 0x00, + ExcludeUserInputEvents = 0x01, + ExcludeSocketNotifiers = 0x02, + WaitForMoreEvents = 0x04, +#ifdef QT3_SUPPORT + ExcludeUserInput = ExcludeUserInputEvents, + WaitForMore = WaitForMoreEvents, +#endif + X11ExcludeTimers = 0x08 +#ifdef QT_DEPRECATED + , DeferredDeletion = 0x10 +#endif + , EventLoopExec = 0x20 + , DialogExec = 0x40 + }; + Q_DECLARE_FLAGS(ProcessEventsFlags, ProcessEventsFlag) + + bool processEvents(ProcessEventsFlags flags = AllEvents); + void processEvents(ProcessEventsFlags flags, int maximumTime); + + int exec(ProcessEventsFlags flags = AllEvents); + void exit(int returnCode = 0); + bool isRunning() const; + + void wakeUp(); + +public Q_SLOTS: + void quit(); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QEventLoop::ProcessEventsFlags) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QEVENTLOOP_H diff --git a/src/corelib/kernel/qfunctions_nacl.cpp b/src/corelib/kernel/qfunctions_nacl.cpp new file mode 100644 index 0000000000..f3d85ef300 --- /dev/null +++ b/src/corelib/kernel/qfunctions_nacl.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qfunctions_nacl.h" +#include +#include + +/* + The purpose of this file is to stub out certain functions + that are not provided by the Native Client SDK. This is + done as an alterative to sprinkling the Qt sources with + NACL ifdefs. + + There are two main classes of functions: + + - Functions that are called but can have no effect: + For these we simply give an empty implementation + + - Functions that are referenced in the source code, but + is not/must not be called at run-time: + These we either leave undefined or implement with a + qFatal. + + This is a work in progress. +*/ + +extern "C" { + +void pthread_cleanup_push(void (*)(void *), void *) +{ + +} + +void pthread_cleanup_pop(int) +{ + +} + +int pthread_setcancelstate(int, int *) +{ + return 0; +} + +int pthread_setcanceltype(int, int *) +{ + return 0; +} + +void pthread_testcancel(void) +{ + +} + + +int pthread_cancel(pthread_t) +{ + return 0; +} + +int pthread_attr_setinheritsched(pthread_attr_t *,int) +{ + return 0; +} + + +int pthread_attr_getinheritsched(const pthread_attr_t *, int *) +{ + return 0; +} + +// event dispatcher, select +//struct fd_set; +//struct timeval; + +int fcntl(int, int, ...) +{ + return 0; +} + +int sigaction(int, const struct sigaction *, struct sigaction *) +{ + return 0; +} + +int open(const char *, int, ...) +{ + return 0; +} + +int open64(const char *, int, ...) +{ + return 0; +} + +int access(const char *, int) +{ + return 0; +} + +typedef long off64_t; +off64_t ftello64(void *) +{ + qFatal("ftello64 called"); + return 0; +} + +off64_t lseek64(int, off_t, int) +{ + qFatal("lseek64 called"); + return 0; +} + +} // Extern C + +int select(int, fd_set *, fd_set *, fd_set *, struct timeval *) +{ + return 0; +} diff --git a/src/corelib/kernel/qfunctions_nacl.h b/src/corelib/kernel/qfunctions_nacl.h new file mode 100644 index 0000000000..db36ad5bfd --- /dev/null +++ b/src/corelib/kernel/qfunctions_nacl.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QNACLUNIMPLEMENTED_H +#define QNACLUNIMPLEMENTED_H + +#ifdef Q_OS_NACL + +#include + +// pthread +#include +#define PTHREAD_CANCEL_DISABLE 1 +#define PTHREAD_CANCEL_ENABLE 2 +#define PTHREAD_INHERIT_SCHED 3 + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +extern "C" { + +void pthread_cleanup_push(void (*handler)(void *), void *arg); +void pthread_cleanup_pop(int execute); + +int pthread_setcancelstate(int state, int *oldstate); +int pthread_setcanceltype(int type, int *oldtype); +void pthread_testcancel(void); +int pthread_cancel(pthread_t thread); + +int pthread_attr_setinheritsched(pthread_attr_t *attr, + int inheritsched); +int pthread_attr_getinheritsched(const pthread_attr_t *attr, + int *inheritsched); + +// event dispatcher, select +//struct fd_set; +//struct timeval; +int fcntl(int fildes, int cmd, ...); +int sigaction(int sig, const struct sigaction * act, struct sigaction * oact); + +typedef long off64_t; +off64_t ftello64(void *stream); +off64_t lseek64(int fildes, off_t offset, int whence); +int open64(const char *path, int oflag, ...); + +} + +int select(int nfds, fd_set * readfds, fd_set * writefds, fd_set * errorfds, struct timeval * timeout); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //Q_OS_NACL + +#endif //QNACLUNIMPLEMENTED_H diff --git a/src/corelib/kernel/qfunctions_p.h b/src/corelib/kernel/qfunctions_p.h new file mode 100644 index 0000000000..d27fec3a0b --- /dev/null +++ b/src/corelib/kernel/qfunctions_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qfunctions_*. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +#ifndef QFUNCTIONS_P_H +#define QFUNCTIONS_P_H + +#include + +#if defined(Q_OS_WINCE) +# include "QtCore/qfunctions_wince.h" +#elif defined(Q_OS_VXWORKS) +# include "QtCore/qfunctions_vxworks.h" +#elif defined(Q_OS_NACL) +# include "QtCore/qfunctions_nacl.h" +#endif + +#ifdef Q_CC_RVCT +// rvct doesn't see static operators when using our qalgorithms +# define Q_STATIC_GLOBAL_OPERATOR inline +# define Q_STATIC_GLOBAL_INLINE_OPERATOR inline +#else +# define Q_STATIC_GLOBAL_OPERATOR static +# define Q_STATIC_GLOBAL_INLINE_OPERATOR static inline +#endif + +QT_BEGIN_HEADER +QT_END_HEADER + +#endif + diff --git a/src/corelib/kernel/qfunctions_vxworks.cpp b/src/corelib/kernel/qfunctions_vxworks.cpp new file mode 100644 index 0000000000..1678ab67a9 --- /dev/null +++ b/src/corelib/kernel/qfunctions_vxworks.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qglobal.h" + +#ifdef Q_OS_VXWORKS + +#include "qplatformdefs.h" +#include "qfunctions_vxworks.h" + +#include +#include +#include + +QT_USE_NAMESPACE + +#ifdef __cplusplus +extern "C" { +#endif + +// no lfind() - used by the TIF image format +void *lfind(const void* key, const void* base, size_t* elements, size_t size, + int (*compare)(const void*, const void*)) +{ + const char* current = (char*) base; + const char* const end = (char*) (current + (*elements) * size); + while (current != end) { + if (compare(current, key) == 0) + return (void*)current; + current += size; + } + return 0; +} + + +// no rand_r(), but rand() +// NOTE: this implementation is wrong for multi threaded applications, +// but there is no way to get it right on VxWorks (in kernel mode) +int rand_r(unsigned int * /*seedp*/) +{ + return rand(); +} + +// no usleep() support +int usleep(unsigned int usec) +{ + div_t dt = div(usec, 1000000); + struct timespec ts = { dt.quot, dt.rem * 1000 }; + + return nanosleep(&ts, 0); +} + + +// gettimeofday() is declared, but is missing from the library +// It IS however defined in the Curtis-Wright X11 libraries, so +// we have to make the symbol 'weak' +#if defined(Q_CC_DIAB) +# pragma weak gettimeofday +#endif +int gettimeofday(struct timeval *tv, void /*struct timezone*/ *) +{ + // the compiler will optimize this and will only use one code path + if (sizeof(struct timeval) == sizeof(struct timespec)) { + int res = clock_gettime(CLOCK_REALTIME, (struct timespec *) tv); + if (!res) + tv->tv_usec /= 1000; + return res; + } else { + struct timespec ts; + + int res = clock_gettime(CLOCK_REALTIME, &ts); + if (!res) { + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; + } + return res; + } +} + +// neither getpagesize() or sysconf(_SC_PAGESIZE) are available +int getpagesize() +{ + return vmPageSizeGet(); +} + +// symlinks are not supported (lstat is now just a call to stat - see qplatformdefs.h) +int symlink(const char *, const char *) +{ + errno = EIO; + return -1; +} + +ssize_t readlink(const char *, char *, size_t) +{ + errno = EIO; + return -1; +} + +// there's no truncate(), but ftruncate() support... +int truncate(const char *path, off_t length) +{ + int fd = open(path, O_WRONLY, 00777); + if (fd >= 0) { + int res = ftruncate(fd, length); + int en = errno; + close(fd); + errno = en; + return res; + } + // errno is already set by open + return -1; +} + + + +// VxWorks doesn't know about passwd & friends. +// in order to avoid patching the unix fs path everywhere +// we introduce some dummy functions that simulate a single +// 'root' user on the system. + +uid_t getuid() +{ + return 0; +} + +gid_t getgid() +{ + return 0; +} + +uid_t geteuid() +{ + return 0; +} + +struct passwd *getpwuid(uid_t uid) +{ + static struct passwd pwbuf = { "root", 0, 0, 0, 0, 0, 0 }; + + if (uid == 0) { + return &pwbuf; + } else { + errno = ENOENT; + return 0; + } +} + +struct group *getgrgid(gid_t gid) +{ + static struct group grbuf = { "root", 0, 0, 0 }; + + if (gid == 0) { + return &grbuf; + } else { + errno = ENOENT; + return 0; + } +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // Q_OS_VXWORKS diff --git a/src/corelib/kernel/qfunctions_vxworks.h b/src/corelib/kernel/qfunctions_vxworks.h new file mode 100644 index 0000000000..78a37210a0 --- /dev/null +++ b/src/corelib/kernel/qfunctions_vxworks.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QFUNCTIONS_VXWORKS_H +#define QFUNCTIONS_VXWORKS_H +#ifdef Q_OS_VXWORKS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef QT_NO_IPV6IFNAME +#include +#endif + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifdef QT_BUILD_CORE_LIB +QT_MODULE(Core) +#endif + +QT_END_NAMESPACE +QT_END_HEADER + + +#ifndef RTLD_LOCAL +#define RTLD_LOCAL 0 +#endif + +#ifndef NSIG +#define NSIG _NSIGS +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// isascii is missing (sometimes!!) +#ifndef isascii +inline int isascii(int c) { return (c & 0x7f); } +#endif + +// no lfind() - used by the TIF image format +void *lfind(const void* key, const void* base, size_t* elements, size_t size, + int (*compare)(const void*, const void*)); + +// no rand_r(), but rand() +// NOTE: this implementation is wrong for multi threaded applications, +// but there is no way to get it right on VxWorks (in kernel mode) +int rand_r(unsigned int * /*seedp*/); + +// no usleep() support +int usleep(unsigned int); + +// gettimeofday() is declared, but is missing from the library. +// It IS however defined in the Curtis-Wright X11 libraries, so +// we have to make the symbol 'weak' +int gettimeofday(struct timeval *tv, void /*struct timezone*/ *) __attribute__((weak)); + +// neither getpagesize() or sysconf(_SC_PAGESIZE) are available +int getpagesize(); + +// symlinks are not supported (lstat is now just a call to stat - see qplatformdefs.h) +int symlink(const char *, const char *); +ssize_t readlink(const char *, char *, size_t); + +// there's no truncate(), but ftruncate() support... +int truncate(const char *path, off_t length); + +// VxWorks doesn't know about passwd & friends. +// in order to avoid patching the unix fs path everywhere +// we introduce some dummy functions that simulate a single +// 'root' user on the system. + +uid_t getuid(); +gid_t getgid(); +uid_t geteuid(); + +struct passwd { + char *pw_name; /* user name */ + char *pw_passwd; /* user password */ + uid_t pw_uid; /* user ID */ + gid_t pw_gid; /* group ID */ + char *pw_gecos; /* real name */ + char *pw_dir; /* home directory */ + char *pw_shell; /* shell program */ +}; + +struct group { + char *gr_name; /* group name */ + char *gr_passwd; /* group password */ + gid_t gr_gid; /* group ID */ + char **gr_mem; /* group members */ +}; + +struct passwd *getpwuid(uid_t uid); +struct group *getgrgid(gid_t gid); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // Q_OS_VXWORKS +#endif // QFUNCTIONS_VXWORKS_H diff --git a/src/corelib/kernel/qfunctions_wince.cpp b/src/corelib/kernel/qfunctions_wince.cpp new file mode 100644 index 0000000000..2de34c3bdc --- /dev/null +++ b/src/corelib/kernel/qfunctions_wince.cpp @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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$ +** +****************************************************************************/ +#ifdef _WIN32_WCE //Q_OS_WINCE + +#include +#include +#include +#include +#include + +#include "qplatformdefs.h" +#include "qfunctions_wince.h" +#include "qstring.h" +#include "qbytearray.h" +#include "qhash.h" + +QT_USE_NAMESPACE + +#ifdef __cplusplus +extern "C" { +#endif + +wchar_t* CEPrivConvCharToWide(const char* string) +{ + size_t length = strlen(string); + wchar_t* wString = new wchar_t[length +1]; + for (unsigned int i = 0; i < (length +1); i++) + wString[i] = string[i]; + return wString; +} + +// Time ------------------------------------------------------------- +time_t qt_wince_ftToTime_t( const FILETIME ft ) +{ + ULARGE_INTEGER li; + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + + // 100-nanosec to seconds + li.QuadPart /= 10000000; + + // FILETIME is from 1601-01-01 T 00:00:00 + // time_t is from 1970-01-01 T 00:00:00 + // 1970 - 1601 = 369 year (89 leap years) + // + // ((369y*365d) + 89d) *24h *60min *60sec + // = 11644473600 seconds + li.QuadPart -= 11644473600; + return li.LowPart; +} + +FILETIME qt_wince_time_tToFt( time_t tt ) +{ + ULARGE_INTEGER li; + li.QuadPart = tt; + li.QuadPart += 11644473600; + li.QuadPart *= 10000000; + + FILETIME ft; + ft.dwLowDateTime = li.LowPart; + ft.dwHighDateTime = li.HighPart; + return ft; +} + +// File I/O --------------------------------------------------------- +int errno = 0; + +int qt_wince__getdrive( void ) +{ + return 1; +} + +int qt_wince__waccess( const wchar_t *path, int pmode ) +{ + DWORD res = GetFileAttributes( path ); + if ( 0xFFFFFFFF == res ) + return -1; + + if ( (pmode & W_OK) && (res & FILE_ATTRIBUTE_READONLY) ) + return -1; + + if ( (pmode & X_OK) && !(res & FILE_ATTRIBUTE_DIRECTORY) ) { + QString file = QString::fromWCharArray(path); + if ( !(file.endsWith(QString::fromLatin1(".exe")) || + file.endsWith(QString::fromLatin1(".com"))) ) + return -1; + } + + return 0; +} + +int qt_wince_open( const char *filename, int oflag, int pmode ) +{ + QString fn( QString::fromLatin1(filename) ); + return _wopen( (wchar_t*)fn.utf16(), oflag, pmode ); +} + +int qt_wince__wopen( const wchar_t *filename, int oflag, int /*pmode*/ ) +{ + wchar_t *flag; + + if ( oflag & _O_APPEND ) { + if ( oflag & _O_WRONLY ) { + flag = L"a"; + } else if ( oflag & _O_RDWR ) { + flag = L"a+"; + } + } else if (oflag & _O_BINARY) { + if ( oflag & _O_WRONLY ) { + flag = L"wb"; + } else if ( oflag & _O_RDWR ) { + flag = L"w+b"; // slightly different from "r+" where the file must exist + } else if ( oflag & _O_RDONLY ) { + flag = L"rb"; + } else { + flag = L"b"; + } + } else { + if ( oflag & _O_WRONLY ) { + flag = L"wt"; + } else if ( oflag & _O_RDWR ) { + flag = L"w+t"; // slightly different from "r+" where the file must exist + } else if ( oflag & _O_RDONLY ) { + flag = L"rt"; + } else { + flag = L"t"; + } + } + + int retval = (int)_wfopen( filename, flag ); + return (retval == NULL) ? -1 : retval; +} + +long qt_wince__lseek( int handle, long offset, int origin ) +{ + return fseek( (FILE*)handle, offset, origin ); +} + +int qt_wince__read( int handle, void *buffer, unsigned int count ) +{ + return fread( buffer, 1, count, (FILE*)handle ); +} + +int qt_wince__write( int handle, const void *buffer, unsigned int count ) +{ + return fwrite( buffer, 1, count, (FILE*)handle ); +} + +int qt_wince__close( int handle ) +{ + if (!handle) + return 0; + return fclose( (FILE*)handle ); +} + +FILE *qt_wince__fdopen(int handle, const char* /*mode*/) +{ + return (FILE*)handle; +} + +FILE *qt_wince_fdopen( int handle, const char* /*mode*/ ) +{ + return (FILE*)handle; +} + +void qt_wince_rewind( FILE *stream ) +{ + fseek( stream, 0L, SEEK_SET ); +} + +int qt_wince___fileno(FILE *f) +{ + return (int) _fileno(f); +} + +FILE *qt_wince_tmpfile( void ) +{ + static long i = 0; + char name[16]; + sprintf( name, "tmp%i", i++ ); + return fopen( name, "r+" ); +} + +int qt_wince__mkdir(const char *dirname) +{ + return CreateDirectory(reinterpret_cast (QString(QString::fromLatin1(dirname)).utf16()), 0) ? 0 : -1; +} + +int qt_wince__rmdir(const char *dirname) +{ + return RemoveDirectory(reinterpret_cast (QString::fromLatin1(dirname).utf16())) ? 0 : -1; +} + +int qt_wince__access( const char *path, int pmode ) +{ + return _waccess(reinterpret_cast (QString::fromLatin1(path).utf16()),pmode); +} + +int qt_wince__rename( const char *oldname, const char *newname ) +{ + return !MoveFile(reinterpret_cast (QString::fromLatin1(oldname).utf16()), reinterpret_cast (QString::fromLatin1(newname).utf16())); +} + +int qt_wince__remove( const char *name ) +{ + return !DeleteFile(reinterpret_cast (QString::fromLatin1(name).utf16())); +} + +int qt_wince_stat( const char *path, struct stat *buffer ) +{ + WIN32_FIND_DATA finfo; + HANDLE ff = FindFirstFile( reinterpret_cast (QString::fromLatin1(path).utf16()), &finfo ); + + if ( ff == INVALID_HANDLE_VALUE ) + return -1; + + buffer->st_ctime = qt_wince_ftToTime_t( finfo.ftCreationTime ); + buffer->st_atime = qt_wince_ftToTime_t( finfo.ftLastAccessTime ); + buffer->st_mtime = qt_wince_ftToTime_t( finfo.ftLastWriteTime ); + buffer->st_nlink = 0; + buffer->st_size = finfo.nFileSizeLow; // ### missing high! + buffer->st_mode = (finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? _S_IFDIR : _S_IFREG; + buffer->st_mode |= (finfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? _O_RDONLY : _O_RDWR; + return (FindClose(ff) == 0); +} + +int qt_wince__fstat( int handle, struct stat *buffer) +{ + BY_HANDLE_FILE_INFORMATION fInfo; + BOOL res = GetFileInformationByHandle((HANDLE)handle, &fInfo); + + buffer->st_ctime = qt_wince_ftToTime_t( fInfo.ftCreationTime ); + buffer->st_atime = qt_wince_ftToTime_t( fInfo.ftLastAccessTime ); + buffer->st_mtime = qt_wince_ftToTime_t( fInfo.ftLastWriteTime ); + buffer->st_nlink = 0; + buffer->st_size = fInfo.nFileSizeLow; // ### missing high! + buffer->st_mode = (fInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? _S_IFDIR : _S_IFREG; + buffer->st_mode |= (fInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? _O_RDONLY : _O_RDWR; + return (res == 0); +} + +int qt_wince_SetErrorMode(int newValue) +{ + static int oldValue; + int result = oldValue; + oldValue = newValue; + return result; +} + +bool qt_wince__chmod(const char *file, int mode) +{ + return _wchmod( reinterpret_cast (QString::fromLatin1(file).utf16()), mode); +} + +bool qt_wince__wchmod(const wchar_t *file, int mode) +{ + BOOL success = FALSE; + // ### Does not work properly, what about just adding one property? + if(mode&_S_IWRITE) { + success = SetFileAttributes(file, FILE_ATTRIBUTE_NORMAL); + } else if((mode&_S_IREAD) && !(mode&_S_IWRITE)) { + success = SetFileAttributes(file, FILE_ATTRIBUTE_READONLY); + } + return success ? 0 : -1; +} + +HANDLE qt_wince_CreateFileA(LPCSTR filename, DWORD access, DWORD share, LPSECURITY_ATTRIBUTES attr, DWORD dispo, DWORD flags, HANDLE tempFile) +{ + return CreateFileW( reinterpret_cast(QString::fromLatin1(filename).utf16()), access, share, attr, dispo, flags, tempFile); +} + +// Graphics --------------------------------------------------------- +BOOL qt_wince_SetWindowOrgEx( HDC /*hdc*/, int /*X*/, int /*Y*/, LPPOINT /*lpPoint*/) { + return TRUE; +} + +// Threading -------------------------------------------------------- +HANDLE qt_wince__beginthread(void( *start_address )( void * ), unsigned stack_size, void *arglist) +{ + unsigned initflag = 0; + if (stack_size > 0) + initflag |= STACK_SIZE_PARAM_IS_A_RESERVATION; + return CreateThread(NULL, stack_size, (LPTHREAD_START_ROUTINE)start_address, arglist, initflag, NULL); +} + +unsigned long qt_wince__beginthreadex( void *security, + unsigned stack_size, + unsigned (__stdcall *start_address)(void *), + void *arglist, + unsigned initflag, + unsigned *thrdaddr) +{ + if (stack_size > 0) + initflag |= STACK_SIZE_PARAM_IS_A_RESERVATION; + return (unsigned long) + CreateThread( (LPSECURITY_ATTRIBUTES)security, + (DWORD)stack_size, + (LPTHREAD_START_ROUTINE)start_address, + (LPVOID)arglist, + (DWORD)initflag | CREATE_SUSPENDED, + (LPDWORD)thrdaddr); +} + +void qt_wince__endthreadex(unsigned nExitCode) { + ExitThread((DWORD)nExitCode); +} + +void *qt_wince_bsearch(const void *key, + const void *base, + size_t num, + size_t size, + int (__cdecl *compare)(const void *, const void *)) +{ + size_t low = 0; + size_t high = num - 1; + while (low <= high) { + size_t mid = (low + high) >> 1; + int c = compare(key, (char*)base + mid * size); + if (c < 0) { + if (!mid) + break; + high = mid - 1; + } else if (c > 0) + low = mid + 1; + else + return (char*) base + mid * size; + } + return 0; +} + +void *lfind(const void* key, const void* base, size_t* elements, size_t size, + int (__cdecl *compare)(const void*, const void*)) +{ + const char* current = (char*) base; + const char* const end = (char*) (current + (*elements) * size); + while (current != end) { + if (compare(current, key) == 0) + return (void*)current; + current += size; + } + return 0; +} + +DWORD qt_wince_GetThreadLocale(void) +{ + return GetUserDefaultLCID(); +} + +void *qt_wince_calloc( size_t num, size_t size ) +{ + void *ptr = malloc( num * size ); + if( ptr ) + memset( ptr, 0, num * size ); + return ptr; +} + +// _getpid is currently only used for creating a temporary filename +int qt_wince__getpid() +{ + return qAbs((int)GetCurrentProcessId()); +} + +#ifdef __cplusplus +} // extern "C" +#endif +// Environment ------------------------------------------------------ +inline QHash& qt_app_environment() +{ + static QHash internalEnvironment; + return internalEnvironment; +} + +errno_t qt_wince_getenv_s(size_t* sizeNeeded, char* buffer, size_t bufferSize, const char* varName) +{ + if (!sizeNeeded) + return EINVAL; + + if (!qt_app_environment().contains(varName)) { + if (buffer) + buffer[0] = '\0'; + return ENOENT; + } + + QByteArray value = qt_app_environment().value(varName); + if (!value.endsWith('\0')) // win32 guarantees terminated string + value.append('\0'); + + if (bufferSize < (size_t)value.size()) { + *sizeNeeded = value.size(); + return 0; + } + + strcpy(buffer, value.constData()); + return 0; +} + +errno_t qt_wince__putenv_s(const char* varName, const char* value) +{ + QByteArray input = value; + if (input.isEmpty()) { + if (qt_app_environment().contains(varName)) + qt_app_environment().remove(varName); + } else { + // win32 guarantees terminated string + if (!input.endsWith('\0')) + input.append('\0'); + qt_app_environment()[varName] = input; + } + + return 0; +} + +#endif // Q_OS_WINCE diff --git a/src/corelib/kernel/qfunctions_wince.h b/src/corelib/kernel/qfunctions_wince.h new file mode 100644 index 0000000000..fffe407139 --- /dev/null +++ b/src/corelib/kernel/qfunctions_wince.h @@ -0,0 +1,397 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QFUNCTIONS_WCE_H +#define QFUNCTIONS_WCE_H +#ifdef Q_OS_WINCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#ifdef QT_BUILD_CORE_LIB +QT_MODULE(Core) +#endif + +QT_END_NAMESPACE +QT_END_HEADER + + +// The standard SDK misses this define... +#define _control87 _controlfp + +#if !defined __cplusplus +#define bool int +#define true 1 +#define false 0 +#endif + +// Environment ------------------------------------------------------ +errno_t qt_wince_getenv_s(size_t*, char*, size_t, const char*); +errno_t qt_wince__putenv_s(const char*, const char*); + +#ifdef __cplusplus // have this as tiff plugin is written in C +extern "C" { +#endif + +#if !defined(NO_ERRNO_H) +#define NO_ERRNO_H +#endif + +// Environment ------------------------------------------------------ +int qt_wince__getpid(void); + + +// Time ------------------------------------------------------------- +#ifndef _TM_DEFINED +#define _TM_DEFINED +struct tm { + int tm_sec; /* seconds after the minute - [0,59] */ + int tm_min; /* minutes after the hour - [0,59] */ + int tm_hour; /* hours since midnight - [0,23] */ + int tm_mday; /* day of the month - [1,31] */ + int tm_mon; /* months since January - [0,11] */ + int tm_year; /* years since 1900 */ + int tm_wday; /* days since Sunday - [0,6] */ + int tm_yday; /* days since January 1 - [0,365] */ + int tm_isdst; /* daylight savings time flag */ +}; +#endif // _TM_DEFINED + +FILETIME qt_wince_time_tToFt( time_t tt ); + +// File I/O --------------------------------------------------------- +#define _O_RDONLY 0x0001 +#define _O_RDWR 0x0002 +#define _O_WRONLY 0x0004 +#define _O_CREAT 0x0008 +#define _O_TRUNC 0x0010 +#define _O_APPEND 0x0020 +#define _O_EXCL 0x0040 + +#define O_RDONLY _O_RDONLY +#define O_RDWR _O_RDWR +#define O_WRONLY _O_WRONLY +#define O_CREAT _O_CREAT +#define O_TRUNC _O_TRUNC +#define O_APPEND _O_APPEND +#define O_EXCL _O_EXCL + +#define _S_IFMT 0x0600 +#define _S_IFDIR 0x0200 +#define _S_IFCHR 0x0100 +#define _S_IFREG 0x0400 +#define _S_IREAD 0x0010 +#define _S_IWRITE 0x0008 + +#define S_IFMT _S_IFMT +#define S_IFDIR _S_IFDIR +#define S_IFCHR _S_IFCHR +#define S_IFREG _S_IFREG +#define S_IREAD _S_IREAD +#define S_IWRITE _S_IWRITE + +#ifndef _IOFBF +#define _IOFBF 0x0000 +#endif + +#ifndef _IOLBF +#define _IOLBF 0x0040 +#endif + +#ifndef _IONBF +#define _IONBF 0x0004 +#endif + +// Regular Berkeley error constants +#ifndef _STAT_DEFINED +#define _STAT_DEFINED +struct stat +{ + int st_mode; + int st_size; + int st_nlink; + time_t st_mtime; + time_t st_atime; + time_t st_ctime; +}; +#endif + +typedef int mode_t; +extern int errno; + +int qt_wince__getdrive( void ); +int qt_wince__waccess( const wchar_t *path, int pmode ); +int qt_wince__wopen( const wchar_t *filename, int oflag, int pmode ); +long qt_wince__lseek( int handle, long offset, int origin ); +int qt_wince__read( int handle, void *buffer, unsigned int count ); +int qt_wince__write( int handle, const void *buffer, unsigned int count ); +int qt_wince__close( int handle ); +FILE *qt_wince__fdopen(int handle, const char *mode); +FILE *qt_wince_fdopen(int handle, const char *mode); +void qt_wince_rewind( FILE *stream ); +int qt_wince___fileno(FILE *); +FILE *qt_wince_tmpfile( void ); + +int qt_wince__mkdir(const char *dirname); +int qt_wince__rmdir(const char *dirname); +int qt_wince__access( const char *path, int pmode ); +int qt_wince__rename( const char *oldname, const char *newname ); +int qt_wince__remove( const char *name ); +#ifdef __cplusplus +int qt_wince_open( const char *filename, int oflag, int pmode = 0 ); +#else +int qt_wince_open( const char *filename, int oflag, int pmode ); +#endif +int qt_wince_stat( const char *path, struct stat *buffer ); +int qt_wince__fstat( int handle, struct stat *buffer); + +#define SEM_FAILCRITICALERRORS 0x0001 +#define SEM_NOOPENFILEERRORBOX 0x0002 +int qt_wince_SetErrorMode(int); +#ifndef CoInitialize +#define CoInitialize(x) CoInitializeEx(x, COINIT_MULTITHREADED) +#endif + +bool qt_wince__chmod(const char *file, int mode); +bool qt_wince__wchmod(const wchar_t *file, int mode); + +#pragma warning(disable: 4273) +HANDLE qt_wince_CreateFileA(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE); + +// Printer ---------------------------------------------------------- +#define ETO_GLYPH_INDEX 0x0010 + +// Graphics --------------------------------------------------------- +#ifndef SM_CXCURSOR +# define SM_CXCURSOR 13 +#endif +#ifndef SM_CYCURSOR +# define SM_CYCURSOR 14 +#endif +BOOL qt_wince_SetWindowOrgEx( HDC hdc, int X, int Y, LPPOINT lpPoint ); + +// Other stuff ------------------------------------------------------ +#define MWMO_ALERTABLE 0x0002 +// ### not the real values +#define CREATE_NO_WINDOW 2 +#define CF_HDROP 15 + +void *qt_wince_calloc(size_t num, size_t size); +#if !defined(TLS_OUT_OF_INDEXES) +# define TLS_OUT_OF_INDEXES 0xffffffff +#endif +DWORD qt_wince_GetThreadLocale(void); + +HANDLE qt_wince__beginthread(void( *start_address )( void * ), unsigned stack_size, void *arglist); + +unsigned long qt_wince__beginthreadex( void *security, + unsigned stack_size, + unsigned (__stdcall *start_address)(void *), + void *arglist, + unsigned initflag, + unsigned *thrdaddr ); +void qt_wince__endthreadex(unsigned nExitCode); + + +// bsearch is needed for building the tiff plugin +// otherwise it could go into qguifunctions_wce +void *qt_wince_bsearch(const void *key, + const void *base, + size_t num, + size_t size, + int (__cdecl *compare)(const void *, const void *)); + +// Missing typedefs +#ifndef _TIME_T_DEFINED +typedef unsigned long time_t; +#define _TIME_T_DEFINED +#endif +typedef HANDLE HDROP; + +#ifndef WS_THICKFRAME +#define WS_THICKFRAME WS_DLGFRAME +#endif + +typedef UINT UWORD; + +// Missing definitions: not necessary equal to their Win32 values +// (the goal is to just have a clean compilation of MFC) +#define WS_MAXIMIZE 0 +#define WS_MINIMIZE 0 +#ifndef WS_EX_TOOLWINDOW +#define WS_EX_TOOLWINDOW 0 +#endif +#define WS_EX_NOPARENTNOTIFY 0 +#define WM_ENTERIDLE 0x0121 +#define WM_PRINT WM_PAINT +#define WM_NCCREATE (0x0081) +#define WM_PARENTNOTIFY 0 +#define WM_NCDESTROY (WM_APP-1) +#ifndef SW_RESTORE +#define SW_RESTORE (SW_SHOWNORMAL) +#endif +#define SW_NORMAL (SW_SHOWNORMAL) +#define WAIT_OBJECT_0 0x00000000L +#define DEFAULT_GUI_FONT SYSTEM_FONT +#ifndef SWP_NOREDRAW +#define SWP_NOREDRAW 0 +#endif +#define WSAGETSELECTEVENT(lParam) LOWORD(lParam) +#define HWND_TOPMOST ((HWND)-1) +#define HWND_NOTOPMOST ((HWND)-2) +#define PS_DOT 2 +#define PD_ALLPAGES 0 +#define PD_USEDEVMODECOPIES 0 +#define PD_NOSELECTION 0 +#define PD_HIDEPRINTTOFILE 0 +#define PD_NOPAGENUMS 0 +#define CF_METAFILEPICT 3 +#define MM_ANISOTROPIC 8 +#define KF_ALTDOWN 0x2000 +#define SPI_GETWORKAREA 48 + +#ifndef WM_SETCURSOR + #define WM_SETCURSOR 0x0020 + #define IDC_ARROW MAKEINTRESOURCE(32512) + #define IDC_IBEAM MAKEINTRESOURCE(32513) + #define IDC_WAIT MAKEINTRESOURCE(32514) + #define IDC_CROSS MAKEINTRESOURCE(32515) + #define IDC_UPARROW MAKEINTRESOURCE(32516) + #define IDC_SIZE MAKEINTRESOURCE(32646) + #define IDC_ICON MAKEINTRESOURCE(32512) + #define IDC_SIZENWSE MAKEINTRESOURCE(32642) + #define IDC_SIZENESW MAKEINTRESOURCE(32643) + #define IDC_SIZEWE MAKEINTRESOURCE(32644) + #define IDC_SIZENS MAKEINTRESOURCE(32645) + #define IDC_SIZEALL MAKEINTRESOURCE(32646) + #define IDC_NO MAKEINTRESOURCE(32648) + #define IDC_APPSTARTING MAKEINTRESOURCE(32650) + #define IDC_HELP MAKEINTRESOURCE(32651) + #define IDC_HAND MAKEINTRESOURCE(32649) +#endif + +#define GMEM_MOVEABLE LMEM_MOVEABLE +#define GPTR LPTR + +// WinCE: CESYSGEN prunes the following FRP defines, +// and INTERNET_TRANSFER_TYPE_ASCII breaks in wininet.h +#undef FTP_TRANSFER_TYPE_ASCII +#define FTP_TRANSFER_TYPE_ASCII 0x00000001 +#undef FTP_TRANSFER_TYPE_BINARY +#define FTP_TRANSFER_TYPE_BINARY 0x00000002 + +typedef DWORD OLE_COLOR; + +// Define the Windows Styles which are not defined by MS +#ifndef WS_POPUPWINDOW +#define WS_POPUPWINDOW WS_POPUP|WS_BORDER|WS_SYSMENU|WS_CAPTION +#endif + +#ifndef WS_OVERLAPPEDWINDOW +#define WS_OVERLAPPEDWINDOW WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX +#endif + +#ifndef WS_TILED +#define WS_TILED WS_OVERLAPPED +#endif + +#ifndef WS_TILEDWINDOW +#define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW +#endif + +#ifndef WS_EX_CAPTIONOKBTN +#define WS_EX_CAPTIONOKBTN 0x80000000L +#endif + +#ifndef WS_EX_NODRAG +#define WS_EX_NODRAG 0x40000000L +#endif + +// As Windows CE lacks some standard functions used in Qt, these got +// reimplented. Other projects do this as well and to not fill the +// global namespace with this implementation, prepend qt_wince* and use +// these versions inside of Qt. +// The other declarations available in this file are being used per +// define inside qplatformdefs.h of the corresponding WinCE mkspec. +#define getenv_s(a,b,c,d) qt_wince_getenv_s(a,b,c,d) +#define _putenv_s(a,b) qt_wince__putenv_s(a,b) +#define _getpid() qt_wince__getpid() +#define time_tToFt(a) qt_wince_time_tToFt(a) +#define _getdrive() qt_wince__getdrive() +#define _waccess(a,b) qt_wince__waccess(a,b) +#define _wopen(a,b,c) qt_wince__wopen(a,b,c) +#define _fdopen(a,b) qt_wince__fdopen(a,b) +#define fdopen(a,b) qt_wince_fdopen(a,b) +#define rewind(a) qt_wince_rewind(a) +#define tmpfile() qt_wince_tmpfile() +#define _rename(a,b) qt_wince__rename(a,b) +#define _remove(a) qt_wince__remove(a) +#define SetErrorMode(a) qt_wince_SetErrorMode(a) +#define _chmod(a,b) qt_wince__chmod(a,b) +#define _wchmod(a,b) qt_wince__wchmod(a,b) +#define CreateFileA(a,b,c,d,e,f,g) qt_wince_CreateFileA(a,b,c,d,e,f,g) +#define SetWindowOrgEx(a,b,c,d) qt_wince_SetWindowOrgEx(a,b,c,d) +#define calloc(a,b) qt_wince_calloc(a,b) +#define GetThreadLocale() qt_wince_GetThreadLocale() +#define _beginthread(a,b,c) qt_wince__beginthread(a,b,c) +#define _beginthreadex(a,b,c,d,e,f) qt_wince__beginthreadex(a,b,c,d,e,f) +#define _endthreadex(a) qt_wince__endthreadex(a) +#define bsearch(a,b,c,d,e) qt_wince_bsearch(a,b,c,d,e) + +#ifdef __cplusplus +} // Extern C. +#endif + +#endif // Q_OS_WINCE +#endif // QFUNCTIONS_WCE_H diff --git a/src/corelib/kernel/qmath.cpp b/src/corelib/kernel/qmath.cpp new file mode 100644 index 0000000000..783dbbfa37 --- /dev/null +++ b/src/corelib/kernel/qmath.cpp @@ -0,0 +1,305 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 + +QT_BEGIN_NAMESPACE + +const qreal qt_sine_table[QT_SINE_TABLE_SIZE] = { + qreal(0.0), + qreal(0.024541228522912288), + qreal(0.049067674327418015), + qreal(0.073564563599667426), + qreal(0.098017140329560604), + qreal(0.1224106751992162), + qreal(0.14673047445536175), + qreal(0.17096188876030122), + qreal(0.19509032201612825), + qreal(0.2191012401568698), + qreal(0.24298017990326387), + qreal(0.26671275747489837), + qreal(0.29028467725446233), + qreal(0.31368174039889152), + qreal(0.33688985339222005), + qreal(0.35989503653498811), + qreal(0.38268343236508978), + qreal(0.40524131400498986), + qreal(0.42755509343028208), + qreal(0.44961132965460654), + qreal(0.47139673682599764), + qreal(0.49289819222978404), + qreal(0.51410274419322166), + qreal(0.53499761988709715), + qreal(0.55557023301960218), + qreal(0.57580819141784534), + qreal(0.59569930449243336), + qreal(0.61523159058062682), + qreal(0.63439328416364549), + qreal(0.65317284295377676), + qreal(0.67155895484701833), + qreal(0.68954054473706683), + qreal(0.70710678118654746), + qreal(0.72424708295146689), + qreal(0.74095112535495911), + qreal(0.75720884650648446), + qreal(0.77301045336273699), + qreal(0.78834642762660623), + qreal(0.80320753148064483), + qreal(0.81758481315158371), + qreal(0.83146961230254524), + qreal(0.84485356524970701), + qreal(0.85772861000027212), + qreal(0.87008699110871135), + qreal(0.88192126434835494), + qreal(0.89322430119551532), + qreal(0.90398929312344334), + qreal(0.91420975570353069), + qreal(0.92387953251128674), + qreal(0.93299279883473885), + qreal(0.94154406518302081), + qreal(0.94952818059303667), + qreal(0.95694033573220894), + qreal(0.96377606579543984), + qreal(0.97003125319454397), + qreal(0.97570213003852857), + qreal(0.98078528040323043), + qreal(0.98527764238894122), + qreal(0.98917650996478101), + qreal(0.99247953459870997), + qreal(0.99518472667219682), + qreal(0.99729045667869021), + qreal(0.99879545620517241), + qreal(0.99969881869620425), + qreal(1.0), + qreal(0.99969881869620425), + qreal(0.99879545620517241), + qreal(0.99729045667869021), + qreal(0.99518472667219693), + qreal(0.99247953459870997), + qreal(0.98917650996478101), + qreal(0.98527764238894122), + qreal(0.98078528040323043), + qreal(0.97570213003852857), + qreal(0.97003125319454397), + qreal(0.96377606579543984), + qreal(0.95694033573220894), + qreal(0.94952818059303667), + qreal(0.94154406518302081), + qreal(0.93299279883473885), + qreal(0.92387953251128674), + qreal(0.91420975570353069), + qreal(0.90398929312344345), + qreal(0.89322430119551521), + qreal(0.88192126434835505), + qreal(0.87008699110871146), + qreal(0.85772861000027212), + qreal(0.84485356524970723), + qreal(0.83146961230254546), + qreal(0.81758481315158371), + qreal(0.80320753148064494), + qreal(0.78834642762660634), + qreal(0.7730104533627371), + qreal(0.75720884650648468), + qreal(0.74095112535495899), + qreal(0.72424708295146689), + qreal(0.70710678118654757), + qreal(0.68954054473706705), + qreal(0.67155895484701855), + qreal(0.65317284295377664), + qreal(0.63439328416364549), + qreal(0.61523159058062693), + qreal(0.59569930449243347), + qreal(0.57580819141784545), + qreal(0.55557023301960218), + qreal(0.53499761988709715), + qreal(0.51410274419322177), + qreal(0.49289819222978415), + qreal(0.47139673682599786), + qreal(0.44961132965460687), + qreal(0.42755509343028203), + qreal(0.40524131400498992), + qreal(0.38268343236508989), + qreal(0.35989503653498833), + qreal(0.33688985339222033), + qreal(0.31368174039889141), + qreal(0.29028467725446239), + qreal(0.26671275747489848), + qreal(0.24298017990326407), + qreal(0.21910124015687005), + qreal(0.19509032201612861), + qreal(0.17096188876030122), + qreal(0.1467304744553618), + qreal(0.12241067519921635), + qreal(0.098017140329560826), + qreal(0.073564563599667732), + qreal(0.049067674327417966), + qreal(0.024541228522912326), + qreal(0.0), + qreal(-0.02454122852291208), + qreal(-0.049067674327417724), + qreal(-0.073564563599667496), + qreal(-0.09801714032956059), + qreal(-0.1224106751992161), + qreal(-0.14673047445536158), + qreal(-0.17096188876030097), + qreal(-0.19509032201612836), + qreal(-0.2191012401568698), + qreal(-0.24298017990326382), + qreal(-0.26671275747489825), + qreal(-0.29028467725446211), + qreal(-0.31368174039889118), + qreal(-0.33688985339222011), + qreal(-0.35989503653498811), + qreal(-0.38268343236508967), + qreal(-0.40524131400498969), + qreal(-0.42755509343028181), + qreal(-0.44961132965460665), + qreal(-0.47139673682599764), + qreal(-0.49289819222978393), + qreal(-0.51410274419322155), + qreal(-0.53499761988709693), + qreal(-0.55557023301960196), + qreal(-0.57580819141784534), + qreal(-0.59569930449243325), + qreal(-0.61523159058062671), + qreal(-0.63439328416364527), + qreal(-0.65317284295377653), + qreal(-0.67155895484701844), + qreal(-0.68954054473706683), + qreal(-0.70710678118654746), + qreal(-0.72424708295146678), + qreal(-0.74095112535495888), + qreal(-0.75720884650648423), + qreal(-0.77301045336273666), + qreal(-0.78834642762660589), + qreal(-0.80320753148064505), + qreal(-0.81758481315158382), + qreal(-0.83146961230254524), + qreal(-0.84485356524970701), + qreal(-0.85772861000027201), + qreal(-0.87008699110871135), + qreal(-0.88192126434835494), + qreal(-0.89322430119551521), + qreal(-0.90398929312344312), + qreal(-0.91420975570353047), + qreal(-0.92387953251128652), + qreal(-0.93299279883473896), + qreal(-0.94154406518302081), + qreal(-0.94952818059303667), + qreal(-0.95694033573220882), + qreal(-0.96377606579543984), + qreal(-0.97003125319454397), + qreal(-0.97570213003852846), + qreal(-0.98078528040323032), + qreal(-0.98527764238894111), + qreal(-0.9891765099647809), + qreal(-0.99247953459871008), + qreal(-0.99518472667219693), + qreal(-0.99729045667869021), + qreal(-0.99879545620517241), + qreal(-0.99969881869620425), + qreal(-1.0), + qreal(-0.99969881869620425), + qreal(-0.99879545620517241), + qreal(-0.99729045667869021), + qreal(-0.99518472667219693), + qreal(-0.99247953459871008), + qreal(-0.9891765099647809), + qreal(-0.98527764238894122), + qreal(-0.98078528040323043), + qreal(-0.97570213003852857), + qreal(-0.97003125319454397), + qreal(-0.96377606579543995), + qreal(-0.95694033573220894), + qreal(-0.94952818059303679), + qreal(-0.94154406518302092), + qreal(-0.93299279883473907), + qreal(-0.92387953251128663), + qreal(-0.91420975570353058), + qreal(-0.90398929312344334), + qreal(-0.89322430119551532), + qreal(-0.88192126434835505), + qreal(-0.87008699110871146), + qreal(-0.85772861000027223), + qreal(-0.84485356524970723), + qreal(-0.83146961230254546), + qreal(-0.81758481315158404), + qreal(-0.80320753148064528), + qreal(-0.78834642762660612), + qreal(-0.77301045336273688), + qreal(-0.75720884650648457), + qreal(-0.74095112535495911), + qreal(-0.724247082951467), + qreal(-0.70710678118654768), + qreal(-0.68954054473706716), + qreal(-0.67155895484701866), + qreal(-0.65317284295377709), + qreal(-0.63439328416364593), + qreal(-0.61523159058062737), + qreal(-0.59569930449243325), + qreal(-0.57580819141784523), + qreal(-0.55557023301960218), + qreal(-0.53499761988709726), + qreal(-0.51410274419322188), + qreal(-0.49289819222978426), + qreal(-0.47139673682599792), + qreal(-0.44961132965460698), + qreal(-0.42755509343028253), + qreal(-0.40524131400499042), + qreal(-0.38268343236509039), + qreal(-0.359895036534988), + qreal(-0.33688985339222), + qreal(-0.31368174039889152), + qreal(-0.2902846772544625), + qreal(-0.26671275747489859), + qreal(-0.24298017990326418), + qreal(-0.21910124015687016), + qreal(-0.19509032201612872), + qreal(-0.17096188876030177), + qreal(-0.14673047445536239), + qreal(-0.12241067519921603), + qreal(-0.098017140329560506), + qreal(-0.073564563599667412), + qreal(-0.049067674327418091), + qreal(-0.024541228522912448) +}; + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qmath.h b/src/corelib/kernel/qmath.h new file mode 100644 index 0000000000..27c27dc4d1 --- /dev/null +++ b/src/corelib/kernel/qmath.h @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QMATH_H +#define QMATH_H + +#include + +#include + +#ifdef Q_OS_SYMBIAN +# include +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#define QT_SINE_TABLE_SIZE 256 + +extern Q_CORE_EXPORT const qreal qt_sine_table[QT_SINE_TABLE_SIZE]; + +inline int qCeil(qreal v) +{ +#ifdef QT_USE_MATH_H_FLOATS + if (sizeof(qreal) == sizeof(float)) + return int(ceilf(float(v))); + else +#endif + return int(ceil(v)); +} + +inline int qFloor(qreal v) +{ +#ifdef QT_USE_MATH_H_FLOATS + if (sizeof(qreal) == sizeof(float)) + return int(floorf(float(v))); + else +#endif + return int(floor(v)); +} + +inline qreal qFabs(qreal v) +{ +#ifdef QT_USE_MATH_H_FLOATS + if(sizeof(qreal) == sizeof(float)) + return fabsf(float(v)); + else +#endif + return fabs(v); +} + +inline qreal qSin(qreal v) +{ +#ifdef Q_OS_SYMBIAN + TReal sin_v; + Math::Sin(sin_v, static_cast(v)); + return static_cast(sin_v); +#else +# ifdef QT_USE_MATH_H_FLOATS + if (sizeof(qreal) == sizeof(float)) + return sinf(float(v)); + else +# endif + return sin(v); +#endif +} + +inline qreal qCos(qreal v) +{ +#ifdef Q_OS_SYMBIAN + TReal cos_v; + Math::Cos(cos_v, static_cast(v)); + return static_cast(cos_v); +#else +# ifdef QT_USE_MATH_H_FLOATS + if (sizeof(qreal) == sizeof(float)) + return cosf(float(v)); + else +# endif + return cos(v); +#endif +} + +inline qreal qTan(qreal v) +{ +#ifdef Q_OS_SYMBIAN + TReal tan_v; + Math::Tan(tan_v, static_cast(v)); + return static_cast(tan_v); +#else +# ifdef QT_USE_MATH_H_FLOATS + if (sizeof(qreal) == sizeof(float)) + return tanf(float(v)); + else +# endif + return tan(v); +#endif +} + +inline qreal qAcos(qreal v) +{ +#ifdef Q_OS_SYMBIAN + TReal acos_v; + Math::ACos(acos_v, static_cast(v)); + return static_cast(acos_v); +#else +# ifdef QT_USE_MATH_H_FLOATS + if (sizeof(qreal) == sizeof(float)) + return acosf(float(v)); + else +# endif + return acos(v); +#endif +} + +inline qreal qAsin(qreal v) +{ +#ifdef Q_OS_SYMBIAN + TReal asin_v; + Math::ASin(asin_v, static_cast(v)); + return static_cast(asin_v); +#else +# ifdef QT_USE_MATH_H_FLOATS + if (sizeof(qreal) == sizeof(float)) + return asinf(float(v)); + else +# endif + return asin(v); +#endif +} + +inline qreal qAtan(qreal v) +{ +#ifdef Q_OS_SYMBIAN + TReal atan_v; + Math::ATan(atan_v, static_cast(v)); + return static_cast(atan_v); +#else +# ifdef QT_USE_MATH_H_FLOATS + if(sizeof(qreal) == sizeof(float)) + return atanf(float(v)); + else +# endif + return atan(v); +#endif +} + +inline qreal qAtan2(qreal x, qreal y) +{ +#ifdef Q_OS_SYMBIAN + TReal atan2_v; + Math::ATan(atan2_v, static_cast(x), static_cast(y)); + return static_cast(atan2_v); +#else +# ifdef QT_USE_MATH_H_FLOATS + if(sizeof(qreal) == sizeof(float)) + return atan2f(float(x), float(y)); + else +# endif + return atan2(x, y); +#endif +} + +inline qreal qSqrt(qreal v) +{ +#ifdef Q_OS_SYMBIAN + TReal sqrt_v; + Math::Sqrt(sqrt_v, static_cast(v)); + return static_cast(sqrt_v); +#else +# ifdef QT_USE_MATH_H_FLOATS + if (sizeof(qreal) == sizeof(float)) + return sqrtf(float(v)); + else +# endif + return sqrt(v); +#endif +} + +inline qreal qLn(qreal v) +{ +#ifdef QT_USE_MATH_H_FLOATS + if (sizeof(qreal) == sizeof(float)) + return logf(float(v)); + else +#endif + return log(v); +} + +inline qreal qExp(qreal v) +{ +#ifdef Q_OS_SYMBIAN + TReal exp_v; + Math::Exp(exp_v, static_cast(v)); + return static_cast(exp_v); +#else + // only one signature + // exists, exp(double) + return exp(v); +#endif +} + +inline qreal qPow(qreal x, qreal y) +{ +#ifdef Q_OS_SYMBIAN + TReal pow_v; + Math::Pow(pow_v, static_cast(x), static_cast(y)); + return static_cast(pow_v); +#else +# ifdef QT_USE_MATH_H_FLOATS + if (sizeof(qreal) == sizeof(float)) + return powf(float(x), float(y)); + else +# endif + return pow(x, y); +#endif +} + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +inline qreal qFastSin(qreal x) +{ + int si = int(x * (0.5 * QT_SINE_TABLE_SIZE / M_PI)); // Would be more accurate with qRound, but slower. + qreal d = x - si * (2.0 * M_PI / QT_SINE_TABLE_SIZE); + int ci = si + QT_SINE_TABLE_SIZE / 4; + si &= QT_SINE_TABLE_SIZE - 1; + ci &= QT_SINE_TABLE_SIZE - 1; + return qt_sine_table[si] + (qt_sine_table[ci] - 0.5 * qt_sine_table[si] * d) * d; +} + +inline qreal qFastCos(qreal x) +{ + int ci = int(x * (0.5 * QT_SINE_TABLE_SIZE / M_PI)); // Would be more accurate with qRound, but slower. + qreal d = x - ci * (2.0 * M_PI / QT_SINE_TABLE_SIZE); + int si = ci + QT_SINE_TABLE_SIZE / 4; + si &= QT_SINE_TABLE_SIZE - 1; + ci &= QT_SINE_TABLE_SIZE - 1; + return qt_sine_table[si] - (qt_sine_table[ci] + 0.5 * qt_sine_table[si] * d) * d; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMATH_H diff --git a/src/corelib/kernel/qmath.qdoc b/src/corelib/kernel/qmath.qdoc new file mode 100644 index 0000000000..91c5f56888 --- /dev/null +++ b/src/corelib/kernel/qmath.qdoc @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \headerfile + \title Math Functions + \ingroup funclists + \brief The header provides various math functions. + \pagekeywords math trigonometry qmath floor ceiling absolute sine cosine tangent inverse tan exponent power natural logarithm +*/ + +/*! + \fn int qCeil(qreal v) + Return the ceiling of the value \a v. + + The ceiling is the smallest integer that is not less than \a v. + For example, if \a v is 41.2, then the ceiling is 42. + + \relates + \sa qFloor() +*/ + +/*! + \fn int qFloor(qreal v) + Return the floor of the value \a v. + + The floor is the largest integer that is not greater than \a v. + For example, if \a v is 41.2, then the floor is 41. + + \relates + \sa qCeil() +*/ + +/*! + \fn qreal qFabs(qreal v) + Returns the absolute value of \a v as a qreal. +*/ + +/*! + \fn qreal qSin(qreal v) + Returns the sine of the angle \a v in radians. + + \relates + \sa qCos(), qTan() +*/ + +/*! + \fn qreal qCos(qreal v) + Returns the cosine of an angle \a v in radians. + + \relates + \sa qSin(), qTan() +*/ + +/*! + \fn qreal qTan(qreal v) + Returns the tangent of an angle \a v in radians. + + \relates + \sa qSin(), qCos() +*/ + +/*! + \fn qreal qAcos(qreal v) + Returns the arccosine of \a v as an angle in radians. + Arccosine is the inverse operation of cosine. + + \relates + \sa qAtan(), qAsin(), qCos() +*/ + +/*! + \fn qreal qAsin(qreal v) + Returns the arcsine of \a v as an angle in radians. + Arcsine is the inverse operation of sine. + + \relates + \sa qSin(), qAtan(), qAcos() +*/ + +/*! + \fn qreal qAtan(qreal v) + Returns the arctangent of \a v as an angle in radians. + Arctangent is the inverse operation of tangent. + + \relates + \sa qTan(), qAcos(), qAsin() +*/ + +/*! + \fn qreal qAtan2(qreal x, qreal y) + Returns the arctangent of a point specified by the coordinates \a x and \a y. + This function will return the angle and its direction. + + \relates + \sa qAtan() +*/ + +/*! + \fn qreal qSqrt(qreal v) + Returns the square root of \a v. + This function returns a NaN if \a v is a negative number. + + \relates + \sa qPow() +*/ + +/*! + \fn qreal qLn(qreal v) + Returns the natural logarithm of \a v. Natural logarithm uses base e. + + \relates + \sa qExp() +*/ + +/*! + \fn qreal qExp(qreal v) + Returns the exponential function of \c e to the power of \a v. + + \relates + \sa qLn() +*/ + +/*! + \fn qreal qPow(qreal x, qreal y) + Returns the value of \a x raised to the power of \a y. + That is, \a x is the base and \a y is the exponent. + + \relates + \sa qSqrt() +*/ diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp new file mode 100644 index 0000000000..e671056466 --- /dev/null +++ b/src/corelib/kernel/qmetaobject.cpp @@ -0,0 +1,2778 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qmetaobject.h" +#include "qmetatype.h" +#include "qobject.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "private/qobject_p.h" +#include "private/qmetaobject_p.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QMetaObject + + \brief The QMetaObject class contains meta-information about Qt + objects. + + \ingroup objectmodel + + The Qt \l{Meta-Object System} in Qt is responsible for the + signals and slots inter-object communication mechanism, runtime + type information, and the Qt property system. A single + QMetaObject instance is created for each QObject subclass that is + used in an application, and this instance stores all the + meta-information for the QObject subclass. This object is + available as QObject::metaObject(). + + This class is not normally required for application programming, + but it is useful if you write meta-applications, such as scripting + engines or GUI builders. + + The functions you are most likely to find useful are these: + \list + \o className() returns the name of a class. + \o superClass() returns the superclass's meta-object. + \o method() and methodCount() provide information + about a class's meta-methods (signals, slots and other + \l{Q_INVOKABLE}{invokable} member functions). + \o enumerator() and enumeratorCount() and provide information about + a class's enumerators. + \o propertyCount() and property() provide information about a + class's properties. + \o constructor() and constructorCount() provide information + about a class's meta-constructors. + \endlist + + The index functions indexOfConstructor(), indexOfMethod(), + indexOfEnumerator(), and indexOfProperty() map names of constructors, + member functions, enumerators, or properties to indexes in the + meta-object. For example, Qt uses indexOfMethod() internally when you + connect a signal to a slot. + + Classes can also have a list of \e{name}--\e{value} pairs of + additional class information, stored in QMetaClassInfo objects. + The number of pairs is returned by classInfoCount(), single pairs + are returned by classInfo(), and you can search for pairs with + indexOfClassInfo(). + + \sa QMetaClassInfo, QMetaEnum, QMetaMethod, QMetaProperty, QMetaType, + {Meta-Object System} +*/ + +/*! + \enum QMetaObject::Call + + \internal + + \value InvokeSlot + \value EmitSignal + \value ReadProperty + \value WriteProperty + \value ResetProperty + \value QueryPropertyDesignable + \value QueryPropertyScriptable + \value QueryPropertyStored + \value QueryPropertyEditable + \value QueryPropertyUser + \value CreateInstance +*/ + +/*! + \enum QMetaMethod::Access + + This enum describes the access level of a method, following the conventions used in C++. + + \value Private + \value Protected + \value Public +*/ + +static inline const QMetaObjectPrivate *priv(const uint* data) +{ return reinterpret_cast(data); } + + +/*! + \since 4.5 + + Constructs a new instance of this class. You can pass up to ten arguments + (\a val0, \a val1, \a val2, \a val3, \a val4, \a val5, \a val6, \a val7, + \a val8, and \a val9) to the constructor. Returns the new object, or 0 if + no suitable constructor is available. + + Note that only constructors that are declared with the Q_INVOKABLE + modifier are made available through the meta-object system. + + \sa Q_ARG(), constructor() +*/ +QObject *QMetaObject::newInstance(QGenericArgument val0, + QGenericArgument val1, + QGenericArgument val2, + QGenericArgument val3, + QGenericArgument val4, + QGenericArgument val5, + QGenericArgument val6, + QGenericArgument val7, + QGenericArgument val8, + QGenericArgument val9) const +{ + QByteArray constructorName = className(); + { + int idx = constructorName.lastIndexOf(':'); + if (idx != -1) + constructorName.remove(0, idx+1); // remove qualified part + } + QVarLengthArray sig; + sig.append(constructorName.constData(), constructorName.length()); + sig.append('('); + + enum { MaximumParamCount = 10 }; + const char *typeNames[] = {val0.name(), val1.name(), val2.name(), val3.name(), val4.name(), + val5.name(), val6.name(), val7.name(), val8.name(), val9.name()}; + + int paramCount; + for (paramCount = 0; paramCount < MaximumParamCount; ++paramCount) { + int len = qstrlen(typeNames[paramCount]); + if (len <= 0) + break; + sig.append(typeNames[paramCount], len); + sig.append(','); + } + if (paramCount == 0) + sig.append(')'); // no parameters + else + sig[sig.size() - 1] = ')'; + sig.append('\0'); + + int idx = indexOfConstructor(sig.constData()); + if (idx < 0) { + QByteArray norm = QMetaObject::normalizedSignature(sig.constData()); + idx = indexOfConstructor(norm.constData()); + } + if (idx < 0) + return 0; + + QVariant ret(QMetaType::QObjectStar, (void*)0); + void *param[] = {ret.data(), val0.data(), val1.data(), val2.data(), val3.data(), val4.data(), + val5.data(), val6.data(), val7.data(), val8.data(), val9.data()}; + + if (static_metacall(CreateInstance, idx, param) >= 0) + return 0; + return *reinterpret_cast(param[0]); +} + +/*! + \internal +*/ +int QMetaObject::static_metacall(Call cl, int idx, void **argv) const +{ + const QMetaObjectExtraData *extra = reinterpret_cast(d.extradata); + if (priv(d.data)->revision >= 6) { + if (!extra || !extra->static_metacall) + return 0; + extra->static_metacall(0, cl, idx, argv); + return -1; + } else if (priv(d.data)->revision >= 2) { + if (!extra || !extra->static_metacall) + return 0; + typedef int (*OldMetacall)(QMetaObject::Call, int, void **); + OldMetacall o = reinterpret_cast(extra->static_metacall); + return o(cl, idx, argv); + } + return 0; +} + +/*! + \internal +*/ +int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv) +{ + if (QMetaObject *mo = object->d_ptr->metaObject) + return static_cast(mo)->metaCall(cl, idx, argv); + else + return object->qt_metacall(cl, idx, argv); +} + +/*! + \fn const char *QMetaObject::className() const + + Returns the class name. + + \sa superClass() +*/ + +/*! + \fn QMetaObject *QMetaObject::superClass() const + + Returns the meta-object of the superclass, or 0 if there is no + such object. + + \sa className() +*/ + +/*! + \internal + + Returns \a obj if object \a obj inherits from this + meta-object; otherwise returns 0. +*/ +QObject *QMetaObject::cast(QObject *obj) const +{ + if (obj) { + const QMetaObject *m = obj->metaObject(); + do { + if (m == this) + return obj; + } while ((m = m->d.superdata)); + } + return 0; +} + +/*! + \internal + + Returns \a obj if object \a obj inherits from this + meta-object; otherwise returns 0. +*/ +const QObject *QMetaObject::cast(const QObject *obj) const +{ + if (obj) { + const QMetaObject *m = obj->metaObject(); + do { + if (m == this) + return obj; + } while ((m = m->d.superdata)); + } + return 0; +} + +#ifndef QT_NO_TRANSLATION +/*! + \internal +*/ +QString QMetaObject::tr(const char *s, const char *c) const +{ + return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::CodecForTr); +} + +/*! + \internal +*/ +QString QMetaObject::tr(const char *s, const char *c, int n) const +{ + return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::CodecForTr, n); +} + +/*! + \internal +*/ +QString QMetaObject::trUtf8(const char *s, const char *c) const +{ + return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::UnicodeUTF8); +} + +/*! + \internal +*/ +QString QMetaObject::trUtf8(const char *s, const char *c, int n) const +{ + return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::UnicodeUTF8, n); +} +#endif // QT_NO_TRANSLATION + +/*! + Returns the method offset for this class; i.e. the index position + of this class's first member function. + + The offset is the sum of all the methods in the class's + superclasses (which is always positive since QObject has the + deleteLater() slot and a destroyed() signal). + + \sa method(), methodCount(), indexOfMethod() +*/ +int QMetaObject::methodOffset() const +{ + int offset = 0; + const QMetaObject *m = d.superdata; + while (m) { + offset += priv(m->d.data)->methodCount; + m = m->d.superdata; + } + return offset; +} + + +/*! + Returns the enumerator offset for this class; i.e. the index + position of this class's first enumerator. + + If the class has no superclasses with enumerators, the offset is + 0; otherwise the offset is the sum of all the enumerators in the + class's superclasses. + + \sa enumerator(), enumeratorCount(), indexOfEnumerator() +*/ +int QMetaObject::enumeratorOffset() const +{ + int offset = 0; + const QMetaObject *m = d.superdata; + while (m) { + offset += priv(m->d.data)->enumeratorCount; + m = m->d.superdata; + } + return offset; +} + +/*! + Returns the property offset for this class; i.e. the index + position of this class's first property. + + The offset is the sum of all the properties in the class's + superclasses (which is always positive since QObject has the + name() property). + + \sa property(), propertyCount(), indexOfProperty() +*/ +int QMetaObject::propertyOffset() const +{ + int offset = 0; + const QMetaObject *m = d.superdata; + while (m) { + offset += priv(m->d.data)->propertyCount; + m = m->d.superdata; + } + return offset; +} + +/*! + Returns the class information offset for this class; i.e. the + index position of this class's first class information item. + + If the class has no superclasses with class information, the + offset is 0; otherwise the offset is the sum of all the class + information items in the class's superclasses. + + \sa classInfo(), classInfoCount(), indexOfClassInfo() +*/ +int QMetaObject::classInfoOffset() const +{ + int offset = 0; + const QMetaObject *m = d.superdata; + while (m) { + offset += priv(m->d.data)->classInfoCount; + m = m->d.superdata; + } + return offset; +} + +/*! + \since 4.5 + + Returns the number of constructors in this class. + + \sa constructor(), indexOfConstructor() +*/ +int QMetaObject::constructorCount() const +{ + if (priv(d.data)->revision < 2) + return 0; + return priv(d.data)->constructorCount; +} + +/*! + Returns the number of methods in this class, including the number of + properties provided by each base class. These include signals and slots + as well as normal member functions. + + Use code like the following to obtain a QStringList containing the methods + specific to a given class: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp methodCount + + \sa method(), methodOffset(), indexOfMethod() +*/ +int QMetaObject::methodCount() const +{ + int n = priv(d.data)->methodCount; + const QMetaObject *m = d.superdata; + while (m) { + n += priv(m->d.data)->methodCount; + m = m->d.superdata; + } + return n; +} + +/*! + Returns the number of enumerators in this class. + + \sa enumerator(), enumeratorOffset(), indexOfEnumerator() +*/ +int QMetaObject::enumeratorCount() const +{ + int n = priv(d.data)->enumeratorCount; + const QMetaObject *m = d.superdata; + while (m) { + n += priv(m->d.data)->enumeratorCount; + m = m->d.superdata; + } + return n; +} + +/*! + Returns the number of properties in this class, including the number of + properties provided by each base class. + + Use code like the following to obtain a QStringList containing the properties + specific to a given class: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp propertyCount + + \sa property(), propertyOffset(), indexOfProperty() +*/ +int QMetaObject::propertyCount() const +{ + int n = priv(d.data)->propertyCount; + const QMetaObject *m = d.superdata; + while (m) { + n += priv(m->d.data)->propertyCount; + m = m->d.superdata; + } + return n; +} + +/*! + Returns the number of items of class information in this class. + + \sa classInfo(), classInfoOffset(), indexOfClassInfo() +*/ +int QMetaObject::classInfoCount() const +{ + int n = priv(d.data)->classInfoCount; + const QMetaObject *m = d.superdata; + while (m) { + n += priv(m->d.data)->classInfoCount; + m = m->d.superdata; + } + return n; +} + +/** \internal +* helper function 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. +* \a normalizeStringData set to true if we should do a second pass for old moc generated files normalizing all the symbols. +*/ +template +static inline int indexOfMethodRelative(const QMetaObject **baseObject, + const char *method, + bool normalizeStringData) +{ + for (const QMetaObject *m = *baseObject; m; m = m->d.superdata) { + int i = (MethodType == MethodSignal && priv(m->d.data)->revision >= 4) + ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1); + const int end = (MethodType == MethodSlot && priv(m->d.data)->revision >= 4) + ? (priv(m->d.data)->signalCount) : 0; + if (!normalizeStringData) { + for (; i >= end; --i) { + const char *stringdata = m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5*i]; + if (method[0] == stringdata[0] && strcmp(method + 1, stringdata + 1) == 0) { + *baseObject = m; + return i; + } + } + } else if (priv(m->d.data)->revision < 5) { + for (; i >= end; --i) { + const char *stringdata = (m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5 * i]); + const QByteArray normalizedSignature = QMetaObject::normalizedSignature(stringdata); + if (normalizedSignature == method) { + *baseObject = m; + return i; + } + } + } + } + return -1; +} + + +/*! + \since 4.5 + + Finds \a constructor and returns its index; otherwise returns -1. + + Note that the \a constructor has to be in normalized form, as returned + by normalizedSignature(). + + \sa constructor(), constructorCount(), normalizedSignature() +*/ +int QMetaObject::indexOfConstructor(const char *constructor) const +{ + if (priv(d.data)->revision < 2) + return -1; + for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) { + const char *data = d.stringdata + d.data[priv(d.data)->constructorData + 5*i]; + if (data[0] == constructor[0] && strcmp(constructor + 1, data + 1) == 0) { + return i; + } + } + return -1; +} + +/*! + Finds \a method and returns its index; otherwise returns -1. + + Note that the \a method has to be in normalized form, as returned + by normalizedSignature(). + + \sa method(), methodCount(), methodOffset(), normalizedSignature() +*/ +int QMetaObject::indexOfMethod(const char *method) const +{ + const QMetaObject *m = this; + 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; +} + +/*! + Finds \a signal and returns its index; otherwise returns -1. + + This is the same as indexOfMethod(), except that it will return + -1 if the method exists but isn't a signal. + + Note that the \a signal has to be in normalized form, as returned + by normalizedSignature(). + + \sa indexOfMethod(), normalizedSignature(), method(), methodCount(), methodOffset() +*/ +int QMetaObject::indexOfSignal(const char *signal) const +{ + const QMetaObject *m = this; + 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; +} + +/*! \internal + Same as QMetaObject::indexOfSignal, but the result is the local offset to the base object. + + \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, + bool normalizeStringData) +{ + int i = indexOfMethodRelative(baseObject, signal, normalizeStringData); +#ifndef QT_NO_DEBUG + const QMetaObject *m = *baseObject; + if (i >= 0 && m && m->d.superdata) { + int conflict = m->d.superdata->indexOfMethod(signal); + if (conflict >= 0) + qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s", + signal, m->d.superdata->d.stringdata, m->d.stringdata); + } +#endif + return i; +} + +/*! + Finds \a slot and returns its index; otherwise returns -1. + + This is the same as indexOfMethod(), except that it will return + -1 if the method exists but isn't a slot. + + \sa indexOfMethod(), method(), methodCount(), methodOffset() +*/ +int QMetaObject::indexOfSlot(const char *slot) const +{ + const QMetaObject *m = this; + int i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, false); + if (i < 0) + i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, true); + if (i >= 0) + i += m->methodOffset(); + return i; +} + +// same as indexOfSignalRelative but for slots. +int QMetaObjectPrivate::indexOfSlotRelative(const QMetaObject **m, + const char *slot, + bool normalizeStringData) +{ + return indexOfMethodRelative(m, slot, normalizeStringData); +} + +static const QMetaObject *QMetaObject_findMetaObject(const QMetaObject *self, const char *name) +{ + while (self) { + if (strcmp(self->d.stringdata, name) == 0) + return self; + if (self->d.extradata) { +#ifdef Q_NO_DATA_RELOCATION + const QMetaObjectAccessor *e; + Q_ASSERT(priv(self->d.data)->revision >= 2); +#else + const QMetaObject **e; + if (priv(self->d.data)->revision < 2) { + e = (const QMetaObject**)(self->d.extradata); + } else +#endif + { + const QMetaObjectExtraData *extra = (const QMetaObjectExtraData*)(self->d.extradata); + e = extra->objects; + } + if (e) { + while (*e) { +#ifdef Q_NO_DATA_RELOCATION + if (const QMetaObject *m =QMetaObject_findMetaObject(&((*e)()), name)) +#else + if (const QMetaObject *m =QMetaObject_findMetaObject((*e), name)) +#endif + return m; + ++e; + } + } + } + self = self->d.superdata; + } + return self; +} + +/*! + Finds enumerator \a name and returns its index; otherwise returns + -1. + + \sa enumerator(), enumeratorCount(), enumeratorOffset() +*/ +int QMetaObject::indexOfEnumerator(const char *name) const +{ + const QMetaObject *m = this; + while (m) { + const QMetaObjectPrivate *d = priv(m->d.data); + for (int i = d->enumeratorCount - 1; i >= 0; --i) { + const char *prop = m->d.stringdata + m->d.data[d->enumeratorData + 4*i]; + if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) { + i += m->enumeratorOffset(); + return i; + } + } + m = m->d.superdata; + } + return -1; +} + +/*! + Finds property \a name and returns its index; otherwise returns + -1. + + \sa property(), propertyCount(), propertyOffset() +*/ +int QMetaObject::indexOfProperty(const char *name) const +{ + const QMetaObject *m = this; + while (m) { + const QMetaObjectPrivate *d = priv(m->d.data); + for (int i = d->propertyCount-1; i >= 0; --i) { + const char *prop = m->d.stringdata + m->d.data[d->propertyData + 3*i]; + if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) { + i += m->propertyOffset(); + return i; + } + } + m = m->d.superdata; + } + + if (priv(this->d.data)->revision >= 3 && (priv(this->d.data)->flags & DynamicMetaObject)) { + QAbstractDynamicMetaObject *me = + const_cast(static_cast(this)); + + return me->createProperty(name, 0); + } + + return -1; +} + +/*! + Finds class information item \a name and returns its index; + otherwise returns -1. + + \sa classInfo(), classInfoCount(), classInfoOffset() +*/ +int QMetaObject::indexOfClassInfo(const char *name) const +{ + int i = -1; + const QMetaObject *m = this; + while (m && i < 0) { + for (i = priv(m->d.data)->classInfoCount-1; i >= 0; --i) + if (strcmp(name, m->d.stringdata + + m->d.data[priv(m->d.data)->classInfoData + 2*i]) == 0) { + i += m->classInfoOffset(); + break; + } + m = m->d.superdata; + } + return i; +} + +/*! + \since 4.5 + + Returns the meta-data for the constructor with the given \a index. + + \sa constructorCount(), newInstance() +*/ +QMetaMethod QMetaObject::constructor(int index) const +{ + int i = index; + QMetaMethod result; + if (priv(d.data)->revision >= 2 && i >= 0 && i < priv(d.data)->constructorCount) { + result.mobj = this; + result.handle = priv(d.data)->constructorData + 5*i; + } + return result; +} + +/*! + Returns the meta-data for the method with the given \a index. + + \sa methodCount(), methodOffset(), indexOfMethod() +*/ +QMetaMethod QMetaObject::method(int index) const +{ + int i = index; + i -= methodOffset(); + if (i < 0 && d.superdata) + return d.superdata->method(index); + + QMetaMethod result; + if (i >= 0 && i < priv(d.data)->methodCount) { + result.mobj = this; + result.handle = priv(d.data)->methodData + 5*i; + } + return result; +} + +/*! + Returns the meta-data for the enumerator with the given \a index. + + \sa enumeratorCount(), enumeratorOffset(), indexOfEnumerator() +*/ +QMetaEnum QMetaObject::enumerator(int index) const +{ + int i = index; + i -= enumeratorOffset(); + if (i < 0 && d.superdata) + return d.superdata->enumerator(index); + + QMetaEnum result; + if (i >= 0 && i < priv(d.data)->enumeratorCount) { + result.mobj = this; + result.handle = priv(d.data)->enumeratorData + 4*i; + } + return result; +} + +/*! + Returns the meta-data for the property with the given \a index. + If no such property exists, a null QMetaProperty is returned. + + \sa propertyCount(), propertyOffset(), indexOfProperty() +*/ +QMetaProperty QMetaObject::property(int index) const +{ + int i = index; + i -= propertyOffset(); + if (i < 0 && d.superdata) + return d.superdata->property(index); + + QMetaProperty result; + if (i >= 0 && i < priv(d.data)->propertyCount) { + int handle = priv(d.data)->propertyData + 3*i; + int flags = d.data[handle + 2]; + const char *type = d.stringdata + d.data[handle + 1]; + result.mobj = this; + result.handle = handle; + result.idx = i; + + if (flags & EnumOrFlag) { + result.menum = enumerator(indexOfEnumerator(type)); + if (!result.menum.isValid()) { + QByteArray enum_name = type; + QByteArray scope_name = d.stringdata; + int s = enum_name.lastIndexOf("::"); + if (s > 0) { + scope_name = enum_name.left(s); + enum_name = enum_name.mid(s + 2); + } + const QMetaObject *scope = 0; + if (scope_name == "Qt") + scope = &QObject::staticQtMetaObject; + else + scope = QMetaObject_findMetaObject(this, scope_name); + if (scope) + result.menum = scope->enumerator(scope->indexOfEnumerator(enum_name)); + } + } + } + return result; +} + +/*! + \since 4.2 + + Returns the property that has the \c USER flag set to true. + + \sa QMetaProperty::isUser() +*/ +QMetaProperty QMetaObject::userProperty() const +{ + const int propCount = propertyCount(); + for (int i = propCount - 1; i >= 0; --i) { + const QMetaProperty prop = property(i); + if (prop.isUser()) + return prop; + } + return QMetaProperty(); +} + +/*! + Returns the meta-data for the item of class information with the + given \a index. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp 0 + + \sa classInfoCount(), classInfoOffset(), indexOfClassInfo() + */ +QMetaClassInfo QMetaObject::classInfo(int index) const +{ + int i = index; + i -= classInfoOffset(); + if (i < 0 && d.superdata) + return d.superdata->classInfo(index); + + QMetaClassInfo result; + if (i >= 0 && i < priv(d.data)->classInfoCount) { + result.mobj = this; + result.handle = priv(d.data)->classInfoData + 2*i; + } + return result; +} + +/*! + Returns true if the \a signal and \a method arguments are + compatible; otherwise returns false. + + Both \a signal and \a method are expected to be normalized. + + \sa normalizedSignature() +*/ +bool QMetaObject::checkConnectArgs(const char *signal, const char *method) +{ + const char *s1 = signal; + const char *s2 = method; + while (*s1++ != '(') { } // scan to first '(' + while (*s2++ != '(') { } + if (*s2 == ')' || qstrcmp(s1,s2) == 0) // method has no args or + return true; // exact match + int s1len = qstrlen(s1); + int s2len = qstrlen(s2); + if (s2len < s1len && strncmp(s1,s2,s2len-1)==0 && s1[s2len-1]==',') + return true; // method has less args + return false; +} + +static void qRemoveWhitespace(const char *s, char *d) +{ + char last = 0; + while (*s && is_space(*s)) + s++; + while (*s) { + while (*s && !is_space(*s)) + last = *d++ = *s++; + while (*s && is_space(*s)) + s++; + if (*s && ((is_ident_char(*s) && is_ident_char(last)) + || ((*s == ':') && (last == '<')))) { + last = *d++ = ' '; + } + } + *d = '\0'; +} + +static char *qNormalizeType(char *d, int &templdepth, QByteArray &result) +{ + const char *t = d; + while (*d && (templdepth + || (*d != ',' && *d != ')'))) { + if (*d == '<') + ++templdepth; + if (*d == '>') + --templdepth; + ++d; + } + if (strncmp("void", t, d - t) != 0) + result += normalizeTypeInternal(t, d); + + return d; +} + + +/*! + \since 4.2 + + Normalizes a \a type. + + See QMetaObject::normalizedSignature() for a description on how + Qt normalizes. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp 1 + + \sa normalizedSignature() + */ +QByteArray QMetaObject::normalizedType(const char *type) +{ + QByteArray result; + + if (!type || !*type) + return result; + + QVarLengthArray stackbuf(qstrlen(type) + 1); + qRemoveWhitespace(type, stackbuf.data()); + int templdepth = 0; + qNormalizeType(stackbuf.data(), templdepth, result); + + return result; +} + +/*! + Normalizes the signature of the given \a method. + + Qt uses normalized signatures to decide whether two given signals + and slots are compatible. Normalization reduces whitespace to a + minimum, moves 'const' to the front where appropriate, removes + 'const' from value types and replaces const references with + values. + + \sa checkConnectArgs(), normalizedType() + */ +QByteArray QMetaObject::normalizedSignature(const char *method) +{ + QByteArray result; + if (!method || !*method) + return result; + int len = int(strlen(method)); + QVarLengthArray stackbuf(len + 1); + char *d = stackbuf.data(); + qRemoveWhitespace(method, d); + + result.reserve(len); + + int argdepth = 0; + int templdepth = 0; + while (*d) { + if (argdepth == 1) { + d = qNormalizeType(d, templdepth, result); + if (!*d) //most likely an invalid signature. + break; + } + if (*d == '(') + ++argdepth; + if (*d == ')') + --argdepth; + result += *d++; + } + + return result; +} + +enum { MaximumParamCount = 11 }; // up to 10 arguments + 1 return value + +/*! + Invokes the \a member (a signal or a slot name) on the object \a + obj. Returns true if the member could be invoked. Returns false + if there is no such member or the parameters did not match. + + The invocation can be either synchronous or asynchronous, + depending on \a type: + + \list + \o If \a type is Qt::DirectConnection, the member will be invoked immediately. + + \o If \a type is Qt::QueuedConnection, + a QEvent will be sent and the member is invoked as soon as the application + enters the main event loop. + + \o If \a type is Qt::BlockingQueuedConnection, the method will be invoked in + the same way as for Qt::QueuedConnection, except that the current thread + will block until the event is delivered. Using this connection type to + communicate between objects in the same thread will lead to deadlocks. + + \o If \a type is Qt::AutoConnection, the member is invoked + synchronously if \a obj lives in the same thread as the + caller; otherwise it will invoke the member asynchronously. + \endlist + + The return value of the \a member function call is placed in \a + ret. If the invocation is asynchronous, the return value cannot + be evaluated. You can pass up to ten arguments (\a val0, \a val1, + \a val2, \a val3, \a val4, \a val5, \a val6, \a val7, \a val8, + and \a val9) to the \a member function. + + QGenericArgument and QGenericReturnArgument are internal + helper classes. Because signals and slots can be dynamically + invoked, you must enclose the arguments using the Q_ARG() and + Q_RETURN_ARG() macros. Q_ARG() takes a type name and a + const reference of that type; Q_RETURN_ARG() takes a type name + and a non-const reference. + + You only need to pass the name of the signal or slot to this function, + not the entire signature. For example, to asynchronously invoke + the \l{QPushButton::animateClick()}{animateClick()} slot on a + QPushButton, use the following code: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp 2 + + With asynchronous method invocations, the parameters must be of + types that are known to Qt's meta-object system, because Qt needs + to copy the arguments to store them in an event behind the + scenes. If you try to use a queued connection and get the error + message + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp 3 + + call qRegisterMetaType() to register the data type before you + call invokeMethod(). + + To synchronously invoke the \c compute(QString, int, double) slot on + some arbitrary object \c obj retrieve its return value: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp 4 + + If the "compute" slot does not take exactly one QString, one int + and one double in the specified order, the call will fail. + + \sa Q_ARG(), Q_RETURN_ARG(), qRegisterMetaType(), QMetaMethod::invoke() +*/ +bool QMetaObject::invokeMethod(QObject *obj, + const char *member, + Qt::ConnectionType type, + QGenericReturnArgument ret, + QGenericArgument val0, + QGenericArgument val1, + QGenericArgument val2, + QGenericArgument val3, + QGenericArgument val4, + QGenericArgument val5, + QGenericArgument val6, + QGenericArgument val7, + QGenericArgument val8, + QGenericArgument val9) +{ + if (!obj) + return false; + + QVarLengthArray sig; + int len = qstrlen(member); + if (len <= 0) + return false; + sig.append(member, len); + sig.append('('); + + const char *typeNames[] = {ret.name(), val0.name(), val1.name(), val2.name(), val3.name(), + val4.name(), val5.name(), val6.name(), val7.name(), val8.name(), + val9.name()}; + + int paramCount; + for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) { + len = qstrlen(typeNames[paramCount]); + if (len <= 0) + break; + sig.append(typeNames[paramCount], len); + sig.append(','); + } + if (paramCount == 1) + sig.append(')'); // no parameters + else + sig[sig.size() - 1] = ')'; + sig.append('\0'); + + int idx = obj->metaObject()->indexOfMethod(sig.constData()); + if (idx < 0) { + QByteArray norm = QMetaObject::normalizedSignature(sig.constData()); + idx = obj->metaObject()->indexOfMethod(norm.constData()); + } + + if (idx < 0 || idx >= obj->metaObject()->methodCount()) { + qWarning("QMetaObject::invokeMethod: No such method %s::%s", + obj->metaObject()->className(), sig.constData()); + return false; + } + QMetaMethod method = obj->metaObject()->method(idx); + return method.invoke(obj, type, ret, + val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); +} + +/*! \fn bool QMetaObject::invokeMethod(QObject *obj, const char *member, + QGenericReturnArgument ret, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()); + \overload invokeMethod() + + This overload always invokes the member using the connection type Qt::AutoConnection. +*/ + +/*! \fn bool QMetaObject::invokeMethod(QObject *obj, const char *member, + Qt::ConnectionType type, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) + + \overload invokeMethod() + + This overload can be used if the return value of the member is of no interest. +*/ + +/*! + \fn bool QMetaObject::invokeMethod(QObject *obj, const char *member, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) + + \overload invokeMethod() + + This overload invokes the member using the connection type Qt::AutoConnection and + ignores return values. +*/ + +/*! + \class QMetaMethod + + \brief The QMetaMethod class provides meta-data about a member + function. + + \ingroup objectmodel + + A QMetaMethod has a methodType(), a signature(), a list of + parameterTypes() and parameterNames(), a return typeName(), a + tag(), and an access() specifier. You can use invoke() to invoke + the method on an arbitrary QObject. + + \sa QMetaObject, QMetaEnum, QMetaProperty, {Qt's Property System} +*/ + +/*! + \enum QMetaMethod::Attributes + + \internal + + \value Compatibility + \value Cloned + \value Scriptable +*/ + +/*! + \fn const QMetaObject *QMetaMethod::enclosingMetaObject() const + \internal +*/ + +/*! + \enum QMetaMethod::MethodType + + \value Method The function is a plain member function. + \value Signal The function is a signal. + \value Slot The function is a slot. + \value Constructor The function is a constructor. +*/ + +/*! + \fn QMetaMethod::QMetaMethod() + \internal +*/ + +/*! + Returns the signature of this method (e.g., + \c{setValue(double)}). + + \sa parameterTypes(), parameterNames() +*/ +const char *QMetaMethod::signature() const +{ + if (!mobj) + return 0; + return mobj->d.stringdata + mobj->d.data[handle]; +} + +/*! + Returns a list of parameter types. + + \sa parameterNames(), signature() +*/ +QList QMetaMethod::parameterTypes() const +{ + QList list; + if (!mobj) + return list; + const char *signature = mobj->d.stringdata + mobj->d.data[handle]; + while (*signature && *signature != '(') + ++signature; + while (*signature && *signature != ')' && *++signature != ')') { + const char *begin = signature; + int level = 0; + while (*signature && (level > 0 || *signature != ',') && *signature != ')') { + if (*signature == '<') + ++level; + else if (*signature == '>') + --level; + ++signature; + } + list += QByteArray(begin, signature - begin); + } + return list; +} + +/*! + Returns a list of parameter names. + + \sa parameterTypes(), signature() +*/ +QList QMetaMethod::parameterNames() const +{ + QList list; + if (!mobj) + return list; + const char *names = mobj->d.stringdata + mobj->d.data[handle + 1]; + if (*names == 0) { + // do we have one or zero arguments? + const char *signature = mobj->d.stringdata + mobj->d.data[handle]; + while (*signature && *signature != '(') + ++signature; + if (*++signature != ')') + list += QByteArray(); + } else { + --names; + do { + const char *begin = ++names; + while (*names && *names != ',') + ++names; + list += QByteArray(begin, names - begin); + } while (*names); + } + return list; +} + + +/*! + Returns the return type of this method, or an empty string if the + return type is \e void. +*/ +const char *QMetaMethod::typeName() const +{ + if (!mobj) + return 0; + return mobj->d.stringdata + mobj->d.data[handle + 2]; +} + +/*! + Returns the tag associated with this method. + + Tags are special macros recognized by \c moc that make it + possible to add extra information about a method. For the moment, + \c moc doesn't support any special tags. +*/ +const char *QMetaMethod::tag() const +{ + if (!mobj) + return 0; + return mobj->d.stringdata + mobj->d.data[handle + 3]; +} + + +/*! \internal */ +int QMetaMethod::attributes() const +{ + if (!mobj) + return false; + return ((mobj->d.data[handle + 4])>>4); +} + +/*! + \since 4.6 + + Returns this method's index. +*/ +int QMetaMethod::methodIndex() const +{ + if (!mobj) + return -1; + return ((handle - priv(mobj->d.data)->methodData) / 5) + mobj->methodOffset(); +} + +/*! + \internal + + Returns the method revision if one was + specified by Q_REVISION, otherwise returns 0. + */ +int QMetaMethod::revision() const +{ + if (!mobj) + return 0; + if ((QMetaMethod::Access)(mobj->d.data[handle + 4] & MethodRevisioned)) { + int offset = priv(mobj->d.data)->methodData + + priv(mobj->d.data)->methodCount * 5 + + (handle - priv(mobj->d.data)->methodData) / 5; + return mobj->d.data[offset]; + } + return 0; +} + +/*! + Returns the access specification of this method (private, + protected, or public). + + Signals are always protected, meaning that you can only emit them + from the class or from a subclass. + + \sa methodType() +*/ +QMetaMethod::Access QMetaMethod::access() const +{ + if (!mobj) + return Private; + return (QMetaMethod::Access)(mobj->d.data[handle + 4] & AccessMask); +} + +/*! + Returns the type of this method (signal, slot, or method). + + \sa access() +*/ +QMetaMethod::MethodType QMetaMethod::methodType() const +{ + if (!mobj) + return QMetaMethod::Method; + return (QMetaMethod::MethodType)((mobj->d.data[handle + 4] & MethodTypeMask)>>2); +} + +/*! + Invokes this method on the object \a object. Returns true if the member could be invoked. + Returns false if there is no such member or the parameters did not match. + + The invocation can be either synchronous or asynchronous, depending on the + \a connectionType: + + \list + \o If \a connectionType is Qt::DirectConnection, the member will be invoked immediately. + + \o If \a connectionType is Qt::QueuedConnection, + a QEvent will be posted and the member is invoked as soon as the application + enters the main event loop. + + \o If \a connectionType is Qt::AutoConnection, the member is invoked + synchronously if \a object lives in the same thread as the + caller; otherwise it will invoke the member asynchronously. + \endlist + + The return value of this method call is placed in \a + returnValue. If the invocation is asynchronous, the return value cannot + be evaluated. You can pass up to ten arguments (\a val0, \a val1, + \a val2, \a val3, \a val4, \a val5, \a val6, \a val7, \a val8, + and \a val9) to this method call. + + QGenericArgument and QGenericReturnArgument are internal + helper classes. Because signals and slots can be dynamically + invoked, you must enclose the arguments using the Q_ARG() and + Q_RETURN_ARG() macros. Q_ARG() takes a type name and a + const reference of that type; Q_RETURN_ARG() takes a type name + and a non-const reference. + + To asynchronously invoke the + \l{QPushButton::animateClick()}{animateClick()} slot on a + QPushButton: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp 6 + + With asynchronous method invocations, the parameters must be of + types that are known to Qt's meta-object system, because Qt needs + to copy the arguments to store them in an event behind the + scenes. If you try to use a queued connection and get the error + message + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp 7 + + call qRegisterMetaType() to register the data type before you + call QMetaMethod::invoke(). + + To synchronously invoke the \c compute(QString, int, double) slot on + some arbitrary object \c obj retrieve its return value: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp 8 + + QMetaObject::normalizedSignature() is used here to ensure that the format + of the signature is what invoke() expects. E.g. extra whitespace is + removed. + + If the "compute" slot does not take exactly one QString, one int + and one double in the specified order, the call will fail. + + \warning this method will not test the validity of the arguments: \a object + must be an instance of the class of the QMetaObject of which this QMetaMethod + has been constructed with. The arguments must have the same type as the ones + expected by the method, else, the behaviour is undefined. + + \sa Q_ARG(), Q_RETURN_ARG(), qRegisterMetaType(), QMetaObject::invokeMethod() +*/ +bool QMetaMethod::invoke(QObject *object, + Qt::ConnectionType connectionType, + QGenericReturnArgument returnValue, + QGenericArgument val0, + QGenericArgument val1, + QGenericArgument val2, + QGenericArgument val3, + QGenericArgument val4, + QGenericArgument val5, + QGenericArgument val6, + QGenericArgument val7, + QGenericArgument val8, + QGenericArgument val9) const +{ + if (!object || !mobj) + return false; + + Q_ASSERT(mobj->cast(object)); + + // check return type + if (returnValue.data()) { + const char *retType = typeName(); + if (qstrcmp(returnValue.name(), retType) != 0) { + // normalize the return value as well + // the trick here is to make a function signature out of the return type + // so that we can call normalizedSignature() and avoid duplicating code + QByteArray unnormalized; + int len = qstrlen(returnValue.name()); + + unnormalized.reserve(len + 3); + unnormalized = "_("; // the function is called "_" + unnormalized.append(returnValue.name()); + unnormalized.append(')'); + + QByteArray normalized = QMetaObject::normalizedSignature(unnormalized.constData()); + normalized.truncate(normalized.length() - 1); // drop the ending ')' + + if (qstrcmp(normalized.constData() + 2, retType) != 0) + return false; + } + } + + // check argument count (we don't allow invoking a method if given too few arguments) + const char *typeNames[] = { + returnValue.name(), + val0.name(), + val1.name(), + val2.name(), + val3.name(), + val4.name(), + val5.name(), + val6.name(), + val7.name(), + val8.name(), + val9.name() + }; + int paramCount; + for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) { + if (qstrlen(typeNames[paramCount]) <= 0) + break; + } + int metaMethodArgumentCount = 0; + { + // based on QMetaObject::parameterNames() + const char *names = mobj->d.stringdata + mobj->d.data[handle + 1]; + if (*names == 0) { + // do we have one or zero arguments? + const char *signature = mobj->d.stringdata + mobj->d.data[handle]; + while (*signature && *signature != '(') + ++signature; + if (*++signature != ')') + ++metaMethodArgumentCount; + } else { + --names; + do { + ++names; + while (*names && *names != ',') + ++names; + ++metaMethodArgumentCount; + } while (*names); + } + } + if (paramCount <= metaMethodArgumentCount) + return false; + + // check connection type + QThread *currentThread = QThread::currentThread(); + QThread *objectThread = object->thread(); + if (connectionType == Qt::AutoConnection) { + connectionType = currentThread == objectThread + ? Qt::DirectConnection + : Qt::QueuedConnection; + } + +#ifdef QT_NO_THREAD + if (connectionType == Qt::BlockingQueuedConnection) { + connectionType = Qt::DirectConnection; + } +#endif + + // invoke! + void *param[] = { + returnValue.data(), + val0.data(), + val1.data(), + val2.data(), + val3.data(), + val4.data(), + val5.data(), + val6.data(), + val7.data(), + val8.data(), + val9.data() + }; + // recompute the methodIndex by reversing the arithmetic in QMetaObject::property() + int idx_relative = ((handle - priv(mobj->d.data)->methodData) / 5); + int idx_offset = mobj->methodOffset(); + QObjectPrivate::StaticMetaCallFunction callFunction = + (QMetaObjectPrivate::get(mobj)->revision >= 6 && mobj->d.extradata) + ? reinterpret_cast(mobj->d.extradata)->static_metacall : 0; + + if (connectionType == Qt::DirectConnection) { + if (callFunction) { + callFunction(object, QMetaObject::InvokeMetaMethod, idx_relative, param); + return true; + } else { + return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx_relative + idx_offset, param) < 0; + } + } else if (connectionType == Qt::QueuedConnection) { + if (returnValue.data()) { + qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in " + "queued connections"); + return false; + } + + int nargs = 1; // include return type + void **args = (void **) qMalloc(paramCount * sizeof(void *)); + Q_CHECK_PTR(args); + int *types = (int *) qMalloc(paramCount * sizeof(int)); + Q_CHECK_PTR(types); + types[0] = 0; // return type + args[0] = 0; + + for (int i = 1; i < paramCount; ++i) { + types[i] = QMetaType::type(typeNames[i]); + if (types[i]) { + args[i] = QMetaType::construct(types[i], param[i]); + ++nargs; + } else if (param[i]) { + qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'", + typeNames[i]); + for (int x = 1; x < i; ++x) { + if (types[x] && args[x]) + QMetaType::destroy(types[x], args[x]); + } + qFree(types); + qFree(args); + return false; + } + } + + QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction, + 0, -1, nargs, types, args)); + } else { // blocking queued connection +#ifndef QT_NO_THREAD + if (currentThread == objectThread) { + qWarning("QMetaMethod::invoke: Dead lock detected in " + "BlockingQueuedConnection: Receiver is %s(%p)", + mobj->className(), object); + } + + QSemaphore semaphore; + QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction, + 0, -1, 0, 0, param, &semaphore)); + semaphore.acquire(); +#endif // QT_NO_THREAD + } + return true; +} + +/*! \fn bool QMetaMethod::invoke(QObject *object, + QGenericReturnArgument returnValue, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) const + \overload invoke() + + This overload always invokes this method using the connection type Qt::AutoConnection. +*/ + +/*! \fn bool QMetaMethod::invoke(QObject *object, + Qt::ConnectionType connectionType, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) const + + \overload invoke() + + This overload can be used if the return value of the member is of no interest. +*/ + +/*! + \fn bool QMetaMethod::invoke(QObject *object, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) const + + \overload invoke() + + This overload invokes this method using the + connection type Qt::AutoConnection and ignores return values. +*/ + +/*! + \class QMetaEnum + \brief The QMetaEnum class provides meta-data about an enumerator. + + \ingroup objectmodel + + Use name() for the enumerator's name. The enumerator's keys (names + of each enumerated item) are returned by key(); use keyCount() to find + the number of keys. isFlag() returns whether the enumerator is + meant to be used as a flag, meaning that its values can be combined + using the OR operator. + + The conversion functions keyToValue(), valueToKey(), keysToValue(), + and valueToKeys() allow conversion between the integer + representation of an enumeration or set value and its literal + representation. The scope() function returns the class scope this + enumerator was declared in. + + \sa QMetaObject, QMetaMethod, QMetaProperty +*/ + +/*! + \fn bool QMetaEnum::isValid() const + + Returns true if this enum is valid (has a name); otherwise returns + false. + + \sa name() +*/ + +/*! + \fn const QMetaObject *QMetaEnum::enclosingMetaObject() const + \internal +*/ + + +/*! + \fn QMetaEnum::QMetaEnum() + \internal +*/ + +/*! + Returns the name of the enumerator (without the scope). + + For example, the Qt::AlignmentFlag enumeration has \c + AlignmentFlag as the name and \l Qt as the scope. + + \sa isValid(), scope() +*/ +const char *QMetaEnum::name() const +{ + if (!mobj) + return 0; + return mobj->d.stringdata + mobj->d.data[handle]; +} + +/*! + Returns the number of keys. + + \sa key() +*/ +int QMetaEnum::keyCount() const +{ + if (!mobj) + return 0; + return mobj->d.data[handle + 2]; +} + + +/*! + Returns the key with the given \a index, or 0 if no such key exists. + + \sa keyCount(), value(), valueToKey() +*/ +const char *QMetaEnum::key(int index) const +{ + if (!mobj) + return 0; + int count = mobj->d.data[handle + 2]; + int data = mobj->d.data[handle + 3]; + if (index >= 0 && index < count) + return mobj->d.stringdata + mobj->d.data[data + 2*index]; + return 0; +} + +/*! + Returns the value with the given \a index; or returns -1 if there + is no such value. + + \sa keyCount(), key(), keyToValue() +*/ +int QMetaEnum::value(int index) const +{ + if (!mobj) + return 0; + int count = mobj->d.data[handle + 2]; + int data = mobj->d.data[handle + 3]; + if (index >= 0 && index < count) + return mobj->d.data[data + 2*index + 1]; + return -1; +} + + +/*! + Returns true if this enumerator is used as a flag; otherwise returns + false. + + When used as flags, enumerators can be combined using the OR + operator. + + \sa keysToValue(), valueToKeys() +*/ +bool QMetaEnum::isFlag() const +{ + return mobj && mobj->d.data[handle + 1]; +} + + +/*! + Returns the scope this enumerator was declared in. + + For example, the Qt::AlignmentFlag enumeration has \c Qt as + the scope and \c AlignmentFlag as the name. + + \sa name() +*/ +const char *QMetaEnum::scope() const +{ + return mobj?mobj->d.stringdata : 0; +} + +/*! + Returns the integer value of the given enumeration \a key, or -1 + if \a key is not defined. + + For flag types, use keysToValue(). + + \sa valueToKey(), isFlag(), keysToValue() +*/ +int QMetaEnum::keyToValue(const char *key) const +{ + if (!mobj || !key) + return -1; + uint scope = 0; + const char *qualified_key = key; + const char *s = key + qstrlen(key); + while (s > key && *s != ':') + --s; + if (s > key && *(s-1)==':') { + scope = s - key - 1; + key += scope + 2; + } + int count = mobj->d.data[handle + 2]; + int data = mobj->d.data[handle + 3]; + for (int i = 0; i < count; ++i) + if ((!scope || (qstrlen(mobj->d.stringdata) == scope && strncmp(qualified_key, mobj->d.stringdata, scope) == 0)) + && strcmp(key, mobj->d.stringdata + mobj->d.data[data + 2*i]) == 0) + return mobj->d.data[data + 2*i + 1]; + return -1; +} + +/*! + Returns the string that is used as the name of the given + enumeration \a value, or 0 if \a value is not defined. + + For flag types, use valueToKeys(). + + \sa isFlag(), valueToKeys() +*/ +const char* QMetaEnum::valueToKey(int value) const +{ + if (!mobj) + return 0; + int count = mobj->d.data[handle + 2]; + int data = mobj->d.data[handle + 3]; + for (int i = 0; i < count; ++i) + if (value == (int)mobj->d.data[data + 2*i + 1]) + return mobj->d.stringdata + mobj->d.data[data + 2*i]; + return 0; +} + +/*! + Returns the value derived from combining together the values of + the \a keys using the OR operator, or -1 if \a keys is not + defined. Note that the strings in \a keys must be '|'-separated. + + \sa isFlag(), valueToKey(), valueToKeys() +*/ +int QMetaEnum::keysToValue(const char *keys) const +{ + if (!mobj) + return -1; + QStringList l = QString::fromLatin1(keys).split(QLatin1Char('|')); + //#### TODO write proper code, do not use QStringList + int value = 0; + int count = mobj->d.data[handle + 2]; + int data = mobj->d.data[handle + 3]; + for (int li = 0; li < l.size(); ++li) { + QString trimmed = l.at(li).trimmed(); + QByteArray qualified_key = trimmed.toLatin1(); + const char *key = qualified_key.constData(); + uint scope = 0; + const char *s = key + qstrlen(key); + while (s > key && *s != ':') + --s; + if (s > key && *(s-1)==':') { + scope = s - key - 1; + key += scope + 2; + } + int i; + for (i = count-1; i >= 0; --i) + if ((!scope || (qstrlen(mobj->d.stringdata) == scope && strncmp(qualified_key.constData(), mobj->d.stringdata, scope) == 0)) + && strcmp(key, mobj->d.stringdata + mobj->d.data[data + 2*i]) == 0) { + value |= mobj->d.data[data + 2*i + 1]; + break; + } + if (i < 0) + value |= -1; + } + return value; +} + +/*! + Returns a byte array of '|'-separated keys that represents the + given \a value. + + \sa isFlag(), valueToKey(), keysToValue() +*/ +QByteArray QMetaEnum::valueToKeys(int value) const +{ + QByteArray keys; + if (!mobj) + return keys; + int count = mobj->d.data[handle + 2]; + int data = mobj->d.data[handle + 3]; + int v = value; + for(int i = 0; i < count; i++) { + int k = mobj->d.data[data + 2*i + 1]; + if ((k != 0 && (v & k) == k ) || (k == value)) { + v = v & ~k; + if (!keys.isEmpty()) + keys += '|'; + keys += mobj->d.stringdata + mobj->d.data[data + 2*i]; + } + } + return keys; +} + +static QByteArray qualifiedName(const QMetaEnum &e) +{ + return QByteArray(e.scope()) + "::" + e.name(); +} + +/*! + \class QMetaProperty + \brief The QMetaProperty class provides meta-data about a property. + + \ingroup objectmodel + + Property meta-data is obtained from an object's meta-object. See + QMetaObject::property() and QMetaObject::propertyCount() for + details. + + \section1 Property Meta-Data + + A property has a name() and a type(), as well as various + attributes that specify its behavior: isReadable(), isWritable(), + isDesignable(), isScriptable(), and isStored(). + + If the property is an enumeration, isEnumType() returns true; if the + property is an enumeration that is also a flag (i.e. its values + can be combined using the OR operator), isEnumType() and + isFlagType() both return true. The enumerator for these types is + available from enumerator(). + + The property's values are set and retrieved with read(), write(), + and reset(); they can also be changed through QObject's set and get + functions. See QObject::setProperty() and QObject::property() for + details. + + \section1 Copying and Assignment + + QMetaProperty objects can be copied by value. However, each copy will + refer to the same underlying property meta-data. + + \sa QMetaObject, QMetaEnum, QMetaMethod, {Qt's Property System} +*/ + +/*! + \fn bool QMetaProperty::isValid() const + + Returns true if this property is valid (readable); otherwise + returns false. + + \sa isReadable() +*/ + +/*! + \fn const QMetaObject *QMetaProperty::enclosingMetaObject() const + \internal +*/ + +/*! + \internal +*/ +QMetaProperty::QMetaProperty() + : mobj(0), handle(0), idx(0) +{ +} + + +/*! + Returns this property's name. + + \sa type(), typeName() +*/ +const char *QMetaProperty::name() const +{ + if (!mobj) + return 0; + int handle = priv(mobj->d.data)->propertyData + 3*idx; + return mobj->d.stringdata + mobj->d.data[handle]; +} + +/*! + Returns the name of this property's type. + + \sa type(), name() +*/ +const char *QMetaProperty::typeName() const +{ + if (!mobj) + return 0; + int handle = priv(mobj->d.data)->propertyData + 3*idx; + return mobj->d.stringdata + mobj->d.data[handle + 1]; +} + +/*! + Returns this property's type. The return value is one + of the values of the QVariant::Type enumeration. + + \sa userType(), typeName(), name() +*/ +QVariant::Type QMetaProperty::type() const +{ + if (!mobj) + return QVariant::Invalid; + int handle = priv(mobj->d.data)->propertyData + 3*idx; + uint flags = mobj->d.data[handle + 2]; + + uint type = flags >> 24; + if (type == 0xff) // special value for QVariant + type = QVariant::LastType; + if (type) + return QVariant::Type(type); + if (isEnumType()) { + int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); + if (enumMetaTypeId == 0) + return QVariant::Int; + } +#ifdef QT_COORD_TYPE + // qreal metatype must be resolved at runtime. + if (strcmp(typeName(), "qreal") == 0) + return QVariant::Type(qMetaTypeId()); +#endif + + return QVariant::UserType; +} + +/*! + \since 4.2 + + Returns this property's user type. The return value is one + of the values that are registered with QMetaType, or 0 if + the type is not registered. + + \sa type(), QMetaType, typeName() + */ +int QMetaProperty::userType() const +{ + QVariant::Type tp = type(); + if (tp != QVariant::UserType) + return tp; + if (isEnumType()) { + int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); + return enumMetaTypeId; + } + return QMetaType::type(typeName()); +} + +/*! + \since 4.6 + + Returns this property's index. +*/ +int QMetaProperty::propertyIndex() const +{ + if (!mobj) + return -1; + return idx + mobj->propertyOffset(); +} + +/*! + Returns true if the property's type is an enumeration value that + is used as a flag; otherwise returns false. + + Flags can be combined using the OR operator. A flag type is + implicitly also an enum type. + + \sa isEnumType(), enumerator(), QMetaEnum::isFlag() +*/ + +bool QMetaProperty::isFlagType() const +{ + return isEnumType() && menum.isFlag(); +} + +/*! + Returns true if the property's type is an enumeration value; + otherwise returns false. + + \sa enumerator(), isFlagType() +*/ +bool QMetaProperty::isEnumType() const +{ + if (!mobj) + return false; + int handle = priv(mobj->d.data)->propertyData + 3*idx; + int flags = mobj->d.data[handle + 2]; + return (flags & EnumOrFlag) && menum.name(); +} + +/*! + \internal + + Returns true if the property has a C++ setter function that + follows Qt's standard "name" / "setName" pattern. Designer and uic + query hasStdCppSet() in order to avoid expensive + QObject::setProperty() calls. All properties in Qt [should] follow + this pattern. +*/ +bool QMetaProperty::hasStdCppSet() const +{ + if (!mobj) + return false; + int handle = priv(mobj->d.data)->propertyData + 3*idx; + int flags = mobj->d.data[handle + 2]; + return (flags & StdCppSet); +} + +/*! + Returns the enumerator if this property's type is an enumerator + type; otherwise the returned value is undefined. + + \sa isEnumType(), isFlagType() +*/ +QMetaEnum QMetaProperty::enumerator() const +{ + return menum; +} + +/*! + Reads the property's value from the given \a object. Returns the value + if it was able to read it; otherwise returns an invalid variant. + + \sa write(), reset(), isReadable() +*/ +QVariant QMetaProperty::read(const QObject *object) const +{ + if (!object || !mobj) + return QVariant(); + + uint t = QVariant::Int; + if (isEnumType()) { + /* + try to create a QVariant that can be converted to this enum + type (only works if the enum has already been registered + with QMetaType) + */ + int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); + if (enumMetaTypeId != 0) + t = enumMetaTypeId; + } else { + int handle = priv(mobj->d.data)->propertyData + 3*idx; + uint flags = mobj->d.data[handle + 2]; + const char *typeName = mobj->d.stringdata + mobj->d.data[handle + 1]; + t = (flags >> 24); + if (t == 0xff) // special value for QVariant + t = QVariant::LastType; + if (t == QVariant::Invalid) + t = QMetaType::type(typeName); + if (t == QVariant::Invalid) + t = QVariant::nameToType(typeName); + if (t == QVariant::Invalid || t == QVariant::UserType) { + if (t == QVariant::Invalid) + qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property '%s::%s'", typeName, mobj->className(), name()); + return QVariant(); + } + } + + // the status variable is changed by qt_metacall to indicate what it did + // this feature is currently only used by QtDBus and should not be depended + // upon. Don't change it without looking into QDBusAbstractInterface first + // -1 (unchanged): normal qt_metacall, result stored in argv[0] + // changed: result stored directly in value + int status = -1; + QVariant value; + void *argv[] = { 0, &value, &status }; + if (t == QVariant::LastType) { + argv[0] = &value; + } else { + value = QVariant(t, (void*)0); + argv[0] = value.data(); + } + QMetaObject::metacall(const_cast(object), QMetaObject::ReadProperty, + idx + mobj->propertyOffset(), argv); + + if (status != -1) + return value; + if (t != QVariant::LastType && argv[0] != value.data()) + // pointer or reference + return QVariant((QVariant::Type)t, argv[0]); + return value; +} + +/*! + Writes \a value as the property's value to the given \a object. Returns + true if the write succeeded; otherwise returns false. + + \sa read(), reset(), isWritable() +*/ +bool QMetaProperty::write(QObject *object, const QVariant &value) const +{ + if (!object || !isWritable()) + return false; + + QVariant v = value; + uint t = QVariant::Invalid; + if (isEnumType()) { + if (v.type() == QVariant::String +#ifdef QT3_SUPPORT + || v.type() == QVariant::CString +#endif + ) { + if (isFlagType()) + v = QVariant(menum.keysToValue(value.toByteArray())); + else + v = QVariant(menum.keyToValue(value.toByteArray())); + } else if (v.type() != QVariant::Int && v.type() != QVariant::UInt) { + int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); + if ((enumMetaTypeId == 0) || (v.userType() != enumMetaTypeId) || !v.constData()) + return false; + v = QVariant(*reinterpret_cast(v.constData())); + } + v.convert(QVariant::Int); + } else { + int handle = priv(mobj->d.data)->propertyData + 3*idx; + uint flags = mobj->d.data[handle + 2]; + t = flags >> 24; + if (t == 0xff) // special value for QVariant + t = QVariant::LastType; + if (t == QVariant::Invalid) { + const char *typeName = mobj->d.stringdata + mobj->d.data[handle + 1]; + const char *vtypeName = value.typeName(); + if (vtypeName && strcmp(typeName, vtypeName) == 0) + t = value.userType(); + else + t = QVariant::nameToType(typeName); + } + if (t == QVariant::Invalid) + return false; + if (t != QVariant::LastType && t != (uint)value.userType() && (t < QMetaType::User && !v.convert((QVariant::Type)t))) + return false; + } + + // the status variable is changed by qt_metacall to indicate what it did + // this feature is currently only used by QtDBus and should not be depended + // upon. Don't change it without looking into QDBusAbstractInterface first + // -1 (unchanged): normal qt_metacall, result stored in argv[0] + // changed: result stored directly in value, return the value of status + int status = -1; + // the flags variable is used by the declarative module to implement + // interception of property writes. + int flags = 0; + void *argv[] = { 0, &v, &status, &flags }; + if (t == QVariant::LastType) + argv[0] = &v; + else + argv[0] = v.data(); + QMetaObject::metacall(object, QMetaObject::WriteProperty, idx + mobj->propertyOffset(), argv); + return status; +} + +/*! + Resets the property for the given \a object with a reset method. + Returns true if the reset worked; otherwise returns false. + + Reset methods are optional; only a few properties support them. + + \sa read(), write() +*/ +bool QMetaProperty::reset(QObject *object) const +{ + if (!object || !mobj || !isResettable()) + return false; + void *argv[] = { 0 }; + QMetaObject::metacall(object, QMetaObject::ResetProperty, idx + mobj->propertyOffset(), argv); + return true; +} + +/*! + Returns true if this property can be reset to a default value; otherwise + returns false. + + \sa reset() +*/ +bool QMetaProperty::isResettable() const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + return flags & Resettable; +} + +/*! + Returns true if this property is readable; otherwise returns false. + + \sa isWritable(), read(), isValid() + */ +bool QMetaProperty::isReadable() const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + return flags & Readable; +} + +/*! + Returns true if this property has a corresponding change notify signal; + otherwise returns false. + + \sa notifySignal() + */ +bool QMetaProperty::hasNotifySignal() const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + return flags & Notify; +} + +/*! + \since 4.5 + + Returns the QMetaMethod instance of the property change notifying signal if + one was specified, otherwise returns an invalid QMetaMethod. + + \sa hasNotifySignal() + */ +QMetaMethod QMetaProperty::notifySignal() const +{ + int id = notifySignalIndex(); + if (id != -1) + return mobj->method(id); + else + return QMetaMethod(); +} + +/*! + \since 4.6 + + Returns the index of the property change notifying signal if one was + specified, otherwise returns -1. + + \sa hasNotifySignal() + */ +int QMetaProperty::notifySignalIndex() const +{ + if (hasNotifySignal()) { + int offset = priv(mobj->d.data)->propertyData + + priv(mobj->d.data)->propertyCount * 3 + idx; + return mobj->d.data[offset] + mobj->methodOffset(); + } else { + return -1; + } +} + +/*! + \internal + + Returns the property revision if one was + specified by REVISION, otherwise returns 0. + */ +int QMetaProperty::revision() const +{ + if (!mobj) + return 0; + int flags = mobj->d.data[handle + 2]; + if (flags & Revisioned) { + int offset = priv(mobj->d.data)->propertyData + + priv(mobj->d.data)->propertyCount * 3 + idx; + // Revision data is placed after NOTIFY data, if present. + // Iterate through properties to discover whether we have NOTIFY signals. + for (int i = 0; i < priv(mobj->d.data)->propertyCount; ++i) { + int handle = priv(mobj->d.data)->propertyData + 3*i; + if (mobj->d.data[handle + 2] & Notify) { + offset += priv(mobj->d.data)->propertyCount; + break; + } + } + return mobj->d.data[offset]; + } else { + return 0; + } +} + +/*! + Returns true if this property is writable; otherwise returns + false. + + \sa isReadable(), write() + */ +bool QMetaProperty::isWritable() const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + return flags & Writable; +} + + +/*! + Returns true if this property is designable for the given \a object; + otherwise returns false. + + If no \a object is given, the function returns false if the + \c{Q_PROPERTY()}'s \c DESIGNABLE attribute is false; otherwise + returns true (if the attribute is true or is a function or expression). + + \sa isScriptable(), isStored() +*/ +bool QMetaProperty::isDesignable(const QObject *object) const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + bool b = flags & Designable; + if (object) { + void *argv[] = { &b }; + QMetaObject::metacall(const_cast(object), QMetaObject::QueryPropertyDesignable, + idx + mobj->propertyOffset(), argv); + } + return b; + + +} + +/*! + Returns true if the property is scriptable for the given \a object; + otherwise returns false. + + If no \a object is given, the function returns false if the + \c{Q_PROPERTY()}'s \c SCRIPTABLE attribute is false; otherwise returns + true (if the attribute is true or is a function or expression). + + \sa isDesignable(), isStored() +*/ +bool QMetaProperty::isScriptable(const QObject *object) const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + bool b = flags & Scriptable; + if (object) { + void *argv[] = { &b }; + QMetaObject::metacall(const_cast(object), QMetaObject::QueryPropertyScriptable, + idx + mobj->propertyOffset(), argv); + } + return b; +} + +/*! + Returns true if the property is stored for \a object; otherwise returns + false. + + If no \a object is given, the function returns false if the + \c{Q_PROPERTY()}'s \c STORED attribute is false; otherwise returns + true (if the attribute is true or is a function or expression). + + \sa isDesignable(), isScriptable() +*/ +bool QMetaProperty::isStored(const QObject *object) const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + bool b = flags & Stored; + if (object) { + void *argv[] = { &b }; + QMetaObject::metacall(const_cast(object), QMetaObject::QueryPropertyStored, + idx + mobj->propertyOffset(), argv); + } + return b; +} + +/*! + Returns true if this property is designated as the \c USER + property, i.e., the one that the user can edit for \a object or + that is significant in some other way. Otherwise it returns + false. e.g., the \c text property is the \c USER editable property + of a QLineEdit. + + If \a object is null, the function returns false if the \c + {Q_PROPERTY()}'s \c USER attribute is false. Otherwise it returns + true. + + \sa QMetaObject::userProperty(), isDesignable(), isScriptable() +*/ +bool QMetaProperty::isUser(const QObject *object) const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + bool b = flags & User; + if (object) { + void *argv[] = { &b }; + QMetaObject::metacall(const_cast(object), QMetaObject::QueryPropertyUser, + idx + mobj->propertyOffset(), argv); + } + return b; +} + +/*! + \since 4.6 + Returns true if the property is constant; otherwise returns false. + + A property is constant if the \c{Q_PROPERTY()}'s \c CONSTANT attribute + is set. +*/ +bool QMetaProperty::isConstant() const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + return flags & Constant; +} + +/*! + \since 4.6 + Returns true if the property is final; otherwise returns false. + + A property is final if the \c{Q_PROPERTY()}'s \c FINAL attribute + is set. +*/ +bool QMetaProperty::isFinal() const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + return flags & Final; +} + +/*! + \obsolete + + Returns true if the property is editable for the given \a object; + otherwise returns false. + + If no \a object is given, the function returns false if the + \c{Q_PROPERTY()}'s \c EDITABLE attribute is false; otherwise returns + true (if the attribute is true or is a function or expression). + + \sa isDesignable(), isScriptable(), isStored() +*/ +bool QMetaProperty::isEditable(const QObject *object) const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + bool b = flags & Editable; + if (object) { + void *argv[] = { &b }; + QMetaObject::metacall(const_cast(object), QMetaObject::QueryPropertyEditable, + idx + mobj->propertyOffset(), argv); + } + return b; +} + +/*! + \class QMetaClassInfo + + \brief The QMetaClassInfo class provides additional information + about a class. + + \ingroup objectmodel + + Class information items are simple \e{name}--\e{value} pairs that + are specified using Q_CLASSINFO() in the source code. The + information can be retrieved using name() and value(). For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp 5 + + This mechanism is free for you to use in your Qt applications. Qt + doesn't use it for any of its classes. + + \sa QMetaObject +*/ + + +/*! + \fn QMetaClassInfo::QMetaClassInfo() + \internal +*/ + +/*! + \fn const QMetaObject *QMetaClassInfo::enclosingMetaObject() const + \internal +*/ + +/*! + Returns the name of this item. + + \sa value() +*/ +const char *QMetaClassInfo::name() const +{ + if (!mobj) + return 0; + return mobj->d.stringdata + mobj->d.data[handle]; +} + +/*! + Returns the value of this item. + + \sa name() +*/ +const char* QMetaClassInfo::value() const +{ + if (!mobj) + return 0; + return mobj->d.stringdata + mobj->d.data[handle + 1]; +} + +/*! + \macro QGenericArgument Q_ARG(Type, const Type &value) + \relates QMetaObject + + This macro takes a \a Type and a \a value of that type and + returns a \l QGenericArgument object that can be passed to + QMetaObject::invokeMethod(). + + \sa Q_RETURN_ARG() +*/ + +/*! + \macro QGenericReturnArgument Q_RETURN_ARG(Type, Type &value) + \relates QMetaObject + + This macro takes a \a Type and a non-const reference to a \a + value of that type and returns a QGenericReturnArgument object + that can be passed to QMetaObject::invokeMethod(). + + \sa Q_ARG() +*/ + +/*! + \class QGenericArgument + + \brief The QGenericArgument class is an internal helper class for + marshalling arguments. + + This class should never be used directly. Please use the \l Q_ARG() + macro instead. + + \sa Q_ARG(), QMetaObject::invokeMethod(), QGenericReturnArgument +*/ + +/*! + \fn QGenericArgument::QGenericArgument(const char *name, const void *data) + + Constructs a QGenericArgument object with the given \a name and \a data. +*/ + +/*! + \fn QGenericArgument::data () const + + Returns the data set in the constructor. +*/ + +/*! + \fn QGenericArgument::name () const + + Returns the name set in the constructor. +*/ + +/*! + \class QGenericReturnArgument + + \brief The QGenericReturnArgument class is an internal helper class for + marshalling arguments. + + This class should never be used directly. Please use the + Q_RETURN_ARG() macro instead. + + \sa Q_RETURN_ARG(), QMetaObject::invokeMethod(), QGenericArgument +*/ + +/*! + \fn QGenericReturnArgument::QGenericReturnArgument(const char *name, void *data) + + Constructs a QGenericReturnArgument object with the given \a name + and \a data. +*/ + +/*! \internal + If the local_method_index is a cloned method, return the index of the original. + + Example: if the index of "destroyed()" is passed, the index of "destroyed(QObject*)" is returned + */ +int QMetaObjectPrivate::originalClone(const QMetaObject *mobj, int local_method_index) +{ + Q_ASSERT(local_method_index < get(mobj)->methodCount); + int handle = get(mobj)->methodData + 5 * local_method_index; + while (mobj->d.data[handle + 4] & MethodCloned) { + Q_ASSERT(local_method_index > 0); + handle -= 5; + local_method_index--; + } + return local_method_index; +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h new file mode 100644 index 0000000000..e9bba45391 --- /dev/null +++ b/src/corelib/kernel/qmetaobject.h @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QMETAOBJECT_H +#define QMETAOBJECT_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +template class QList; + +class Q_CORE_EXPORT QMetaMethod +{ +public: + inline QMetaMethod() : mobj(0),handle(0) {} + + const char *signature() const; + const char *typeName() const; + QList parameterTypes() const; + QList parameterNames() const; + const char *tag() const; + enum Access { Private, Protected, Public }; + Access access() const; + enum MethodType { Method, Signal, Slot, Constructor }; + MethodType methodType() const; + enum Attributes { Compatibility = 0x1, Cloned = 0x2, Scriptable = 0x4 }; + int attributes() const; + int methodIndex() const; + int revision() const; + + inline const QMetaObject *enclosingMetaObject() const { return mobj; } + + bool invoke(QObject *object, + Qt::ConnectionType connectionType, + QGenericReturnArgument returnValue, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) const; + inline bool invoke(QObject *object, + QGenericReturnArgument returnValue, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) const + { + return invoke(object, Qt::AutoConnection, returnValue, + val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); + } + inline bool invoke(QObject *object, + Qt::ConnectionType connectionType, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) const + { + return invoke(object, connectionType, QGenericReturnArgument(), + val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); + } + inline bool invoke(QObject *object, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) const + { + return invoke(object, Qt::AutoConnection, QGenericReturnArgument(), + val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); + } + +private: + const QMetaObject *mobj; + uint handle; + friend struct QMetaObject; + friend struct QMetaObjectPrivate; + friend class QObject; +}; +Q_DECLARE_TYPEINFO(QMetaMethod, Q_MOVABLE_TYPE); + +class Q_CORE_EXPORT QMetaEnum +{ +public: + inline QMetaEnum() : mobj(0),handle(0) {} + + const char *name() const; + bool isFlag() const; + + int keyCount() const; + const char *key(int index) const; + int value(int index) const; + + const char *scope() const; + + int keyToValue(const char *key) const; + const char* valueToKey(int value) const; + int keysToValue(const char * keys) const; + QByteArray valueToKeys(int value) const; + + inline const QMetaObject *enclosingMetaObject() const { return mobj; } + + inline bool isValid() const { return name() != 0; } +private: + const QMetaObject *mobj; + uint handle; + friend struct QMetaObject; +}; +Q_DECLARE_TYPEINFO(QMetaEnum, Q_MOVABLE_TYPE); + +class Q_CORE_EXPORT QMetaProperty +{ +public: + QMetaProperty(); + + const char *name() const; + const char *typeName() const; + QVariant::Type type() const; + int userType() const; + int propertyIndex() const; + + bool isReadable() const; + bool isWritable() const; + bool isResettable() const; + bool isDesignable(const QObject *obj = 0) const; + bool isScriptable(const QObject *obj = 0) const; + bool isStored(const QObject *obj = 0) const; + bool isEditable(const QObject *obj = 0) const; + bool isUser(const QObject *obj = 0) const; + bool isConstant() const; + bool isFinal() const; + + bool isFlagType() const; + bool isEnumType() const; + QMetaEnum enumerator() const; + + bool hasNotifySignal() const; + QMetaMethod notifySignal() const; + int notifySignalIndex() const; + + int revision() const; + + QVariant read(const QObject *obj) const; + bool write(QObject *obj, const QVariant &value) const; + bool reset(QObject *obj) const; + + bool hasStdCppSet() const; + inline bool isValid() const { return isReadable(); } + inline const QMetaObject *enclosingMetaObject() const { return mobj; } + +private: + const QMetaObject *mobj; + uint handle; + int idx; + QMetaEnum menum; + friend struct QMetaObject; +}; + +class Q_CORE_EXPORT QMetaClassInfo +{ +public: + inline QMetaClassInfo() : mobj(0),handle(0) {} + const char *name() const; + const char *value() const; + inline const QMetaObject *enclosingMetaObject() const { return mobj; } +private: + const QMetaObject *mobj; + uint handle; + friend struct QMetaObject; +}; +Q_DECLARE_TYPEINFO(QMetaClassInfo, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMETAOBJECT_H diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h new file mode 100644 index 0000000000..fdadf4ac9f --- /dev/null +++ b/src/corelib/kernel/qmetaobject_p.h @@ -0,0 +1,326 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QMETAOBJECT_P_H +#define QMETAOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of moc. This header file may change from version to version without notice, +// or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +enum PropertyFlags { + Invalid = 0x00000000, + Readable = 0x00000001, + Writable = 0x00000002, + Resettable = 0x00000004, + EnumOrFlag = 0x00000008, + StdCppSet = 0x00000100, +// Override = 0x00000200, + Constant = 0x00000400, + Final = 0x00000800, + Designable = 0x00001000, + ResolveDesignable = 0x00002000, + Scriptable = 0x00004000, + ResolveScriptable = 0x00008000, + Stored = 0x00010000, + ResolveStored = 0x00020000, + Editable = 0x00040000, + ResolveEditable = 0x00080000, + User = 0x00100000, + ResolveUser = 0x00200000, + Notify = 0x00400000, + Revisioned = 0x00800000 +}; + +enum MethodFlags { + AccessPrivate = 0x00, + AccessProtected = 0x01, + AccessPublic = 0x02, + AccessMask = 0x03, //mask + + MethodMethod = 0x00, + MethodSignal = 0x04, + MethodSlot = 0x08, + MethodConstructor = 0x0c, + MethodTypeMask = 0x0c, + + MethodCompatibility = 0x10, + MethodCloned = 0x20, + MethodScriptable = 0x40, + MethodRevisioned = 0x80 +}; + +enum MetaObjectFlags { + DynamicMetaObject = 0x01 +}; + +class QMutex; + +struct QMetaObjectPrivate +{ + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + int enumeratorCount, enumeratorData; + 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 + // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself + + static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) + { return reinterpret_cast(metaobject->d.data); } + + static int indexOfSignalRelative(const QMetaObject **baseObject, + const char* name, + bool normalizeStringData); + static int indexOfSlotRelative(const QMetaObject **m, + const char *slot, + bool normalizeStringData); + static int originalClone(const QMetaObject *obj, int local_method_index); + +#ifndef QT_NO_QOBJECT + //defined in qobject.cpp + enum DisconnectType { DisconnectAll, DisconnectOne }; + static void memberIndexes(const QObject *obj, const QMetaMethod &member, + int *signalIndex, int *methodIndex); + static bool connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index_relative, + const QMetaObject *rmeta = 0, + int type = 0, int *types = 0); + static bool disconnect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, + DisconnectType = DisconnectAll); + static inline bool disconnectHelper(QObjectPrivate::Connection *c, + const QObject *receiver, int method_index, + QMutex *senderMutex, DisconnectType); +#endif +}; + +#ifndef UTILS_H +// mirrored in moc's utils.h +static inline bool is_ident_char(char s) +{ + return ((s >= 'a' && s <= 'z') + || (s >= 'A' && s <= 'Z') + || (s >= '0' && s <= '9') + || s == '_' + ); +} + +static inline bool is_space(char s) +{ + return (s == ' ' || s == '\t'); +} +#endif + +// This code is shared with moc.cpp +static QByteArray normalizeTypeInternal(const char *t, const char *e, bool fixScope = false, bool adjustConst = true) +{ + int len = e - t; + /* + Convert 'char const *' into 'const char *'. Start at index 1, + not 0, because 'const char *' is already OK. + */ + QByteArray constbuf; + for (int i = 1; i < len; i++) { + if ( t[i] == 'c' + && strncmp(t + i + 1, "onst", 4) == 0 + && (i + 5 >= len || !is_ident_char(t[i + 5])) + && !is_ident_char(t[i-1]) + ) { + constbuf = QByteArray(t, len); + if (is_space(t[i-1])) + constbuf.remove(i-1, 6); + else + constbuf.remove(i, 5); + constbuf.prepend("const "); + t = constbuf.data(); + e = constbuf.data() + constbuf.length(); + break; + } + /* + We musn't convert 'char * const *' into 'const char **' + and we must beware of 'Bar'. + */ + if (t[i] == '&' || t[i] == '*' ||t[i] == '<') + break; + } + if (adjustConst && e > t + 6 && strncmp("const ", t, 6) == 0) { + if (*(e-1) == '&') { // treat const reference as value + t += 6; + --e; + } else if (is_ident_char(*(e-1)) || *(e-1) == '>') { // treat const value as value + t += 6; + } + } + QByteArray result; + result.reserve(len); + +#if 1 + // consume initial 'const ' + if (strncmp("const ", t, 6) == 0) { + t+= 6; + result += "const "; + } +#endif + + // some type substitutions for 'unsigned x' + if (strncmp("unsigned", t, 8) == 0) { + // make sure "unsigned" is an isolated word before making substitutions + if (!t[8] || !is_ident_char(t[8])) { + if (strncmp(" int", t+8, 4) == 0) { + t += 8+4; + result += "uint"; + } else if (strncmp(" long", t+8, 5) == 0) { + if ((strlen(t + 8 + 5) < 4 || strncmp(t + 8 + 5, " int", 4) != 0) // preserve '[unsigned] long int' + && (strlen(t + 8 + 5) < 5 || strncmp(t + 8 + 5, " long", 5) != 0) // preserve '[unsigned] long long' + ) { + t += 8+5; + result += "ulong"; + } + } else if (strncmp(" short", t+8, 6) != 0 // preserve unsigned short + && strncmp(" char", t+8, 5) != 0) { // preserve unsigned char + // treat rest (unsigned) as uint + t += 8; + result += "uint"; + } + } + } else { + // discard 'struct', 'class', and 'enum'; they are optional + // and we don't want them in the normalized signature + struct { + const char *keyword; + int len; + } optional[] = { + { "struct ", 7 }, + { "class ", 6 }, + { "enum ", 5 }, + { 0, 0 } + }; + int i = 0; + do { + if (strncmp(optional[i].keyword, t, optional[i].len) == 0) { + t += optional[i].len; + break; + } + } while (optional[++i].keyword != 0); + } + + bool star = false; + while (t != e) { + char c = *t++; + if (fixScope && c == ':' && *t == ':' ) { + ++t; + c = *t++; + int i = result.size() - 1; + while (i >= 0 && is_ident_char(result.at(i))) + --i; + result.resize(i + 1); + } + star = star || c == '*'; + result += c; + if (c == '<') { + //template recursion + const char* tt = t; + int templdepth = 1; + while (t != e) { + c = *t++; + if (c == '<') + ++templdepth; + if (c == '>') + --templdepth; + if (templdepth == 0 || (templdepth == 1 && c == ',')) { + result += normalizeTypeInternal(tt, t-1, fixScope, false); + result += c; + if (templdepth == 0) { + if (*t == '>') + result += ' '; // avoid >> + break; + } + tt = t; + } + } + } + + // cv qualifers can appear after the type as well + if (!is_ident_char(c) && t != e && (e - t >= 5 && strncmp("const", t, 5) == 0) + && (e - t == 5 || !is_ident_char(t[5]))) { + t += 5; + while (t != e && is_space(*t)) + ++t; + if (adjustConst && t != e && *t == '&') { + // treat const ref as value + ++t; + } else if (adjustConst && !star) { + // treat const as value + } 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; +} + + +QT_END_NAMESPACE + +#endif + diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp new file mode 100644 index 0000000000..bdc96c638b --- /dev/null +++ b/src/corelib/kernel/qmetatype.cpp @@ -0,0 +1,1509 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qmetatype.h" +#include "qobjectdefs.h" +#include "qdatetime.h" +#include "qbytearray.h" +#include "qreadwritelock.h" +#include "qstring.h" +#include "qstringlist.h" +#include "qvector.h" +#include "qlocale.h" +#include "qeasingcurve.h" + +#ifdef QT_BOOTSTRAPPED +# ifndef QT_NO_GEOM_VARIANT +# define QT_NO_GEOM_VARIANT +# endif +#else +# include "qbitarray.h" +# include "qurl.h" +# include "qvariant.h" +#endif + +#ifndef QT_NO_GEOM_VARIANT +# include "qsize.h" +# include "qpoint.h" +# include "qrect.h" +# include "qline.h" +#endif + +QT_BEGIN_NAMESPACE + +#define NS(x) QT_PREPEND_NAMESPACE(x) + +/*! + \macro Q_DECLARE_METATYPE(Type) + \relates QMetaType + + This macro makes the type \a Type known to QMetaType as long as it + provides a public default constructor, a public copy constructor and + a public destructor. + It is needed to use the type \a Type as a custom type in QVariant. + + Ideally, this macro should be placed below the declaration of + the class or struct. If that is not possible, it can be put in + a private header file which has to be included every time that + type is used in a QVariant. + + Adding a Q_DECLARE_METATYPE() makes the type known to all template + based functions, including QVariant. Note that if you intend to + use the type in \e queued signal and slot connections or in + QObject's property system, you also have to call + qRegisterMetaType() since the names are resolved at runtime. + + This example shows a typical use case of Q_DECLARE_METATYPE(): + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp 0 + + If \c MyStruct is in a namespace, the Q_DECLARE_METATYPE() macro + has to be outside the namespace: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp 1 + + Since \c{MyStruct} is now known to QMetaType, it can be used in QVariant: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp 2 + + \sa qRegisterMetaType() +*/ + +/*! + \enum QMetaType::Type + + These are the built-in types supported by QMetaType: + + \value Void \c void + \value Bool \c bool + \value Int \c int + \value UInt \c{unsigned int} + \value Double \c double + \value QChar QChar + \value QString QString + \value QByteArray QByteArray + + \value VoidStar \c{void *} + \value Long \c{long} + \value LongLong LongLong + \value Short \c{short} + \value Char \c{char} + \value ULong \c{unsigned long} + \value ULongLong ULongLong + \value UShort \c{unsigned short} + \value UChar \c{unsigned char} + \value Float \c float + \value QObjectStar QObject * + \value QWidgetStar QWidget * + \value QVariant QVariant + + \value QColorGroup QColorGroup + \value QCursor QCursor + \value QDate QDate + \value QSize QSize + \value QTime QTime + \value QVariantList QVariantList + \value QPolygon QPolygon + \value QColor QColor + \value QSizeF QSizeF + \value QRectF QRectF + \value QLine QLine + \value QTextLength QTextLength + \value QStringList QStringList + \value QVariantMap QVariantMap + \value QVariantHash QVariantHash + \value QIcon QIcon + \value QPen QPen + \value QLineF QLineF + \value QTextFormat QTextFormat + \value QRect QRect + \value QPoint QPoint + \value QUrl QUrl + \value QRegExp QRegExp + \value QDateTime QDateTime + \value QPointF QPointF + \value QPalette QPalette + \value QFont QFont + \value QBrush QBrush + \value QRegion QRegion + \value QBitArray QBitArray + \value QImage QImage + \value QKeySequence QKeySequence + \value QSizePolicy QSizePolicy + \value QPixmap QPixmap + \value QLocale QLocale + \value QBitmap QBitmap + \value QMatrix QMatrix + \value QTransform QTransform + \value QMatrix4x4 QMatrix4x4 + \value QVector2D QVector2D + \value QVector3D QVector3D + \value QVector4D QVector4D + \value QQuaternion QQuaternion + \value QEasingCurve QEasingCurve + + \value User Base value for user types + + \omitvalue FirstCoreExtType + \omitvalue FirstGuiType + \omitvalue LastCoreExtType + \omitvalue LastCoreType + \omitvalue LastGuiType + \omitvalue QReal + + Additional types can be registered using Q_DECLARE_METATYPE(). + + \sa type(), typeName() +*/ + +/*! + \class QMetaType + \brief The QMetaType class manages named types in the meta-object system. + + \ingroup objectmodel + \threadsafe + + The class is used as a helper to marshall types in QVariant and + in queued signals and slots connections. It associates a type + name to a type so that it can be created and destructed + dynamically at run-time. Declare new types with Q_DECLARE_METATYPE() + to make them available to QVariant and other template-based functions. + Call qRegisterMetaType() to make type available to non-template based + functions, such as the queued signal and slot connections. + + Any class or struct that has a public default + constructor, a public copy constructor, and a public destructor + can be registered. + + The following code allocates and destructs an instance of + \c{MyClass}: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp 3 + + If we want the stream operators \c operator<<() and \c + operator>>() to work on QVariant objects that store custom types, + the custom type must provide \c operator<<() and \c operator>>() + operators. + + \sa Q_DECLARE_METATYPE(), QVariant::setValue(), QVariant::value(), QVariant::fromValue() +*/ + +#define QT_ADD_STATIC_METATYPE(STR, TP) \ + { STR, sizeof(STR) - 1, TP } + +/* Note: these MUST be in the order of the enums */ +static const struct { const char * typeName; int typeNameLength; int type; } types[] = { + + /* All Core types */ + QT_ADD_STATIC_METATYPE("void", QMetaType::Void), + QT_ADD_STATIC_METATYPE("bool", QMetaType::Bool), + QT_ADD_STATIC_METATYPE("int", QMetaType::Int), + QT_ADD_STATIC_METATYPE("uint", QMetaType::UInt), + QT_ADD_STATIC_METATYPE("qlonglong", QMetaType::LongLong), + QT_ADD_STATIC_METATYPE("qulonglong", QMetaType::ULongLong), + QT_ADD_STATIC_METATYPE("double", QMetaType::Double), + QT_ADD_STATIC_METATYPE("QChar", QMetaType::QChar), + QT_ADD_STATIC_METATYPE("QVariantMap", QMetaType::QVariantMap), + QT_ADD_STATIC_METATYPE("QVariantList", QMetaType::QVariantList), + QT_ADD_STATIC_METATYPE("QString", QMetaType::QString), + QT_ADD_STATIC_METATYPE("QStringList", QMetaType::QStringList), + QT_ADD_STATIC_METATYPE("QByteArray", QMetaType::QByteArray), + QT_ADD_STATIC_METATYPE("QBitArray", QMetaType::QBitArray), + QT_ADD_STATIC_METATYPE("QDate", QMetaType::QDate), + QT_ADD_STATIC_METATYPE("QTime", QMetaType::QTime), + QT_ADD_STATIC_METATYPE("QDateTime", QMetaType::QDateTime), + QT_ADD_STATIC_METATYPE("QUrl", QMetaType::QUrl), + QT_ADD_STATIC_METATYPE("QLocale", QMetaType::QLocale), + QT_ADD_STATIC_METATYPE("QRect", QMetaType::QRect), + QT_ADD_STATIC_METATYPE("QRectF", QMetaType::QRectF), + QT_ADD_STATIC_METATYPE("QSize", QMetaType::QSize), + QT_ADD_STATIC_METATYPE("QSizeF", QMetaType::QSizeF), + QT_ADD_STATIC_METATYPE("QLine", QMetaType::QLine), + QT_ADD_STATIC_METATYPE("QLineF", QMetaType::QLineF), + QT_ADD_STATIC_METATYPE("QPoint", QMetaType::QPoint), + QT_ADD_STATIC_METATYPE("QPointF", QMetaType::QPointF), + QT_ADD_STATIC_METATYPE("QRegExp", QMetaType::QRegExp), + QT_ADD_STATIC_METATYPE("QVariantHash", QMetaType::QVariantHash), + QT_ADD_STATIC_METATYPE("QEasingCurve", QMetaType::QEasingCurve), + + /* All GUI types */ + QT_ADD_STATIC_METATYPE("QColorGroup", 63), + QT_ADD_STATIC_METATYPE("QFont", QMetaType::QFont), + QT_ADD_STATIC_METATYPE("QPixmap", QMetaType::QPixmap), + QT_ADD_STATIC_METATYPE("QBrush", QMetaType::QBrush), + QT_ADD_STATIC_METATYPE("QColor", QMetaType::QColor), + QT_ADD_STATIC_METATYPE("QPalette", QMetaType::QPalette), + QT_ADD_STATIC_METATYPE("QIcon", QMetaType::QIcon), + QT_ADD_STATIC_METATYPE("QImage", QMetaType::QImage), + QT_ADD_STATIC_METATYPE("QPolygon", QMetaType::QPolygon), + QT_ADD_STATIC_METATYPE("QRegion", QMetaType::QRegion), + QT_ADD_STATIC_METATYPE("QBitmap", QMetaType::QBitmap), + QT_ADD_STATIC_METATYPE("QCursor", QMetaType::QCursor), + QT_ADD_STATIC_METATYPE("QSizePolicy", QMetaType::QSizePolicy), + QT_ADD_STATIC_METATYPE("QKeySequence", QMetaType::QKeySequence), + QT_ADD_STATIC_METATYPE("QPen", QMetaType::QPen), + QT_ADD_STATIC_METATYPE("QTextLength", QMetaType::QTextLength), + QT_ADD_STATIC_METATYPE("QTextFormat", QMetaType::QTextFormat), + QT_ADD_STATIC_METATYPE("QMatrix", QMetaType::QMatrix), + QT_ADD_STATIC_METATYPE("QTransform", QMetaType::QTransform), + QT_ADD_STATIC_METATYPE("QMatrix4x4", QMetaType::QMatrix4x4), + QT_ADD_STATIC_METATYPE("QVector2D", QMetaType::QVector2D), + QT_ADD_STATIC_METATYPE("QVector3D", QMetaType::QVector3D), + QT_ADD_STATIC_METATYPE("QVector4D", QMetaType::QVector4D), + QT_ADD_STATIC_METATYPE("QQuaternion", QMetaType::QQuaternion), + + /* All Metatype builtins */ + QT_ADD_STATIC_METATYPE("void*", QMetaType::VoidStar), + QT_ADD_STATIC_METATYPE("long", QMetaType::Long), + QT_ADD_STATIC_METATYPE("short", QMetaType::Short), + QT_ADD_STATIC_METATYPE("char", QMetaType::Char), + QT_ADD_STATIC_METATYPE("ulong", QMetaType::ULong), + QT_ADD_STATIC_METATYPE("ushort", QMetaType::UShort), + QT_ADD_STATIC_METATYPE("uchar", QMetaType::UChar), + QT_ADD_STATIC_METATYPE("float", QMetaType::Float), + QT_ADD_STATIC_METATYPE("QObject*", QMetaType::QObjectStar), + QT_ADD_STATIC_METATYPE("QWidget*", QMetaType::QWidgetStar), + QT_ADD_STATIC_METATYPE("QVariant", QMetaType::QVariant), + + /* Type aliases - order doesn't matter */ + QT_ADD_STATIC_METATYPE("unsigned long", QMetaType::ULong), + QT_ADD_STATIC_METATYPE("unsigned int", QMetaType::UInt), + QT_ADD_STATIC_METATYPE("unsigned short", QMetaType::UShort), + QT_ADD_STATIC_METATYPE("unsigned char", QMetaType::UChar), + QT_ADD_STATIC_METATYPE("long long", QMetaType::LongLong), + QT_ADD_STATIC_METATYPE("unsigned long long", QMetaType::ULongLong), + QT_ADD_STATIC_METATYPE("qint8", QMetaType::Char), + QT_ADD_STATIC_METATYPE("signed char", QMetaType::Char), + QT_ADD_STATIC_METATYPE("quint8", QMetaType::UChar), + QT_ADD_STATIC_METATYPE("qint16", QMetaType::Short), + QT_ADD_STATIC_METATYPE("quint16", QMetaType::UShort), + QT_ADD_STATIC_METATYPE("qint32", QMetaType::Int), + QT_ADD_STATIC_METATYPE("quint32", QMetaType::UInt), + QT_ADD_STATIC_METATYPE("qint64", QMetaType::LongLong), + QT_ADD_STATIC_METATYPE("quint64", QMetaType::ULongLong), + QT_ADD_STATIC_METATYPE("QList", QMetaType::QVariantList), + QT_ADD_STATIC_METATYPE("QMap", QMetaType::QVariantMap), + QT_ADD_STATIC_METATYPE("QHash", QMetaType::QVariantHash), + // let QMetaTypeId2 figure out the type at compile time + QT_ADD_STATIC_METATYPE("qreal", QMetaTypeId2::MetaType), + + {0, 0, QMetaType::Void} +}; + +struct QMetaTypeGuiHelper +{ + QMetaType::Constructor constr; + QMetaType::Destructor destr; +#ifndef QT_NO_DATASTREAM + QMetaType::SaveOperator saveOp; + QMetaType::LoadOperator loadOp; +#endif +}; +Q_CORE_EXPORT const QMetaTypeGuiHelper *qMetaTypeGuiHelper = 0; + +class QCustomTypeInfo +{ +public: + QCustomTypeInfo() : typeName(), constr(0), destr(0) +#ifndef QT_NO_DATASTREAM + , saveOp(0), loadOp(0) +#endif + {} + + QByteArray typeName; + QMetaType::Constructor constr; + QMetaType::Destructor destr; +#ifndef QT_NO_DATASTREAM + QMetaType::SaveOperator saveOp; + QMetaType::LoadOperator loadOp; +#endif + int alias; +}; + +Q_DECLARE_TYPEINFO(QCustomTypeInfo, Q_MOVABLE_TYPE); +Q_GLOBAL_STATIC(QVector, customTypes) +Q_GLOBAL_STATIC(QReadWriteLock, customTypesLock) + +#ifndef QT_NO_DATASTREAM +/*! \internal +*/ +void QMetaType::registerStreamOperators(const char *typeName, SaveOperator saveOp, + LoadOperator loadOp) +{ + int idx = type(typeName); + if (!idx) + return; + registerStreamOperators(idx, saveOp, loadOp); +} + +/*! \internal +*/ +void QMetaType::registerStreamOperators(int idx, SaveOperator saveOp, + LoadOperator loadOp) +{ + if (idx < User) + return; //builtin types should not be registered; + QVector *ct = customTypes(); + if (!ct) + return; + QWriteLocker locker(customTypesLock()); + QCustomTypeInfo &inf = (*ct)[idx - User]; + inf.saveOp = saveOp; + inf.loadOp = loadOp; +} +#endif // QT_NO_DATASTREAM + +/*! + Returns the type name associated with the given \a type, or 0 if no + matching type was found. The returned pointer must not be deleted. + + \sa type(), isRegistered(), Type +*/ +const char *QMetaType::typeName(int type) +{ + enum { GuiTypeCount = LastGuiType - FirstGuiType }; + + if (type >= 0 && type <= LastCoreType) { + return types[type].typeName; + } else if (type >= FirstGuiType && type <= LastGuiType) { + return types[type - FirstGuiType + LastCoreType + 1].typeName; + } else if (type >= FirstCoreExtType && type <= LastCoreExtType) { + return types[type - FirstCoreExtType + GuiTypeCount + LastCoreType + 2].typeName; + } else if (type >= User) { + const QVector * const ct = customTypes(); + QReadLocker locker(customTypesLock()); + return ct && ct->count() > type - User && !ct->at(type - User).typeName.isEmpty() + ? ct->at(type - User).typeName.constData() + : static_cast(0); + } + + return 0; +} + +/*! \internal + Similar to QMetaType::type(), but only looks in the static set of types. +*/ +static inline int qMetaTypeStaticType(const char *typeName, int length) +{ + int i = 0; + while (types[i].typeName && ((length != types[i].typeNameLength) + || strcmp(typeName, types[i].typeName))) { + ++i; + } + return types[i].type; +} + +/*! \internal + Similar to QMetaType::type(), but only looks in the custom set of + types, and doesn't lock the mutex. +*/ +static int qMetaTypeCustomType_unlocked(const char *typeName, int length) +{ + const QVector * const ct = customTypes(); + if (!ct) + return 0; + + for (int v = 0; v < ct->count(); ++v) { + const QCustomTypeInfo &customInfo = ct->at(v); + if ((length == customInfo.typeName.size()) + && !strcmp(typeName, customInfo.typeName.constData())) { + if (customInfo.alias >= 0) + return customInfo.alias; + return v + QMetaType::User; + } + } + return 0; +} + +/*! \internal + + Registers a user type for marshalling, with \a typeName, a \a + destructor, and a \a constructor. Returns the type's handle, + or -1 if the type could not be registered. + */ +int QMetaType::registerType(const char *typeName, Destructor destructor, + Constructor constructor) +{ + QVector *ct = customTypes(); + if (!ct || !typeName || !destructor || !constructor) + return -1; + +#ifdef QT_NO_QOBJECT + NS(QByteArray) normalizedTypeName = typeName; +#else + NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); +#endif + + int idx = qMetaTypeStaticType(normalizedTypeName.constData(), + normalizedTypeName.size()); + + if (!idx) { + QWriteLocker locker(customTypesLock()); + idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), + normalizedTypeName.size()); + if (!idx) { + QCustomTypeInfo inf; + inf.typeName = normalizedTypeName; + inf.constr = constructor; + inf.destr = destructor; + inf.alias = -1; + idx = ct->size() + User; + ct->append(inf); + } + } + return idx; +} + +/*! \internal + \since 4.7 + + Registers a user type for marshalling, as an alias of another type (typedef) +*/ +int QMetaType::registerTypedef(const char* typeName, int aliasId) +{ + QVector *ct = customTypes(); + if (!ct || !typeName) + return -1; + +#ifdef QT_NO_QOBJECT + NS(QByteArray) normalizedTypeName = typeName; +#else + NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); +#endif + + int idx = qMetaTypeStaticType(normalizedTypeName.constData(), + normalizedTypeName.size()); + + if (idx) { + Q_ASSERT(idx == aliasId); + return idx; + } + + QWriteLocker locker(customTypesLock()); + idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), + normalizedTypeName.size()); + + if (idx) + return idx; + + QCustomTypeInfo inf; + inf.typeName = normalizedTypeName; + inf.alias = aliasId; + inf.constr = 0; + inf.destr = 0; + ct->append(inf); + return aliasId; +} + +/*! + \since 4.4 + + Unregisters a user type, with \a typeName. + + \sa type(), typeName() + */ +void QMetaType::unregisterType(const char *typeName) +{ + QVector *ct = customTypes(); + if (!ct || !typeName) + return; + +#ifdef QT_NO_QOBJECT + NS(QByteArray) normalizedTypeName = typeName; +#else + NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); +#endif + QWriteLocker locker(customTypesLock()); + for (int v = 0; v < ct->count(); ++v) { + if (ct->at(v).typeName == typeName) { + QCustomTypeInfo &inf = (*ct)[v]; + inf.typeName.clear(); + inf.constr = 0; + inf.destr = 0; + inf.alias = -1; + } + } +} + +/*! + Returns true if the datatype with ID \a type is registered; + otherwise returns false. + + \sa type(), typeName(), Type +*/ +bool QMetaType::isRegistered(int type) +{ + if (type >= 0 && type < User) { + // predefined type + return true; + } + QReadLocker locker(customTypesLock()); + const QVector * const ct = customTypes(); + return ((type >= User) && (ct && ct->count() > type - User) && !ct->at(type - User).typeName.isEmpty()); +} + +/*! + Returns a handle to the type called \a typeName, or 0 if there is + no such type. + + \sa isRegistered(), typeName(), Type +*/ +int QMetaType::type(const char *typeName) +{ + int length = qstrlen(typeName); + if (!length) + return 0; + int type = qMetaTypeStaticType(typeName, length); + if (!type) { + QReadLocker locker(customTypesLock()); + type = qMetaTypeCustomType_unlocked(typeName, length); +#ifndef QT_NO_QOBJECT + if (!type) { + const NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); + type = qMetaTypeStaticType(normalizedTypeName.constData(), + normalizedTypeName.size()); + if (!type) { + type = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), + normalizedTypeName.size()); + } + } +#endif + } + return type; +} + +#ifndef QT_NO_DATASTREAM +/*! + Writes the object pointed to by \a data with the ID \a type to + the given \a stream. Returns true if the object is saved + successfully; otherwise returns false. + + The type must have been registered with qRegisterMetaType() and + qRegisterMetaTypeStreamOperators() beforehand. + + Normally, you should not need to call this function directly. + Instead, use QVariant's \c operator<<(), which relies on save() + to stream custom types. + + \sa load(), qRegisterMetaTypeStreamOperators() +*/ +bool QMetaType::save(QDataStream &stream, int type, const void *data) +{ + if (!data || !isRegistered(type)) + return false; + + switch(type) { + case QMetaType::Void: + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + return false; + case QMetaType::Long: + stream << qlonglong(*static_cast(data)); + break; + case QMetaType::Int: + stream << *static_cast(data); + break; + case QMetaType::Short: + stream << *static_cast(data); + break; + case QMetaType::Char: + // force a char to be signed + stream << *static_cast(data); + break; + case QMetaType::ULong: + stream << qulonglong(*static_cast(data)); + break; + case QMetaType::UInt: + stream << *static_cast(data); + break; + case QMetaType::LongLong: + stream << *static_cast(data); + break; + case QMetaType::ULongLong: + stream << *static_cast(data); + break; + case QMetaType::UShort: + stream << *static_cast(data); + break; + case QMetaType::UChar: + stream << *static_cast(data); + break; + case QMetaType::Bool: + stream << qint8(*static_cast(data)); + break; + case QMetaType::Float: + stream << *static_cast(data); + break; + case QMetaType::Double: + stream << *static_cast(data); + break; + case QMetaType::QChar: + stream << *static_cast(data); + break; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QVariantMap: + stream << *static_cast(data); + break; + case QMetaType::QVariantHash: + stream << *static_cast(data); + break; + case QMetaType::QVariantList: + stream << *static_cast(data); + break; + case QMetaType::QVariant: + stream << *static_cast(data); + break; +#endif + case QMetaType::QByteArray: + stream << *static_cast(data); + break; + case QMetaType::QString: + stream << *static_cast(data); + break; + case QMetaType::QStringList: + stream << *static_cast(data); + break; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QBitArray: + stream << *static_cast(data); + break; +#endif + case QMetaType::QDate: + stream << *static_cast(data); + break; + case QMetaType::QTime: + stream << *static_cast(data); + break; + case QMetaType::QDateTime: + stream << *static_cast(data); + break; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QUrl: + stream << *static_cast(data); + break; +#endif + case QMetaType::QLocale: + stream << *static_cast(data); + break; +#ifndef QT_NO_GEOM_VARIANT + case QMetaType::QRect: + stream << *static_cast(data); + break; + case QMetaType::QRectF: + stream << *static_cast(data); + break; + case QMetaType::QSize: + stream << *static_cast(data); + break; + case QMetaType::QSizeF: + stream << *static_cast(data); + break; + case QMetaType::QLine: + stream << *static_cast(data); + break; + case QMetaType::QLineF: + stream << *static_cast(data); + break; + case QMetaType::QPoint: + stream << *static_cast(data); + break; + case QMetaType::QPointF: + stream << *static_cast(data); + break; +#endif +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + stream << *static_cast(data); + break; +#endif +#ifndef QT_BOOTSTRAPPED + case QMetaType::QEasingCurve: + stream << *static_cast(data); + break; +#endif +#ifdef QT3_SUPPORT + case QMetaType::QColorGroup: +#endif + case QMetaType::QFont: + case QMetaType::QPixmap: + case QMetaType::QBrush: + case QMetaType::QColor: + case QMetaType::QPalette: + case QMetaType::QIcon: + case QMetaType::QImage: + case QMetaType::QPolygon: + case QMetaType::QRegion: + case QMetaType::QBitmap: + case QMetaType::QCursor: + case QMetaType::QSizePolicy: + case QMetaType::QKeySequence: + case QMetaType::QPen: + case QMetaType::QTextLength: + case QMetaType::QTextFormat: + case QMetaType::QMatrix: + case QMetaType::QTransform: + case QMetaType::QMatrix4x4: + case QMetaType::QVector2D: + case QMetaType::QVector3D: + case QMetaType::QVector4D: + case QMetaType::QQuaternion: + if (!qMetaTypeGuiHelper) + return false; + qMetaTypeGuiHelper[type - FirstGuiType].saveOp(stream, data); + break; + default: { + const QVector * const ct = customTypes(); + if (!ct) + return false; + + SaveOperator saveOp = 0; + { + QReadLocker locker(customTypesLock()); + saveOp = ct->at(type - User).saveOp; + } + + if (!saveOp) + return false; + saveOp(stream, data); + break; } + } + + return true; +} + +/*! + Reads the object of the specified \a type from the given \a + stream into \a data. Returns true if the object is loaded + successfully; otherwise returns false. + + The type must have been registered with qRegisterMetaType() and + qRegisterMetaTypeStreamOperators() beforehand. + + Normally, you should not need to call this function directly. + Instead, use QVariant's \c operator>>(), which relies on load() + to stream custom types. + + \sa save(), qRegisterMetaTypeStreamOperators() +*/ +bool QMetaType::load(QDataStream &stream, int type, void *data) +{ + if (!data || !isRegistered(type)) + return false; + + switch(type) { + case QMetaType::Void: + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + return false; + case QMetaType::Long: { + qlonglong l; + stream >> l; + *static_cast(data) = long(l); + break; } + case QMetaType::Int: + stream >> *static_cast(data); + break; + case QMetaType::Short: + stream >> *static_cast(data); + break; + case QMetaType::Char: + // force a char to be signed + stream >> *static_cast(data); + break; + case QMetaType::ULong: { + qulonglong ul; + stream >> ul; + *static_cast(data) = ulong(ul); + break; } + case QMetaType::UInt: + stream >> *static_cast(data); + break; + case QMetaType::LongLong: + stream >> *static_cast(data); + break; + case QMetaType::ULongLong: + stream >> *static_cast(data); + break; + case QMetaType::UShort: + stream >> *static_cast(data); + break; + case QMetaType::UChar: + stream >> *static_cast(data); + break; + case QMetaType::Bool: { + qint8 b; + stream >> b; + *static_cast(data) = b; + break; } + case QMetaType::Float: + stream >> *static_cast(data); + break; + case QMetaType::Double: + stream >> *static_cast(data); + break; + case QMetaType::QChar: + stream >> *static_cast< NS(QChar)*>(data); + break; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QVariantMap: + stream >> *static_cast< NS(QVariantMap)*>(data); + break; + case QMetaType::QVariantHash: + stream >> *static_cast< NS(QVariantHash)*>(data); + break; + case QMetaType::QVariantList: + stream >> *static_cast< NS(QVariantList)*>(data); + break; + case QMetaType::QVariant: + stream >> *static_cast< NS(QVariant)*>(data); + break; +#endif + case QMetaType::QByteArray: + stream >> *static_cast< NS(QByteArray)*>(data); + break; + case QMetaType::QString: + stream >> *static_cast< NS(QString)*>(data); + break; + case QMetaType::QStringList: + stream >> *static_cast< NS(QStringList)*>(data); + break; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QBitArray: + stream >> *static_cast< NS(QBitArray)*>(data); + break; +#endif + case QMetaType::QDate: + stream >> *static_cast< NS(QDate)*>(data); + break; + case QMetaType::QTime: + stream >> *static_cast< NS(QTime)*>(data); + break; + case QMetaType::QDateTime: + stream >> *static_cast< NS(QDateTime)*>(data); + break; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QUrl: + stream >> *static_cast< NS(QUrl)*>(data); + break; +#endif + case QMetaType::QLocale: + stream >> *static_cast< NS(QLocale)*>(data); + break; +#ifndef QT_NO_GEOM_VARIANT + case QMetaType::QRect: + stream >> *static_cast< NS(QRect)*>(data); + break; + case QMetaType::QRectF: + stream >> *static_cast< NS(QRectF)*>(data); + break; + case QMetaType::QSize: + stream >> *static_cast< NS(QSize)*>(data); + break; + case QMetaType::QSizeF: + stream >> *static_cast< NS(QSizeF)*>(data); + break; + case QMetaType::QLine: + stream >> *static_cast< NS(QLine)*>(data); + break; + case QMetaType::QLineF: + stream >> *static_cast< NS(QLineF)*>(data); + break; + case QMetaType::QPoint: + stream >> *static_cast< NS(QPoint)*>(data); + break; + case QMetaType::QPointF: + stream >> *static_cast< NS(QPointF)*>(data); + break; +#endif +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + stream >> *static_cast< NS(QRegExp)*>(data); + break; +#endif +#ifndef QT_BOOTSTRAPPED + case QMetaType::QEasingCurve: + stream >> *static_cast< NS(QEasingCurve)*>(data); + break; +#endif +#ifdef QT3_SUPPORT + case QMetaType::QColorGroup: +#endif + case QMetaType::QFont: + case QMetaType::QPixmap: + case QMetaType::QBrush: + case QMetaType::QColor: + case QMetaType::QPalette: + case QMetaType::QIcon: + case QMetaType::QImage: + case QMetaType::QPolygon: + case QMetaType::QRegion: + case QMetaType::QBitmap: + case QMetaType::QCursor: + case QMetaType::QSizePolicy: + case QMetaType::QKeySequence: + case QMetaType::QPen: + case QMetaType::QTextLength: + case QMetaType::QTextFormat: + case QMetaType::QMatrix: + case QMetaType::QTransform: + case QMetaType::QMatrix4x4: + case QMetaType::QVector2D: + case QMetaType::QVector3D: + case QMetaType::QVector4D: + case QMetaType::QQuaternion: + if (!qMetaTypeGuiHelper) + return false; + qMetaTypeGuiHelper[type - FirstGuiType].loadOp(stream, data); + break; + default: { + const QVector * const ct = customTypes(); + if (!ct) + return false; + + LoadOperator loadOp = 0; + { + QReadLocker locker(customTypesLock()); + loadOp = ct->at(type - User).loadOp; + } + + if (!loadOp) + return false; + loadOp(stream, data); + break; } + } + return true; +} +#endif // QT_NO_DATASTREAM + +/*! + Returns a copy of \a copy, assuming it is of type \a type. If \a + copy is zero, creates a default type. + + \sa destroy(), isRegistered(), Type +*/ +void *QMetaType::construct(int type, const void *copy) +{ + if (copy) { + switch(type) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + return new void *(*static_cast(copy)); + case QMetaType::Long: + return new long(*static_cast(copy)); + case QMetaType::Int: + return new int(*static_cast(copy)); + case QMetaType::Short: + return new short(*static_cast(copy)); + case QMetaType::Char: + return new char(*static_cast(copy)); + case QMetaType::ULong: + return new ulong(*static_cast(copy)); + case QMetaType::UInt: + return new uint(*static_cast(copy)); + case QMetaType::LongLong: + return new qlonglong(*static_cast(copy)); + case QMetaType::ULongLong: + return new qulonglong(*static_cast(copy)); + case QMetaType::UShort: + return new ushort(*static_cast(copy)); + case QMetaType::UChar: + return new uchar(*static_cast(copy)); + case QMetaType::Bool: + return new bool(*static_cast(copy)); + case QMetaType::Float: + return new float(*static_cast(copy)); + case QMetaType::Double: + return new double(*static_cast(copy)); + case QMetaType::QChar: + return new NS(QChar)(*static_cast(copy)); +#ifndef QT_BOOTSTRAPPED + case QMetaType::QVariantMap: + return new NS(QVariantMap)(*static_cast(copy)); + case QMetaType::QVariantHash: + return new NS(QVariantHash)(*static_cast(copy)); + case QMetaType::QVariantList: + return new NS(QVariantList)(*static_cast(copy)); + case QMetaType::QVariant: + return new NS(QVariant)(*static_cast(copy)); +#endif + case QMetaType::QByteArray: + return new NS(QByteArray)(*static_cast(copy)); + case QMetaType::QString: + return new NS(QString)(*static_cast(copy)); + case QMetaType::QStringList: + return new NS(QStringList)(*static_cast(copy)); +#ifndef QT_BOOTSTRAPPED + case QMetaType::QBitArray: + return new NS(QBitArray)(*static_cast(copy)); +#endif + case QMetaType::QDate: + return new NS(QDate)(*static_cast(copy)); + case QMetaType::QTime: + return new NS(QTime)(*static_cast(copy)); + case QMetaType::QDateTime: + return new NS(QDateTime)(*static_cast(copy)); +#ifndef QT_BOOTSTRAPPED + case QMetaType::QUrl: + return new NS(QUrl)(*static_cast(copy)); +#endif + case QMetaType::QLocale: + return new NS(QLocale)(*static_cast(copy)); +#ifndef QT_NO_GEOM_VARIANT + case QMetaType::QRect: + return new NS(QRect)(*static_cast(copy)); + case QMetaType::QRectF: + return new NS(QRectF)(*static_cast(copy)); + case QMetaType::QSize: + return new NS(QSize)(*static_cast(copy)); + case QMetaType::QSizeF: + return new NS(QSizeF)(*static_cast(copy)); + case QMetaType::QLine: + return new NS(QLine)(*static_cast(copy)); + case QMetaType::QLineF: + return new NS(QLineF)(*static_cast(copy)); + case QMetaType::QPoint: + return new NS(QPoint)(*static_cast(copy)); + case QMetaType::QPointF: + return new NS(QPointF)(*static_cast(copy)); +#endif +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + return new NS(QRegExp)(*static_cast(copy)); +#endif +#ifndef QT_BOOTSTRAPPED + case QMetaType::QEasingCurve: + return new NS(QEasingCurve)(*static_cast(copy)); +#endif + case QMetaType::Void: + return 0; + default: + ; + } + } else { + switch(type) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + return new void *; + case QMetaType::Long: + return new long; + case QMetaType::Int: + return new int; + case QMetaType::Short: + return new short; + case QMetaType::Char: + return new char; + case QMetaType::ULong: + return new ulong; + case QMetaType::UInt: + return new uint; + case QMetaType::LongLong: + return new qlonglong; + case QMetaType::ULongLong: + return new qulonglong; + case QMetaType::UShort: + return new ushort; + case QMetaType::UChar: + return new uchar; + case QMetaType::Bool: + return new bool; + case QMetaType::Float: + return new float; + case QMetaType::Double: + return new double; + case QMetaType::QChar: + return new NS(QChar); +#ifndef QT_BOOTSTRAPPED + case QMetaType::QVariantMap: + return new NS(QVariantMap); + case QMetaType::QVariantHash: + return new NS(QVariantHash); + case QMetaType::QVariantList: + return new NS(QVariantList); + case QMetaType::QVariant: + return new NS(QVariant); +#endif + case QMetaType::QByteArray: + return new NS(QByteArray); + case QMetaType::QString: + return new NS(QString); + case QMetaType::QStringList: + return new NS(QStringList); +#ifndef QT_BOOTSTRAPPED + case QMetaType::QBitArray: + return new NS(QBitArray); +#endif + case QMetaType::QDate: + return new NS(QDate); + case QMetaType::QTime: + return new NS(QTime); + case QMetaType::QDateTime: + return new NS(QDateTime); +#ifndef QT_BOOTSTRAPPED + case QMetaType::QUrl: + return new NS(QUrl); +#endif + case QMetaType::QLocale: + return new NS(QLocale); +#ifndef QT_NO_GEOM_VARIANT + case QMetaType::QRect: + return new NS(QRect); + case QMetaType::QRectF: + return new NS(QRectF); + case QMetaType::QSize: + return new NS(QSize); + case QMetaType::QSizeF: + return new NS(QSizeF); + case QMetaType::QLine: + return new NS(QLine); + case QMetaType::QLineF: + return new NS(QLineF); + case QMetaType::QPoint: + return new NS(QPoint); + case QMetaType::QPointF: + return new NS(QPointF); +#endif +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + return new NS(QRegExp); +#endif +#ifndef QT_BOOTSTRAPPED + case QMetaType::QEasingCurve: + return new NS(QEasingCurve); +#endif + case QMetaType::Void: + return 0; + default: + ; + } + } + + Constructor constr = 0; + if (type >= FirstGuiType && type <= LastGuiType) { + if (!qMetaTypeGuiHelper) + return 0; + constr = qMetaTypeGuiHelper[type - FirstGuiType].constr; + } else { + const QVector * const ct = customTypes(); + QReadLocker locker(customTypesLock()); + if (type < User || !ct || ct->count() <= type - User) + return 0; + if (ct->at(type - User).typeName.isEmpty()) + return 0; + constr = ct->at(type - User).constr; + } + + return constr(copy); +} + +/*! + Destroys the \a data, assuming it is of the \a type given. + + \sa construct(), isRegistered(), Type +*/ +void QMetaType::destroy(int type, void *data) +{ + if (!data) + return; + switch(type) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + delete static_cast(data); + break; + case QMetaType::Long: + delete static_cast(data); + break; + case QMetaType::Int: + delete static_cast(data); + break; + case QMetaType::Short: + delete static_cast(data); + break; + case QMetaType::Char: + delete static_cast(data); + break; + case QMetaType::ULong: + delete static_cast(data); + break; + case QMetaType::LongLong: + delete static_cast(data); + break; + case QMetaType::ULongLong: + delete static_cast(data); + break; + case QMetaType::UInt: + delete static_cast(data); + break; + case QMetaType::UShort: + delete static_cast(data); + break; + case QMetaType::UChar: + delete static_cast(data); + break; + case QMetaType::Bool: + delete static_cast(data); + break; + case QMetaType::Float: + delete static_cast(data); + break; + case QMetaType::Double: + delete static_cast(data); + break; + case QMetaType::QChar: + delete static_cast< NS(QChar)* >(data); + break; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QVariantMap: + delete static_cast< NS(QVariantMap)* >(data); + break; + case QMetaType::QVariantHash: + delete static_cast< NS(QVariantHash)* >(data); + break; + case QMetaType::QVariantList: + delete static_cast< NS(QVariantList)* >(data); + break; + case QMetaType::QVariant: + delete static_cast< NS(QVariant)* >(data); + break; +#endif + case QMetaType::QByteArray: + delete static_cast< NS(QByteArray)* >(data); + break; + case QMetaType::QString: + delete static_cast< NS(QString)* >(data); + break; + case QMetaType::QStringList: + delete static_cast< NS(QStringList)* >(data); + break; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QBitArray: + delete static_cast< NS(QBitArray)* >(data); + break; +#endif + case QMetaType::QDate: + delete static_cast< NS(QDate)* >(data); + break; + case QMetaType::QTime: + delete static_cast< NS(QTime)* >(data); + break; + case QMetaType::QDateTime: + delete static_cast< NS(QDateTime)* >(data); + break; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QUrl: + delete static_cast< NS(QUrl)* >(data); +#endif + break; + case QMetaType::QLocale: + delete static_cast< NS(QLocale)* >(data); + break; +#ifndef QT_NO_GEOM_VARIANT + case QMetaType::QRect: + delete static_cast< NS(QRect)* >(data); + break; + case QMetaType::QRectF: + delete static_cast< NS(QRectF)* >(data); + break; + case QMetaType::QSize: + delete static_cast< NS(QSize)* >(data); + break; + case QMetaType::QSizeF: + delete static_cast< NS(QSizeF)* >(data); + break; + case QMetaType::QLine: + delete static_cast< NS(QLine)* >(data); + break; + case QMetaType::QLineF: + delete static_cast< NS(QLineF)* >(data); + break; + case QMetaType::QPoint: + delete static_cast< NS(QPoint)* >(data); + break; + case QMetaType::QPointF: + delete static_cast< NS(QPointF)* >(data); + break; +#endif +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + delete static_cast< NS(QRegExp)* >(data); + break; +#endif +#ifndef QT_BOOTSTRAPPED + case QMetaType::QEasingCurve: + delete static_cast< NS(QEasingCurve)* >(data); + break; +#endif + case QMetaType::Void: + break; + default: { + const QVector * const ct = customTypes(); + Destructor destr = 0; + if (type >= FirstGuiType && type <= LastGuiType) { + Q_ASSERT(qMetaTypeGuiHelper); + + if (!qMetaTypeGuiHelper) + return; + destr = qMetaTypeGuiHelper[type - FirstGuiType].destr; + } else { + QReadLocker locker(customTypesLock()); + if (type < User || !ct || ct->count() <= type - User) + break; + if (ct->at(type - User).typeName.isEmpty()) + break; + destr = ct->at(type - User).destr; + } + destr(data); + break; } + } +} + +/*! + \fn int qRegisterMetaType(const char *typeName) + \relates QMetaType + \threadsafe + + Registers the type name \a typeName for the type \c{T}. Returns + the internal ID used by QMetaType. Any class or struct that has a + public default constructor, a public copy constructor and a public + destructor can be registered. + + After a type has been registered, you can create and destroy + objects of that type dynamically at run-time. + + This example registers the class \c{MyClass}: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp 4 + + This function is useful to register typedefs so they can be used + by QMetaProperty, or in QueuedConnections + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp 9 + + \sa qRegisterMetaTypeStreamOperators(), QMetaType::isRegistered(), + Q_DECLARE_METATYPE() +*/ + +/*! + \fn int qRegisterMetaTypeStreamOperators(const char *typeName) + \relates QMetaType + \threadsafe + + Registers the stream operators for the type \c{T} called \a + typeName. + + Afterward, the type can be streamed using QMetaType::load() and + QMetaType::save(). These functions are used when streaming a + QVariant. + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp 5 + + The stream operators should have the following signatures: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp 6 + + \sa qRegisterMetaType(), QMetaType::isRegistered(), Q_DECLARE_METATYPE() +*/ + +/*! \typedef QMetaType::Destructor + \internal +*/ +/*! \typedef QMetaType::Constructor + \internal +*/ +/*! \typedef QMetaType::SaveOperator + \internal +*/ +/*! \typedef QMetaType::LoadOperator + \internal +*/ + +/*! + \fn int qRegisterMetaType() + \relates QMetaType + \threadsafe + \since 4.2 + + Call this function to register the type \c T. \c T must be declared with + Q_DECLARE_METATYPE(). Returns the meta type Id. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp 7 + + To use the type \c T in QVariant, using Q_DECLARE_METATYPE() is + sufficient. To use the type \c T in queued signal and slot connections, + \c{qRegisterMetaType()} must be called before the first connection + is established. + + Also, to use type \c T with the QObject::property() API, + \c{qRegisterMetaType()} must be called before it is used, typically + in the constructor of the class that uses \c T, or in the \c{main()} + function. + + \sa Q_DECLARE_METATYPE() + */ + +/*! \fn int qMetaTypeId() + \relates QMetaType + \threadsafe + \since 4.1 + + Returns the meta type id of type \c T at compile time. If the + type was not declared with Q_DECLARE_METATYPE(), compilation will + fail. + + Typical usage: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp 8 + + QMetaType::type() returns the same ID as qMetaTypeId(), but does + a lookup at runtime based on the name of the type. + QMetaType::type() is a bit slower, but compilation succeeds if a + type is not registered. + + \sa Q_DECLARE_METATYPE(), QMetaType::type() +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h new file mode 100644 index 0000000000..a2bb7d1597 --- /dev/null +++ b/src/corelib/kernel/qmetatype.h @@ -0,0 +1,416 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QMETATYPE_H +#define QMETATYPE_H + +#include +#include + +#ifndef QT_NO_DATASTREAM +#include +#endif + +#ifdef Bool +#error qmetatype.h must be included before any header file that defines Bool +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class Q_CORE_EXPORT QMetaType { +public: + enum Type { + // these are merged with QVariant + Void = 0, Bool = 1, Int = 2, UInt = 3, LongLong = 4, ULongLong = 5, + Double = 6, QChar = 7, QVariantMap = 8, QVariantList = 9, + QString = 10, QStringList = 11, QByteArray = 12, + QBitArray = 13, QDate = 14, QTime = 15, QDateTime = 16, QUrl = 17, + QLocale = 18, QRect = 19, QRectF = 20, QSize = 21, QSizeF = 22, + QLine = 23, QLineF = 24, QPoint = 25, QPointF = 26, QRegExp = 27, + QVariantHash = 28, QEasingCurve = 29, LastCoreType = QEasingCurve, + + FirstGuiType = 63 /* QColorGroup */, +#ifdef QT3_SUPPORT + QColorGroup = 63, +#endif + QFont = 64, QPixmap = 65, QBrush = 66, QColor = 67, QPalette = 68, + QIcon = 69, QImage = 70, QPolygon = 71, QRegion = 72, QBitmap = 73, + QCursor = 74, QSizePolicy = 75, QKeySequence = 76, QPen = 77, + QTextLength = 78, QTextFormat = 79, QMatrix = 80, QTransform = 81, + QMatrix4x4 = 82, QVector2D = 83, QVector3D = 84, QVector4D = 85, + QQuaternion = 86, + LastGuiType = QQuaternion, + + FirstCoreExtType = 128 /* VoidStar */, + VoidStar = 128, Long = 129, Short = 130, Char = 131, ULong = 132, + UShort = 133, UChar = 134, Float = 135, QObjectStar = 136, QWidgetStar = 137, + QVariant = 138, + LastCoreExtType = QVariant, + +// This logic must match the one in qglobal.h +#if defined(QT_COORD_TYPE) + QReal = 0, +#elif defined(QT_NO_FPU) || defined(QT_ARCH_ARM) || defined(QT_ARCH_WINDOWSCE) || defined(QT_ARCH_SYMBIAN) + QReal = Float, +#else + QReal = Double, +#endif + + User = 256 + }; + + typedef void (*Destructor)(void *); + typedef void *(*Constructor)(const void *); + +#ifndef QT_NO_DATASTREAM + typedef void (*SaveOperator)(QDataStream &, const void *); + 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); + static int registerTypedef(const char *typeName, int aliasId); + static int type(const char *typeName); + static const char *typeName(int type); + static bool isRegistered(int type); + static void *construct(int type, const void *copy = 0); + static void destroy(int type, void *data); + static void unregisterType(const char *typeName); + +#ifndef QT_NO_DATASTREAM + static bool save(QDataStream &stream, int type, const void *data); + static bool load(QDataStream &stream, int type, void *data); +#endif +}; + +template +void qMetaTypeDeleteHelper(T *t) +{ + delete t; +} + +template +void *qMetaTypeConstructHelper(const T *t) +{ + if (!t) + return new T(); + return new T(*static_cast(t)); +} + +#ifndef QT_NO_DATASTREAM +template +void qMetaTypeSaveHelper(QDataStream &stream, const T *t) +{ + stream << *t; +} + +template +void qMetaTypeLoadHelper(QDataStream &stream, T *t) +{ + stream >> *t; +} +#endif // QT_NO_DATASTREAM + +template +struct QMetaTypeId +{ + enum { Defined = 0 }; +}; + +template +struct QMetaTypeId2 +{ + enum { Defined = QMetaTypeId::Defined }; + static inline int qt_metatype_id() { return QMetaTypeId::qt_metatype_id(); } +}; + +namespace QtPrivate { + template ::Defined> + struct QMetaTypeIdHelper { + static inline int qt_metatype_id() + { return QMetaTypeId2::qt_metatype_id(); } + }; + template struct QMetaTypeIdHelper { + static inline int qt_metatype_id() + { return -1; } + }; +} + +template +int qRegisterMetaType(const char *typeName +#ifndef qdoc + , T * dummy = 0 +#endif +) +{ + const int typedefOf = dummy ? -1 : QtPrivate::QMetaTypeIdHelper::qt_metatype_id(); + if (typedefOf != -1) + return QMetaType::registerTypedef(typeName, typedefOf); + + typedef void*(*ConstructPtr)(const T*); + ConstructPtr cptr = qMetaTypeConstructHelper; + typedef void(*DeletePtr)(T*); + DeletePtr dptr = qMetaTypeDeleteHelper; + + return QMetaType::registerType(typeName, reinterpret_cast(dptr), + reinterpret_cast(cptr)); +} + +#ifndef QT_NO_DATASTREAM +template +void qRegisterMetaTypeStreamOperators(const char *typeName +#ifndef qdoc + , T * /* dummy */ = 0 +#endif +) +{ + typedef void(*SavePtr)(QDataStream &, const T *); + typedef void(*LoadPtr)(QDataStream &, T *); + SavePtr sptr = qMetaTypeSaveHelper; + LoadPtr lptr = qMetaTypeLoadHelper; + + qRegisterMetaType(typeName); + QMetaType::registerStreamOperators(typeName, reinterpret_cast(sptr), + reinterpret_cast(lptr)); +} +#endif // QT_NO_DATASTREAM + +template +inline int qMetaTypeId( +#ifndef qdoc + T * /* dummy */ = 0 +#endif +) +{ + return QMetaTypeId2::qt_metatype_id(); +} + +template +inline int qRegisterMetaType( +#if !defined(qdoc) && !defined(Q_CC_SUN) + T * dummy = 0 +#endif +) +{ +#ifdef Q_CC_SUN + return qMetaTypeId(static_cast(0)); +#else + return qMetaTypeId(dummy); +#endif +} + +#ifndef QT_NO_DATASTREAM +template +inline int qRegisterMetaTypeStreamOperators() +{ + typedef void(*SavePtr)(QDataStream &, const T *); + typedef void(*LoadPtr)(QDataStream &, T *); + SavePtr sptr = qMetaTypeSaveHelper; + LoadPtr lptr = qMetaTypeLoadHelper; + + register int id = qMetaTypeId(); + QMetaType::registerStreamOperators(id, + reinterpret_cast(sptr), + reinterpret_cast(lptr)); + + return id; +} +#endif + +#define Q_DECLARE_METATYPE(TYPE) \ + QT_BEGIN_NAMESPACE \ + template <> \ + struct QMetaTypeId< TYPE > \ + { \ + enum { Defined = 1 }; \ + static int qt_metatype_id() \ + { \ + static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \ + if (!metatype_id) \ + metatype_id = qRegisterMetaType< TYPE >(#TYPE, \ + reinterpret_cast< TYPE *>(quintptr(-1))); \ + return metatype_id; \ + } \ + }; \ + QT_END_NAMESPACE + +#define Q_DECLARE_BUILTIN_METATYPE(TYPE, NAME) \ + QT_BEGIN_NAMESPACE \ + template<> struct QMetaTypeId2 \ + { \ + enum { Defined = 1, MetaType = QMetaType::NAME }; \ + static inline int qt_metatype_id() { return QMetaType::NAME; } \ + }; \ + QT_END_NAMESPACE + +class QString; +class QByteArray; +class QChar; +class QStringList; +class QBitArray; +class QDate; +class QTime; +class QDateTime; +class QUrl; +class QLocale; +class QRect; +class QRectF; +class QSize; +class QSizeF; +class QLine; +class QLineF; +class QPoint; +class QPointF; +#ifndef QT_NO_REGEXP +class QRegExp; +#endif +class QEasingCurve; +class QWidget; +class QObject; + +#ifdef QT3_SUPPORT +class QColorGroup; +#endif +class QFont; +class QPixmap; +class QBrush; +class QColor; +class QPalette; +class QIcon; +class QImage; +class QPolygon; +class QRegion; +class QBitmap; +class QCursor; +class QSizePolicy; +class QKeySequence; +class QPen; +class QTextLength; +class QTextFormat; +class QMatrix; +class QTransform; +class QMatrix4x4; +class QVector2D; +class QVector3D; +class QVector4D; +class QQuaternion; +class QVariant; + +QT_END_NAMESPACE + +Q_DECLARE_BUILTIN_METATYPE(QString, QString) +Q_DECLARE_BUILTIN_METATYPE(int, Int) +Q_DECLARE_BUILTIN_METATYPE(uint, UInt) +Q_DECLARE_BUILTIN_METATYPE(bool, Bool) +Q_DECLARE_BUILTIN_METATYPE(double, Double) +Q_DECLARE_BUILTIN_METATYPE(QByteArray, QByteArray) +Q_DECLARE_BUILTIN_METATYPE(QChar, QChar) +Q_DECLARE_BUILTIN_METATYPE(long, Long) +Q_DECLARE_BUILTIN_METATYPE(short, Short) +Q_DECLARE_BUILTIN_METATYPE(char, Char) +Q_DECLARE_BUILTIN_METATYPE(signed char, Char) +Q_DECLARE_BUILTIN_METATYPE(ulong, ULong) +Q_DECLARE_BUILTIN_METATYPE(ushort, UShort) +Q_DECLARE_BUILTIN_METATYPE(uchar, UChar) +Q_DECLARE_BUILTIN_METATYPE(float, Float) +Q_DECLARE_BUILTIN_METATYPE(QObject *, QObjectStar) +Q_DECLARE_BUILTIN_METATYPE(QWidget *, QWidgetStar) +Q_DECLARE_BUILTIN_METATYPE(void *, VoidStar) +Q_DECLARE_BUILTIN_METATYPE(qlonglong, LongLong) +Q_DECLARE_BUILTIN_METATYPE(qulonglong, ULongLong) +Q_DECLARE_BUILTIN_METATYPE(QStringList, QStringList) +Q_DECLARE_BUILTIN_METATYPE(QBitArray, QBitArray) +Q_DECLARE_BUILTIN_METATYPE(QDate, QDate) +Q_DECLARE_BUILTIN_METATYPE(QTime, QTime) +Q_DECLARE_BUILTIN_METATYPE(QDateTime, QDateTime) +Q_DECLARE_BUILTIN_METATYPE(QUrl, QUrl) +Q_DECLARE_BUILTIN_METATYPE(QLocale, QLocale) +Q_DECLARE_BUILTIN_METATYPE(QRect, QRect) +Q_DECLARE_BUILTIN_METATYPE(QRectF, QRectF) +Q_DECLARE_BUILTIN_METATYPE(QSize, QSize) +Q_DECLARE_BUILTIN_METATYPE(QSizeF, QSizeF) +Q_DECLARE_BUILTIN_METATYPE(QLine, QLine) +Q_DECLARE_BUILTIN_METATYPE(QLineF, QLineF) +Q_DECLARE_BUILTIN_METATYPE(QPoint, QPoint) +Q_DECLARE_BUILTIN_METATYPE(QPointF, QPointF) +#ifndef QT_NO_REGEXP +Q_DECLARE_BUILTIN_METATYPE(QRegExp, QRegExp) +#endif +Q_DECLARE_BUILTIN_METATYPE(QEasingCurve, QEasingCurve) + +#ifdef QT3_SUPPORT +Q_DECLARE_BUILTIN_METATYPE(QColorGroup, QColorGroup) +#endif +Q_DECLARE_BUILTIN_METATYPE(QFont, QFont) +Q_DECLARE_BUILTIN_METATYPE(QPixmap, QPixmap) +Q_DECLARE_BUILTIN_METATYPE(QBrush, QBrush) +Q_DECLARE_BUILTIN_METATYPE(QColor, QColor) +Q_DECLARE_BUILTIN_METATYPE(QPalette, QPalette) +Q_DECLARE_BUILTIN_METATYPE(QIcon, QIcon) +Q_DECLARE_BUILTIN_METATYPE(QImage, QImage) +Q_DECLARE_BUILTIN_METATYPE(QPolygon, QPolygon) +Q_DECLARE_BUILTIN_METATYPE(QRegion, QRegion) +Q_DECLARE_BUILTIN_METATYPE(QBitmap, QBitmap) +Q_DECLARE_BUILTIN_METATYPE(QCursor, QCursor) +Q_DECLARE_BUILTIN_METATYPE(QSizePolicy, QSizePolicy) +Q_DECLARE_BUILTIN_METATYPE(QKeySequence, QKeySequence) +Q_DECLARE_BUILTIN_METATYPE(QPen, QPen) +Q_DECLARE_BUILTIN_METATYPE(QTextLength, QTextLength) +Q_DECLARE_BUILTIN_METATYPE(QTextFormat, QTextFormat) +Q_DECLARE_BUILTIN_METATYPE(QMatrix, QMatrix) +Q_DECLARE_BUILTIN_METATYPE(QTransform, QTransform) +Q_DECLARE_BUILTIN_METATYPE(QMatrix4x4, QMatrix4x4) +Q_DECLARE_BUILTIN_METATYPE(QVector2D, QVector2D) +Q_DECLARE_BUILTIN_METATYPE(QVector3D, QVector3D) +Q_DECLARE_BUILTIN_METATYPE(QVector4D, QVector4D) +Q_DECLARE_BUILTIN_METATYPE(QQuaternion, QQuaternion) +Q_DECLARE_BUILTIN_METATYPE(QVariant, QVariant) + +QT_END_HEADER + +#endif // QMETATYPE_H diff --git a/src/corelib/kernel/qmimedata.cpp b/src/corelib/kernel/qmimedata.cpp new file mode 100644 index 0000000000..4e0f8aeb8e --- /dev/null +++ b/src/corelib/kernel/qmimedata.cpp @@ -0,0 +1,627 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qmimedata.h" + +#include "private/qobject_p.h" +#include "qurl.h" +#include "qstringlist.h" +#include "qtextcodec.h" + +QT_BEGIN_NAMESPACE + +struct QMimeDataStruct +{ + QString format; + QVariant data; +}; + +class QMimeDataPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QMimeData) +public: + void removeData(const QString &format); + void setData(const QString &format, const QVariant &data); + QVariant getData(const QString &format) const; + + QVariant retrieveTypedData(const QString &format, QVariant::Type type) const; + + QList dataList; +}; + +void QMimeDataPrivate::removeData(const QString &format) +{ + for (int i=0; iretrieveData(format, type); + if (data.type() == type || !data.isValid()) + return data; + + // provide more conversion possiblities than just what QVariant provides + + // URLs can be lists as well... + if ((type == QVariant::Url && data.type() == QVariant::List) + || (type == QVariant::List && data.type() == QVariant::Url)) + return data; + + // images and pixmaps are interchangeable + if ((type == QVariant::Pixmap && data.type() == QVariant::Image) + || (type == QVariant::Image && data.type() == QVariant::Pixmap)) + return data; + + if (data.type() == QVariant::ByteArray) { + // see if we can convert to the requested type + switch(type) { +#ifndef QT_NO_TEXTCODEC + case QVariant::String: { + const QByteArray ba = data.toByteArray(); + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + if (format == QLatin1String("text/html")) + codec = QTextCodec::codecForHtml(ba, codec); + return codec->toUnicode(ba); + } +#endif // QT_NO_TEXTCODEC + case QVariant::Color: { + QVariant newData = data; + newData.convert(QVariant::Color); + return newData; + } + case QVariant::List: { + if (format != QLatin1String("text/uri-list")) + break; + // fall through + } + case QVariant::Url: { + QByteArray ba = data.toByteArray(); + // Qt 3.x will send text/uri-list with a trailing + // null-terminator (that is *not* sent for any other + // text/* mime-type), so chop it off + if (ba.endsWith('\0')) + ba.chop(1); + + QList urls = ba.split('\n'); + QList list; + for (int i = 0; i < urls.size(); ++i) { + QByteArray ba = urls.at(i).trimmed(); + if (!ba.isEmpty()) + list.append(QUrl::fromEncoded(ba)); + } + return list; + } + default: + break; + } + + } else if (type == QVariant::ByteArray) { + + // try to convert to bytearray + switch(data.type()) { + case QVariant::ByteArray: + case QVariant::Color: + return data.toByteArray(); + break; + case QVariant::String: + return data.toString().toUtf8(); + break; + case QVariant::Url: + return data.toUrl().toEncoded(); + break; + case QVariant::List: { + // has to be list of URLs + QByteArray result; + QList list = data.toList(); + for (int i = 0; i < list.size(); ++i) { + if (list.at(i).type() == QVariant::Url) { + result += list.at(i).toUrl().toEncoded(); + result += "\r\n"; + } + } + if (!result.isEmpty()) + return result; + break; + } + default: + break; + } + } + return data; +} + +/*! + \class QMimeData + \brief The QMimeData class provides a container for data that records information + about its MIME type. + + QMimeData is used to describe information that can be stored in + the \l{QClipboard}{clipboard}, and transferred via the \l{drag + and drop} mechanism. QMimeData objects associate the data that + they hold with the corresponding MIME types to ensure that + information can be safely transferred between applications, and + copied around within the same application. + + QMimeData objects are usually created using \c new and supplied + to QDrag or QClipboard objects. This is to enable Qt to manage + the memory that they use. + + A single QMimeData object can store the same data using several + different formats at the same time. The formats() function + returns a list of the available formats in order of preference. + The data() function returns the raw data associated with a MIME + type, and setData() allows you to set the data for a MIME type. + + For the most common MIME types, QMimeData provides convenience + functions to access the data: + + \table + \header \o Tester \o Getter \o Setter \o MIME Types + \row \o hasText() \o text() \o setText() \o \c text/plain + \row \o hasHtml() \o html() \o setHtml() \o \c text/html + \row \o hasUrls() \o urls() \o setUrls() \o \c text/uri-list + \row \o hasImage() \o imageData() \o setImageData() \o \c image/ * + \row \o hasColor() \o colorData() \o setColorData() \o \c application/x-color + \endtable + + For example, if your write a widget that accepts URL drags, you + would end up writing code like this: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmimedata.cpp 0 + + There are three approaches for storing custom data in a QMimeData + object: + + \list 1 + \o Custom data can be stored directly in a QMimeData object as a + QByteArray using setData(). For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmimedata.cpp 1 + + \o We can subclass QMimeData and reimplement hasFormat(), + formats(), and retrieveData(). + + \o If the drag and drop operation occurs within a single + application, we can subclass QMimeData and add extra data in + it, and use a qobject_cast() in the receiver's drop event + handler. For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmimedata.cpp 2 + \endlist + + \section1 Platform-Specific MIME Types + + On Windows, formats() will also return custom formats available + in the MIME data, using the \c{x-qt-windows-mime} subtype to + indicate that they represent data in non-standard formats. + The formats will take the following form: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmimedata.cpp 3 + + The following are examples of custom MIME types: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmimedata.cpp 4 + + The \c value declaration of each format describes the way in which the + data is encoded. + + On Windows, the MIME format does not always map directly to the + clipboard formats. Qt provides QWindowsMime to map clipboard + formats to open-standard MIME formats. Similarly, the + QMacPasteboardMime maps MIME to Mac flavors. + + \sa QClipboard, QDragEnterEvent, QDragMoveEvent, QDropEvent, QDrag, + QWindowsMime, QMacPasteboardMime, {Drag and Drop} +*/ + +/*! + Constructs a new MIME data object with no data in it. +*/ +QMimeData::QMimeData() + : QObject(*new QMimeDataPrivate, 0) +{ +} + +/*! + Destroys the MIME data object. +*/ +QMimeData::~QMimeData() +{ +} + +/*! + Returns a list of URLs contained within the MIME data object. + + URLs correspond to the MIME type \c text/uri-list. + + \sa hasUrls(), data() +*/ +QList QMimeData::urls() const +{ + Q_D(const QMimeData); + QVariant data = d->retrieveTypedData(QLatin1String("text/uri-list"), QVariant::List); + QList urls; + if (data.type() == QVariant::Url) + urls.append(data.toUrl()); + else if (data.type() == QVariant::List) { + QList list = data.toList(); + for (int i = 0; i < list.size(); ++i) { + if (list.at(i).type() == QVariant::Url) + urls.append(list.at(i).toUrl()); + } + } + return urls; +} + +/*! + Sets the URLs stored in the MIME data object to those specified by \a urls. + + URLs correspond to the MIME type \c text/uri-list. + + \sa hasUrls(), setData() +*/ +void QMimeData::setUrls(const QList &urls) +{ + Q_D(QMimeData); + QList list; + for (int i = 0; i < urls.size(); ++i) + list.append(urls.at(i)); + + d->setData(QLatin1String("text/uri-list"), list); +} + +/*! + Returns true if the object can return a list of urls; otherwise + returns false. + + URLs correspond to the MIME type \c text/uri-list. + + \sa setUrls(), urls(), hasFormat() +*/ +bool QMimeData::hasUrls() const +{ + return hasFormat(QLatin1String("text/uri-list")); +} + + +/*! + Returns a plain text (MIME type \c text/plain) representation of + the data. + + \sa hasText(), html(), data() +*/ +QString QMimeData::text() const +{ + Q_D(const QMimeData); + QVariant data = d->retrieveTypedData(QLatin1String("text/plain"), QVariant::String); + return data.toString(); +} + +/*! + Sets \a text as the plain text (MIME type \c text/plain) used to + represent the data. + + \sa hasText(), setHtml(), setData() +*/ +void QMimeData::setText(const QString &text) +{ + Q_D(QMimeData); + d->setData(QLatin1String("text/plain"), text); +} + +/*! + Returns true if the object can return plain text (MIME type \c + text/plain); otherwise returns false. + + \sa setText(), text(), hasHtml(), hasFormat() +*/ +bool QMimeData::hasText() const +{ + return hasFormat(QLatin1String("text/plain")); +} + +/*! + Returns a string if the data stored in the object is HTML (MIME + type \c text/html); otherwise returns an empty string. + + \sa hasHtml(), setData() +*/ +QString QMimeData::html() const +{ + Q_D(const QMimeData); + QVariant data = d->retrieveTypedData(QLatin1String("text/html"), QVariant::String); + return data.toString(); +} + +/*! + Sets \a html as the HTML (MIME type \c text/html) used to + represent the data. + + \sa hasHtml(), setText(), setData() +*/ +void QMimeData::setHtml(const QString &html) +{ + Q_D(QMimeData); + d->setData(QLatin1String("text/html"), html); +} + +/*! + Returns true if the object can return HTML (MIME type \c + text/html); otherwise returns false. + + \sa setHtml(), html(), hasFormat() +*/ +bool QMimeData::hasHtml() const +{ + return hasFormat(QLatin1String("text/html")); +} + +/*! + Returns a QVariant storing a QImage if the object can return an + image; otherwise returns a null variant. + + A QVariant is used because QMimeData belongs to the \l QtCore + library, whereas QImage belongs to \l QtGui. To convert the + QVariant to a QImage, simply use qvariant_cast(). For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmimedata.cpp 5 + + \sa hasImage() +*/ +QVariant QMimeData::imageData() const +{ + Q_D(const QMimeData); + return d->retrieveTypedData(QLatin1String("application/x-qt-image"), QVariant::Image); +} + +/*! + Sets the data in the object to the given \a image. + + A QVariant is used because QMimeData belongs to the \l QtCore + library, whereas QImage belongs to \l QtGui. The conversion + from QImage to QVariant is implicit. For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmimedata.cpp 6 + + \sa hasImage(), setData() +*/ +void QMimeData::setImageData(const QVariant &image) +{ + Q_D(QMimeData); + d->setData(QLatin1String("application/x-qt-image"), image); +} + +/*! + Returns true if the object can return an image; otherwise returns + false. + + \sa setImageData(), imageData(), hasFormat() +*/ +bool QMimeData::hasImage() const +{ + return hasFormat(QLatin1String("application/x-qt-image")); +} + +/*! + Returns a color if the data stored in the object represents a + color (MIME type \c application/x-color); otherwise returns a + null variant. + + A QVariant is used because QMimeData belongs to the \l QtCore + library, whereas QColor belongs to \l QtGui. To convert the + QVariant to a QColor, simply use qvariant_cast(). For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qmimedata.cpp 7 + + \sa hasColor(), setColorData(), data() +*/ +QVariant QMimeData::colorData() const +{ + Q_D(const QMimeData); + return d->retrieveTypedData(QLatin1String("application/x-color"), QVariant::Color); +} + +/*! + Sets the color data in the object to the given \a color. + + Colors correspond to the MIME type \c application/x-color. + + \sa hasColor(), setData() +*/ +void QMimeData::setColorData(const QVariant &color) +{ + Q_D(QMimeData); + d->setData(QLatin1String("application/x-color"), color); +} + + +/*! + Returns true if the object can return a color (MIME type \c + application/x-color); otherwise returns false. + + \sa setColorData(), colorData(), hasFormat() +*/ +bool QMimeData::hasColor() const +{ + return hasFormat(QLatin1String("application/x-color")); +} + +/*! + Returns the data stored in the object in the format described by + the MIME type specified by \a mimeType. +*/ +QByteArray QMimeData::data(const QString &mimeType) const +{ + Q_D(const QMimeData); + QVariant data = d->retrieveTypedData(mimeType, QVariant::ByteArray); + return data.toByteArray(); +} + +/*! + Sets the data associated with the MIME type given by \a mimeType + to the specified \a data. + + For the most common types of data, you can call the higher-level + functions setText(), setHtml(), setUrls(), setImageData(), and + setColorData() instead. + + Note that if you want to use a custom data type in an item view drag and drop + operation, you must register it as a Qt \l{QMetaType}{meta type}, using the + Q_DECLARE_METATYPE() macro, and implement stream operators for it. The stream + operators must then be registered with the qRegisterMetaTypeStreamOperators() + function. + + \sa hasFormat(), QMetaType, qRegisterMetaTypeStreamOperators() +*/ +void QMimeData::setData(const QString &mimeType, const QByteArray &data) +{ + Q_D(QMimeData); + d->setData(mimeType, QVariant(data)); +} + +/*! + Returns true if the object can return data for the MIME type + specified by \a mimeType; otherwise returns false. + + For the most common types of data, you can call the higher-level + functions hasText(), hasHtml(), hasUrls(), hasImage(), and + hasColor() instead. + + \sa formats(), setData(), data() +*/ +bool QMimeData::hasFormat(const QString &mimeType) const +{ + return formats().contains(mimeType); +} + +/*! + Returns a list of formats supported by the object. This is a list + of MIME types for which the object can return suitable data. The + formats in the list are in a priority order. + + For the most common types of data, you can call the higher-level + functions hasText(), hasHtml(), hasUrls(), hasImage(), and + hasColor() instead. + + \sa hasFormat(), setData(), data() +*/ +QStringList QMimeData::formats() const +{ + Q_D(const QMimeData); + QStringList list; + for (int i=0; idataList.size(); i++) + list += d->dataList.at(i).format; + return list; +} + +/*! + Returns a variant with the given \a type containing data for the + MIME type specified by \a mimeType. If the object does not + support the MIME type or variant type given, a null variant is + returned instead. + + This function is called by the general data() getter and by the + convenience getters (text(), html(), urls(), imageData(), and + colorData()). You can reimplement it if you want to store your + data using a custom data structure (instead of a QByteArray, + which is what setData() provides). You would then also need + to reimplement hasFormat() and formats(). + + \sa data() +*/ +QVariant QMimeData::retrieveData(const QString &mimeType, QVariant::Type type) const +{ + Q_UNUSED(type); + Q_D(const QMimeData); + return d->getData(mimeType); +} + +/*! + Removes all the MIME type and data entries in the object. +*/ +void QMimeData::clear() +{ + Q_D(QMimeData); + d->dataList.clear(); +} + +/*! + \since 4.4 + + Removes the data entry for \a mimeType in the object. +*/ +void QMimeData::removeFormat(const QString &mimeType) +{ + Q_D(QMimeData); + d->removeData(mimeType); +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qmimedata.h b/src/corelib/kernel/qmimedata.h new file mode 100644 index 0000000000..28b999c235 --- /dev/null +++ b/src/corelib/kernel/qmimedata.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QMIMEDATA_H +#define QMIMEDATA_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QUrl; +class QMimeDataPrivate; + +class Q_CORE_EXPORT QMimeData : public QObject +{ + Q_OBJECT +public: + QMimeData(); + ~QMimeData(); + + QList urls() const; + void setUrls(const QList &urls); + bool hasUrls() const; + + QString text() const; + void setText(const QString &text); + bool hasText() const; + + QString html() const; + void setHtml(const QString &html); + bool hasHtml() const; + + QVariant imageData() const; + void setImageData(const QVariant &image); + bool hasImage() const; + + QVariant colorData() const; + void setColorData(const QVariant &color); + bool hasColor() const; + + QByteArray data(const QString &mimetype) const; + void setData(const QString &mimetype, const QByteArray &data); + void removeFormat(const QString &mimetype); + + virtual bool hasFormat(const QString &mimetype) const; + virtual QStringList formats() const; + + void clear(); +protected: + virtual QVariant retrieveData(const QString &mimetype, + QVariant::Type preferredType) const; +private: + Q_DISABLE_COPY(QMimeData) + Q_DECLARE_PRIVATE(QMimeData) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMIMEDATA_H diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp new file mode 100644 index 0000000000..357cfd3f33 --- /dev/null +++ b/src/corelib/kernel/qobject.cpp @@ -0,0 +1,4310 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qobject.h" +#include "qobject_p.h" +#include "qmetaobject_p.h" + +#include "qabstracteventdispatcher.h" +#include "qcoreapplication.h" +#include "qcoreapplication_p.h" +#include "qvariant.h" +#include "qmetaobject.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +static int DIRECT_CONNECTION_ONLY = 0; + +static int *queuedConnectionTypes(const QList &typeNames) +{ + int *types = new int [typeNames.count() + 1]; + Q_CHECK_PTR(types); + for (int i = 0; i < typeNames.count(); ++i) { + const QByteArray typeName = typeNames.at(i); + if (typeName.endsWith('*')) + types[i] = QMetaType::VoidStar; + else + types[i] = QMetaType::type(typeName); + + if (!types[i]) { + qWarning("QObject::connect: Cannot queue arguments of type '%s'\n" + "(Make sure '%s' is registered using qRegisterMetaType().)", + typeName.constData(), typeName.constData()); + delete [] types; + return 0; + } + } + types[typeNames.count()] = 0; + + return types; +} + +static QBasicAtomicPointer signalSlotMutexes = Q_BASIC_ATOMIC_INITIALIZER(0); +static QBasicAtomicInt objectCount = Q_BASIC_ATOMIC_INITIALIZER(0); + +/** \internal + * mutex to be locked when accessing the connectionlists or the senders list + */ +static inline QMutex *signalSlotLock(const QObject *o) +{ + if (!signalSlotMutexes) { + QMutexPool *mp = new QMutexPool; + if (!signalSlotMutexes.testAndSetOrdered(0, mp)) { + delete mp; + } + } + return signalSlotMutexes->get(o); +} + +extern "C" Q_CORE_EXPORT void qt_addObject(QObject *) +{ + objectCount.ref(); +} + +extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *) +{ + if(!objectCount.deref()) { + QMutexPool *old = signalSlotMutexes.fetchAndStoreAcquire(0); + delete old; + } +} + +void (*QAbstractDeclarativeData::destroyed)(QAbstractDeclarativeData *, QObject *) = 0; +void (*QAbstractDeclarativeData::parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *) = 0; +void (*QAbstractDeclarativeData::objectNameChanged)(QAbstractDeclarativeData *, QObject *) = 0; + +QObjectData::~QObjectData() {} + +QObjectPrivate::QObjectPrivate(int version) + : threadData(0), connectionLists(0), senders(0), currentSender(0), currentChildBeingDeleted(0) +{ + if (version != QObjectPrivateVersion) + qFatal("Cannot mix incompatible Qt library (version 0x%x) with this library (version 0x%x)", + version, QObjectPrivateVersion); + + // QObjectData initialization + q_ptr = 0; + parent = 0; // no parent yet. It is set by setParent() + isWidget = false; // assume not a widget object + pendTimer = false; // no timers yet + blockSig = false; // not blocking signals + wasDeleted = false; // double-delete catcher + sendChildEvents = true; // if we should send ChildInsert and ChildRemove events to parent + receiveChildEvents = true; + postedEvents = 0; + extraData = 0; + connectedSignals[0] = connectedSignals[1] = 0; + inThreadChangeEvent = false; +#ifdef QT_JAMBI_BUILD + inEventHandler = false; + deleteWatch = 0; +#endif + metaObject = 0; + hasGuards = false; +} + +QObjectPrivate::~QObjectPrivate() +{ + if (pendTimer) { + // unregister pending timers + if (threadData->eventDispatcher) + threadData->eventDispatcher->unregisterTimers(q_ptr); + } + + if (postedEvents) + QCoreApplication::removePostedEvents(q_ptr, 0); + + threadData->deref(); + + delete static_cast(metaObject); +#ifdef QT_JAMBI_BUILD + if (deleteWatch) + *deleteWatch = 1; +#endif +#ifndef QT_NO_USERDATA + if (extraData) + qDeleteAll(extraData->userData); + delete extraData; +#endif +} + + +#ifdef QT_JAMBI_BUILD +int *QObjectPrivate::setDeleteWatch(QObjectPrivate *d, int *w) { + int *old = d->deleteWatch; + d->deleteWatch = w; + return old; +} + + +void QObjectPrivate::resetDeleteWatch(QObjectPrivate *d, int *oldWatch, int deleteWatch) { + if (!deleteWatch) + d->deleteWatch = oldWatch; + + if (oldWatch) + *oldWatch = deleteWatch; +} +#endif + +#ifdef QT3_SUPPORT +void QObjectPrivate::sendPendingChildInsertedEvents() +{ + Q_Q(QObject); + for (int i = 0; i < pendingChildInsertedEvents.size(); ++i) { + QObject *c = pendingChildInsertedEvents.at(i).data(); + if (!c || c->parent() != q) + continue; + QChildEvent childEvent(QEvent::ChildInserted, c); + QCoreApplication::sendEvent(q, &childEvent); + } + pendingChildInsertedEvents.clear(); +} + +#endif + + +/*!\internal + For a given metaobject, compute the signal offset, and the method offset (including signals) +*/ +static void computeOffsets(const QMetaObject *metaobject, int *signalOffset, int *methodOffset) +{ + *signalOffset = *methodOffset = 0; + const QMetaObject *m = metaobject->d.superdata; + while (m) { + const QMetaObjectPrivate *d = QMetaObjectPrivate::get(m); + *methodOffset += d->methodCount; + *signalOffset += (d->revision >= 4) ? d->signalCount : d->methodCount; + /*Before Qt 4.6 (revision 4), the signalCount information was not generated by moc. + so for compatibility we consider all the method as slot for old moc output*/ + m = m->d.superdata; + } +} + +/* + This vector contains the all connections from an object. + + Each object may have one vector containing the lists of + connections for a given signal. The index in the vector correspond + to the signal index. The signal index is the one returned by + QObjectPrivate::signalIndex (not QMetaObject::indexOfSignal). + Negative index means connections to all signals. + + This vector is protected by the object mutex (signalSlotMutexes()) + + Each Connection is also part of a 'senders' linked list. The mutex + of the receiver must be locked when touching the pointers of this + linked list. +*/ +class QObjectConnectionListVector : public QVector +{ +public: + bool orphaned; //the QObject owner of this vector has been destroyed while the vector was inUse + bool dirty; //some Connection have been disconnected (their receiver is 0) but not removed from the list yet + int inUse; //number of functions that are currently accessing this object or its connections + QObjectPrivate::ConnectionList allsignals; + + QObjectConnectionListVector() + : QVector(), orphaned(false), dirty(false), inUse(0) + { } + + QObjectPrivate::ConnectionList &operator[](int at) + { + if (at < 0) + return allsignals; + return QVector::operator[](at); + } +}; + +// Used by QAccessibleWidget +bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const +{ + Q_Q(const QObject); + int signal_index = signalIndex(signal); + if (signal_index < 0) + return false; + QMutexLocker locker(signalSlotLock(q)); + if (connectionLists) { + if (signal_index < connectionLists->count()) { + const QObjectPrivate::Connection *c = + connectionLists->at(signal_index).first; + + while (c) { + if (c->receiver == receiver) + return true; + c = c->nextConnectionList; + } + } + } + return false; +} + +// Used by QAccessibleWidget +QObjectList QObjectPrivate::receiverList(const char *signal) const +{ + Q_Q(const QObject); + QObjectList returnValue; + int signal_index = signalIndex(signal); + if (signal_index < 0) + return returnValue; + QMutexLocker locker(signalSlotLock(q)); + if (connectionLists) { + if (signal_index < connectionLists->count()) { + const QObjectPrivate::Connection *c = connectionLists->at(signal_index).first; + + while (c) { + if (c->receiver) + returnValue << c->receiver; + c = c->nextConnectionList; + } + } + } + return returnValue; +} + +// Used by QAccessibleWidget +QObjectList QObjectPrivate::senderList() const +{ + QObjectList returnValue; + QMutexLocker locker(signalSlotLock(q_func())); + for (Connection *c = senders; c; c = c->next) + returnValue << c->sender; + return returnValue; +} + +void QObjectPrivate::addConnection(int signal, Connection *c) +{ + if (!connectionLists) + connectionLists = new QObjectConnectionListVector(); + if (signal >= connectionLists->count()) + connectionLists->resize(signal + 1); + + ConnectionList &connectionList = (*connectionLists)[signal]; + if (connectionList.last) { + connectionList.last->nextConnectionList = c; + } else { + connectionList.first = c; + } + connectionList.last = c; + + cleanConnectionLists(); +} + +void QObjectPrivate::cleanConnectionLists() +{ + if (connectionLists->dirty && !connectionLists->inUse) { + // remove broken connections + for (int signal = -1; signal < connectionLists->count(); ++signal) { + QObjectPrivate::ConnectionList &connectionList = + (*connectionLists)[signal]; + + // Set to the last entry in the connection list that was *not* + // deleted. This is needed to update the list's last pointer + // at the end of the cleanup. + QObjectPrivate::Connection *last = 0; + + QObjectPrivate::Connection **prev = &connectionList.first; + QObjectPrivate::Connection *c = *prev; + while (c) { + if (c->receiver) { + last = c; + prev = &c->nextConnectionList; + c = *prev; + } else { + QObjectPrivate::Connection *next = c->nextConnectionList; + *prev = next; + delete c; + c = next; + } + } + + // Correct the connection list's last pointer. + // As conectionList.last could equal last, this could be a noop + connectionList.last = last; + } + connectionLists->dirty = false; + } +} + +typedef QMultiHash GuardHash; +Q_GLOBAL_STATIC(GuardHash, guardHash) +Q_GLOBAL_STATIC(QMutex, guardHashLock) + +/*!\internal + */ +void QMetaObject::addGuard(QObject **ptr) +{ + if (!*ptr) + return; + GuardHash *hash = guardHash(); + if (!hash) { + *ptr = 0; + return; + } + QMutexLocker locker(guardHashLock()); + QObjectPrivate::get(*ptr)->hasGuards = true; + hash->insert(*ptr, ptr); +} + +/*!\internal + */ +void QMetaObject::removeGuard(QObject **ptr) +{ + if (!*ptr) + return; + GuardHash *hash = guardHash(); + /* check that the hash is empty - otherwise we might detach + the shared_null hash, which will alloc, which is not nice */ + if (!hash || hash->isEmpty()) + return; + QMutexLocker locker(guardHashLock()); + if (!*ptr) //check again, under the lock + return; + GuardHash::iterator it = hash->find(*ptr); + const GuardHash::iterator end = hash->end(); + bool more = false; //if the QObject has more pointer attached to it. + for (; it.key() == *ptr && it != end; ++it) { + if (it.value() == ptr) { + it = hash->erase(it); + if (!more) more = (it != end && it.key() == *ptr); + break; + } + more = true; + } + if (!more) + QObjectPrivate::get(*ptr)->hasGuards = false; +} + +/*!\internal + */ +void QMetaObject::changeGuard(QObject **ptr, QObject *o) +{ + GuardHash *hash = guardHash(); + if (!hash) { + *ptr = 0; + return; + } + QMutexLocker locker(guardHashLock()); + if (o) { + hash->insert(o, ptr); + QObjectPrivate::get(o)->hasGuards = true; + } + if (*ptr) { + bool more = false; //if the QObject has more pointer attached to it. + GuardHash::iterator it = hash->find(*ptr); + const GuardHash::iterator end = hash->end(); + for (; it.key() == *ptr && it != end; ++it) { + if (it.value() == ptr) { + it = hash->erase(it); + if (!more) more = (it != end && it.key() == *ptr); + break; + } + more = true; + } + if (!more) + QObjectPrivate::get(*ptr)->hasGuards = false; + } + *ptr = o; +} + +/*! \internal + */ +void QObjectPrivate::clearGuards(QObject *object) +{ + GuardHash *hash = 0; + QMutex *mutex = 0; + QT_TRY { + hash = guardHash(); + mutex = guardHashLock(); + } QT_CATCH(const std::bad_alloc &) { + // do nothing in case of OOM - code below is safe + } + + /* check that the hash is empty - otherwise we might detach + the shared_null hash, which will alloc, which is not nice */ + if (hash && !hash->isEmpty()) { + QMutexLocker locker(mutex); + GuardHash::iterator it = hash->find(object); + const GuardHash::iterator end = hash->end(); + while (it.key() == object && it != end) { + *it.value() = 0; + it = hash->erase(it); + } + } +} + +/*! \internal + */ +QMetaCallEvent::QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction, + const QObject *sender, int signalId, + int nargs, int *types, void **args, QSemaphore *semaphore) + : QEvent(MetaCall), sender_(sender), signalId_(signalId), + nargs_(nargs), types_(types), args_(args), semaphore_(semaphore), + callFunction_(callFunction), method_offset_(method_offset), method_relative_(method_relative) +{ } + +/*! \internal + */ +QMetaCallEvent::~QMetaCallEvent() +{ + if (types_) { + for (int i = 0; i < nargs_; ++i) { + if (types_[i] && args_[i]) + QMetaType::destroy(types_[i], args_[i]); + } + qFree(types_); + qFree(args_); + } +#ifndef QT_NO_THREAD + if (semaphore_) + semaphore_->release(); +#endif +} + +/*! \internal + */ +void QMetaCallEvent::placeMetaCall(QObject *object) +{ + if (callFunction_) { + callFunction_(object, QMetaObject::InvokeMetaMethod, method_relative_, args_); + } else { + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method_offset_ + method_relative_, args_); + } +} + +/*! + \class QObject + \brief The QObject class is the base class of all Qt objects. + + \ingroup objectmodel + + \reentrant + + QObject is the heart of the Qt \l{Object Model}. The central + feature in this model is a very powerful mechanism for seamless + object communication called \l{signals and slots}. You can + connect a signal to a slot with connect() and destroy the + connection with disconnect(). To avoid never ending notification + loops you can temporarily block signals with blockSignals(). The + protected functions connectNotify() and disconnectNotify() make + it possible to track connections. + + QObjects organize themselves in \l {Object Trees & Ownership} + {object trees}. When you create a QObject with another object as + parent, the object will automatically add itself to the parent's + children() list. The parent takes ownership of the object; i.e., + it will automatically delete its children in its destructor. You + can look for an object by name and optionally type using + findChild() or findChildren(). + + Every object has an objectName() and its class name can be found + via the corresponding metaObject() (see QMetaObject::className()). + You can determine whether the object's class inherits another + class in the QObject inheritance hierarchy by using the + inherits() function. + + When an object is deleted, it emits a destroyed() signal. You can + catch this signal to avoid dangling references to QObjects. + + QObjects can receive events through event() and filter the events + of other objects. See installEventFilter() and eventFilter() for + details. A convenience handler, childEvent(), can be reimplemented + to catch child events. + + Events are delivered in the thread in which the object was + created; see \l{Thread Support in Qt} and thread() for details. + Note that event processing is not done at all for QObjects with no + thread affinity (thread() returns zero). Use the moveToThread() + function to change the thread affinity for an object and its + children (the object cannot be moved if it has a parent). + + Last but not least, QObject provides the basic timer support in + Qt; see QTimer for high-level support for timers. + + Notice that the Q_OBJECT macro is mandatory for any object that + implements signals, slots or properties. You also need to run the + \l{moc}{Meta Object Compiler} on the source file. We strongly + recommend the use of this macro in all subclasses of QObject + regardless of whether or not they actually use signals, slots and + properties, since failure to do so may lead certain functions to + exhibit strange behavior. + + All Qt widgets inherit QObject. The convenience function + isWidgetType() returns whether an object is actually a widget. It + is much faster than + \l{qobject_cast()}{qobject_cast}(\e{obj}) or + \e{obj}->\l{inherits()}{inherits}("QWidget"). + + Some QObject functions, e.g. children(), return a QObjectList. + QObjectList is a typedef for QList. + + \target No copy constructor + \section1 No copy constructor or assignment operator + + QObject has neither a copy constructor nor an assignment operator. + This is by design. Actually, they are declared, but in a + \c{private} section with the macro Q_DISABLE_COPY(). In fact, all + Qt classes derived from QObject (direct or indirect) use this + macro to declare their copy constructor and assignment operator to + be private. The reasoning is found in the discussion on + \l{Identity vs Value} {Identity vs Value} on the Qt \l{Object + Model} page. + + The main consequence is that you should use pointers to QObject + (or to your QObject subclass) where you might otherwise be tempted + to use your QObject subclass as a value. For example, without a + copy constructor, you can't use a subclass of QObject as the value + to be stored in one of the container classes. You must store + pointers. + + \section1 Auto-Connection + + Qt's meta-object system provides a mechanism to automatically connect + signals and slots between QObject subclasses and their children. As long + as objects are defined with suitable object names, and slots follow a + simple naming convention, this connection can be performed at run-time + by the QMetaObject::connectSlotsByName() function. + + \l uic generates code that invokes this function to enable + auto-connection to be performed between widgets on forms created + with \QD. More information about using auto-connection with \QD is + given in the \l{Using a Designer UI File in Your Application} section of + the \QD manual. + + \section1 Dynamic Properties + + From Qt 4.2, dynamic properties can be added to and removed from QObject + instances at run-time. Dynamic properties do not need to be declared at + compile-time, yet they provide the same advantages as static properties + and are manipulated using the same API - using property() to read them + and setProperty() to write them. + + From Qt 4.3, dynamic properties are supported by + \l{Qt Designer's Widget Editing Mode#The Property Editor}{Qt Designer}, + and both standard Qt widgets and user-created forms can be given dynamic + properties. + + \section1 Internationalization (i18n) + + All QObject subclasses support Qt's translation features, making it possible + to translate an application's user interface into different languages. + + To make user-visible text translatable, it must be wrapped in calls to + the tr() function. This is explained in detail in the + \l{Writing Source Code for Translation} document. + + \sa QMetaObject, QPointer, QObjectCleanupHandler, Q_DISABLE_COPY() + \sa {Object Trees & Ownership} +*/ + +/*! + \relates QObject + + Returns a pointer to the object named \a name that inherits \a + type and with a given \a parent. + + Returns 0 if there is no such child. + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 0 +*/ + +void *qt_find_obj_child(QObject *parent, const char *type, const QString &name) +{ + QObjectList list = parent->children(); + if (list.size() == 0) return 0; + for (int i = 0; i < list.size(); ++i) { + QObject *obj = list.at(i); + if (name == obj->objectName() && obj->inherits(type)) + return obj; + } + return 0; +} + + +/***************************************************************************** + QObject member functions + *****************************************************************************/ + +// check the constructor's parent thread argument +static bool check_parent_thread(QObject *parent, + QThreadData *parentThreadData, + QThreadData *currentThreadData) +{ + if (parent && parentThreadData != currentThreadData) { + QThread *parentThread = parentThreadData->thread; + QThread *currentThread = currentThreadData->thread; + qWarning("QObject: Cannot create children for a parent that is in a different thread.\n" + "(Parent is %s(%p), parent's thread is %s(%p), current thread is %s(%p)", + parent->metaObject()->className(), + parent, + parentThread ? parentThread->metaObject()->className() : "QThread", + parentThread, + currentThread ? currentThread->metaObject()->className() : "QThread", + currentThread); + return false; + } + return true; +} + +/*! + Constructs an object with parent object \a parent. + + The parent of an object may be viewed as the object's owner. For + instance, a \l{QDialog}{dialog box} is the parent of the \gui OK + and \gui Cancel buttons it contains. + + The destructor of a parent object destroys all child objects. + + Setting \a parent to 0 constructs an object with no parent. If the + object is a widget, it will become a top-level window. + + \sa parent(), findChild(), findChildren() +*/ + +QObject::QObject(QObject *parent) + : d_ptr(new QObjectPrivate) +{ + Q_D(QObject); + d_ptr->q_ptr = this; + d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); + d->threadData->ref(); + if (parent) { + QT_TRY { + if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) + parent = 0; + setParent(parent); + } QT_CATCH(...) { + d->threadData->deref(); + QT_RETHROW; + } + } + qt_addObject(this); +} + +#ifdef QT3_SUPPORT +/*! + \overload QObject() + \obsolete + + Creates a new QObject with the given \a parent and object \a name. + */ +QObject::QObject(QObject *parent, const char *name) + : d_ptr(new QObjectPrivate) +{ + Q_D(QObject); + qt_addObject(d_ptr->q_ptr = this); + d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); + d->threadData->ref(); + if (parent) { + if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) + parent = 0; + setParent(parent); + } + setObjectName(QString::fromAscii(name)); +} +#endif + +/*! \internal + */ +QObject::QObject(QObjectPrivate &dd, QObject *parent) + : d_ptr(&dd) +{ + Q_D(QObject); + d_ptr->q_ptr = this; + d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current(); + d->threadData->ref(); + if (parent) { + QT_TRY { + if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData)) + parent = 0; + if (d->isWidget) { + if (parent) { + d->parent = parent; + d->parent->d_func()->children.append(this); + } + // no events sent here, this is done at the end of the QWidget constructor + } else { + setParent(parent); + } + } QT_CATCH(...) { + d->threadData->deref(); + QT_RETHROW; + } + } + qt_addObject(this); +} + +/*! + Destroys the object, deleting all its child objects. + + All signals to and from the object are automatically disconnected, and + any pending posted events for the object are removed from the event + queue. However, it is often safer to use deleteLater() rather than + deleting a QObject subclass directly. + + \warning All child objects are deleted. If any of these objects + are on the stack or global, sooner or later your program will + crash. We do not recommend holding pointers to child objects from + outside the parent. If you still do, the destroyed() signal gives + you an opportunity to detect when an object is destroyed. + + \warning Deleting a QObject while pending events are waiting to + be delivered can cause a crash. You must not delete the QObject + directly if it exists in a different thread than the one currently + executing. Use deleteLater() instead, which will cause the event + loop to delete the object after all pending events have been + delivered to it. + + \sa deleteLater() +*/ + +QObject::~QObject() +{ + Q_D(QObject); + d->wasDeleted = true; + d->blockSig = 0; // unblock signals so we always emit destroyed() + + if (d->hasGuards && !d->isWidget) { + // set all QPointers for this object to zero - note that + // ~QWidget() does this for us, so we don't have to do it twice + QObjectPrivate::clearGuards(this); + } + + if (d->sharedRefcount) { + if (d->sharedRefcount->strongref > 0) { + qWarning("QObject: shared QObject was deleted directly. The program is malformed and may crash."); + // but continue deleting, it's too late to stop anyway + } + + // indicate to all QWeakPointers that this QObject has now been deleted + d->sharedRefcount->strongref = 0; + if (!d->sharedRefcount->weakref.deref()) + delete d->sharedRefcount; + } + + + if (d->isSignalConnected(0)) { + QT_TRY { + emit destroyed(this); + } QT_CATCH(...) { + // 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()."); + QT_RETHROW; + } + } + + if (d->declarativeData) + QAbstractDeclarativeData::destroyed(d->declarativeData, this); + + // set ref to zero to indicate that this object has been deleted + if (d->currentSender != 0) + d->currentSender->ref = 0; + d->currentSender = 0; + + if (d->connectionLists || d->senders) { + QMutex *signalSlotMutex = signalSlotLock(this); + QMutexLocker locker(signalSlotMutex); + + // disconnect all receivers + if (d->connectionLists) { + ++d->connectionLists->inUse; + int connectionListsCount = d->connectionLists->count(); + for (int signal = -1; signal < connectionListsCount; ++signal) { + QObjectPrivate::ConnectionList &connectionList = + (*d->connectionLists)[signal]; + + while (QObjectPrivate::Connection *c = connectionList.first) { + if (!c->receiver) { + connectionList.first = c->nextConnectionList; + delete c; + continue; + } + + QMutex *m = signalSlotLock(c->receiver); + bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); + + if (c->receiver) { + *c->prev = c->next; + if (c->next) c->next->prev = c->prev; + } + if (needToUnlock) + m->unlockInline(); + + connectionList.first = c->nextConnectionList; + delete c; + } + } + + if (!--d->connectionLists->inUse) { + delete d->connectionLists; + } else { + d->connectionLists->orphaned = true; + } + d->connectionLists = 0; + } + + // disconnect all senders + QObjectPrivate::Connection *node = d->senders; + while (node) { + QObject *sender = node->sender; + QMutex *m = signalSlotLock(sender); + node->prev = &node; + bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); + //the node has maybe been removed while the mutex was unlocked in relock? + if (!node || node->sender != sender) { + m->unlockInline(); + continue; + } + node->receiver = 0; + QObjectConnectionListVector *senderLists = sender->d_func()->connectionLists; + if (senderLists) + senderLists->dirty = true; + + node = node->next; + if (needToUnlock) + m->unlockInline(); + } + } + + if (!d->children.isEmpty()) + d->deleteChildren(); + + qt_removeObject(this); + + if (d->parent) // remove it from parent object + d->setParent_helper(0); + +#ifdef QT_JAMBI_BUILD + if (d->inEventHandler) { + qWarning("QObject: Do not delete object, '%s', during its event handler!", + objectName().isNull() ? "unnamed" : qPrintable(objectName())); + } +#endif +} + +QObjectPrivate::Connection::~Connection() +{ + if (argumentTypes != &DIRECT_CONNECTION_ONLY) + delete [] static_cast(argumentTypes); +} + + +/*! + \fn QMetaObject *QObject::metaObject() const + + Returns a pointer to the meta-object of this object. + + A meta-object contains information about a class that inherits + QObject, e.g. class name, superclass name, properties, signals and + slots. Every QObject subclass that contains the Q_OBJECT macro will have a + meta-object. + + The meta-object information is required by the signal/slot + connection mechanism and the property system. The inherits() + function also makes use of the meta-object. + + If you have no pointer to an actual object instance but still + want to access the meta-object of a class, you can use \l + staticMetaObject. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 1 + + \sa staticMetaObject +*/ + +/*! + \variable QObject::staticMetaObject + + This variable stores the meta-object for the class. + + A meta-object contains information about a class that inherits + QObject, e.g. class name, superclass name, properties, signals and + slots. Every class that contains the Q_OBJECT macro will also have + a meta-object. + + The meta-object information is required by the signal/slot + connection mechanism and the property system. The inherits() + function also makes use of the meta-object. + + If you have a pointer to an object, you can use metaObject() to + retrieve the meta-object associated with that object. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 2 + + \sa metaObject() +*/ + +/*! \fn T *qobject_cast(QObject *object) + \relates QObject + + Returns the given \a object cast to type T if the object is of type + T (or of a subclass); otherwise returns 0. If \a object is 0 then + it will also return 0. + + The class T must inherit (directly or indirectly) QObject and be + declared with the \l Q_OBJECT macro. + + A class is considered to inherit itself. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 3 + + The qobject_cast() function behaves similarly to the standard C++ + \c dynamic_cast(), with the advantages that it doesn't require + RTTI support and it works across dynamic library boundaries. + + qobject_cast() can also be used in conjunction with interfaces; + see the \l{tools/plugandpaint}{Plug & Paint} example for details. + + \warning If T isn't declared with the Q_OBJECT macro, this + function's return value is undefined. + + \sa QObject::inherits() +*/ + +/*! + \fn bool QObject::inherits(const char *className) const + + Returns true if this object is an instance of a class that + inherits \a className or a QObject subclass that inherits \a + className; otherwise returns false. + + A class is considered to inherit itself. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 4 + + If you need to determine whether an object is an instance of a particular + class for the purpose of casting it, consider using qobject_cast(object) + instead. + + \sa metaObject(), qobject_cast() +*/ + +/*! + \property QObject::objectName + + \brief the name of this object + + You can find an object by name (and type) using findChild(). You can + find a set of objects with findChildren(). + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 5 + + By default, this property contains an empty string. + + \sa metaObject(), QMetaObject::className() +*/ + +QString QObject::objectName() const +{ + Q_D(const QObject); + return d->objectName; +} + +/* + Sets the object's name to \a name. +*/ +void QObject::setObjectName(const QString &name) +{ + Q_D(QObject); + bool objectNameChanged = d->declarativeData && d->objectName != name; + + d->objectName = name; + + if (objectNameChanged) + d->declarativeData->objectNameChanged(d->declarativeData, this); +} + + +#ifdef QT3_SUPPORT +/*! \internal + QObject::child is compat but needs to call itself recursively, + that's why we need this helper. +*/ +static QObject *qChildHelper(const char *objName, const char *inheritsClass, + bool recursiveSearch, const QObjectList &children) +{ + if (children.isEmpty()) + return 0; + + bool onlyWidgets = (inheritsClass && qstrcmp(inheritsClass, "QWidget") == 0); + const QLatin1String oName(objName); + for (int i = 0; i < children.size(); ++i) { + QObject *obj = children.at(i); + if (onlyWidgets) { + if (obj->isWidgetType() && (!objName || obj->objectName() == oName)) + return obj; + } else if ((!inheritsClass || obj->inherits(inheritsClass)) + && (!objName || obj->objectName() == oName)) + return obj; + if (recursiveSearch && (obj = qChildHelper(objName, inheritsClass, + recursiveSearch, obj->children()))) + return obj; + } + return 0; +} + + +/*! + Searches the children and optionally grandchildren of this object, + and returns a child that is called \a objName that inherits \a + inheritsClass. If \a inheritsClass is 0 (the default), any class + matches. + + If \a recursiveSearch is true (the default), child() performs a + depth-first search of the object's children. + + If there is no such object, this function returns 0. If there are + more than one, the first one found is returned. +*/ +QObject* QObject::child(const char *objName, const char *inheritsClass, + bool recursiveSearch) const +{ + Q_D(const QObject); + return qChildHelper(objName, inheritsClass, recursiveSearch, d->children); +} +#endif + +/*! + \fn bool QObject::isWidgetType() const + + Returns true if the object is a widget; otherwise returns false. + + Calling this function is equivalent to calling + inherits("QWidget"), except that it is much faster. +*/ + + +/*! + This virtual function receives events to an object and should + return true if the event \a e was recognized and processed. + + The event() function can be reimplemented to customize the + behavior of an object. + + \sa installEventFilter(), timerEvent(), QApplication::sendEvent(), + QApplication::postEvent(), QWidget::event() +*/ + +bool QObject::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::Timer: + timerEvent((QTimerEvent*)e); + break; + +#ifdef QT3_SUPPORT + case QEvent::ChildInsertedRequest: + d_func()->sendPendingChildInsertedEvents(); + break; +#endif + + case QEvent::ChildAdded: + case QEvent::ChildPolished: +#ifdef QT3_SUPPORT + case QEvent::ChildInserted: +#endif + case QEvent::ChildRemoved: + childEvent((QChildEvent*)e); + break; + + case QEvent::DeferredDelete: + qDeleteInEventHandler(this); + break; + + case QEvent::MetaCall: + { +#ifdef QT_JAMBI_BUILD + d_func()->inEventHandler = false; +#endif + QMetaCallEvent *mce = static_cast(e); + QObjectPrivate::Sender currentSender; + currentSender.sender = const_cast(mce->sender()); + currentSender.signal = mce->signalId(); + currentSender.ref = 1; + QObjectPrivate::Sender * const previousSender = + QObjectPrivate::setCurrentSender(this, ¤tSender); +#if defined(QT_NO_EXCEPTIONS) + mce->placeMetaCall(this); +#else + QT_TRY { + mce->placeMetaCall(this); + } QT_CATCH(...) { + QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); + QT_RETHROW; + } +#endif + QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); + break; + } + + case QEvent::ThreadChange: { + Q_D(QObject); + QThreadData *threadData = d->threadData; + QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher; + if (eventDispatcher) { + QList > timers = eventDispatcher->registeredTimers(this); + if (!timers.isEmpty()) { + // set inThreadChangeEvent to true to tell the dispatcher not to release out timer ids + // back to the pool (since the timer ids are moving to a new thread). + d->inThreadChangeEvent = true; + eventDispatcher->unregisterTimers(this); + d->inThreadChangeEvent = false; + QMetaObject::invokeMethod(this, "_q_reregisterTimers", Qt::QueuedConnection, + Q_ARG(void*, (new QList >(timers)))); + } + } + break; + } + + default: + if (e->type() >= QEvent::User) { + customEvent(e); + break; + } + return false; + } + return true; +} + +/*! + \fn void QObject::timerEvent(QTimerEvent *event) + + This event handler can be reimplemented in a subclass to receive + timer events for the object. + + QTimer provides a higher-level interface to the timer + functionality, and also more general information about timers. The + timer event is passed in the \a event parameter. + + \sa startTimer(), killTimer(), event() +*/ + +void QObject::timerEvent(QTimerEvent *) +{ +} + + +/*! + This event handler can be reimplemented in a subclass to receive + child events. The event is passed in the \a event parameter. + + QEvent::ChildAdded and QEvent::ChildRemoved events are sent to + objects when children are added or removed. In both cases you can + only rely on the child being a QObject, or if isWidgetType() + returns true, a QWidget. (This is because, in the + \l{QEvent::ChildAdded}{ChildAdded} case, the child is not yet + fully constructed, and in the \l{QEvent::ChildRemoved}{ChildRemoved} + case it might have been destructed already). + + QEvent::ChildPolished events are sent to widgets when children + are polished, or when polished children are added. If you receive + a child polished event, the child's construction is usually + completed. However, this is not guaranteed, and multiple polish + events may be delivered during the execution of a widget's + constructor. + + For every child widget, you receive one + \l{QEvent::ChildAdded}{ChildAdded} event, zero or more + \l{QEvent::ChildPolished}{ChildPolished} events, and one + \l{QEvent::ChildRemoved}{ChildRemoved} event. + + The \l{QEvent::ChildPolished}{ChildPolished} event is omitted if + a child is removed immediately after it is added. If a child is + polished several times during construction and destruction, you + may receive several child polished events for the same child, + each time with a different virtual table. + + \sa event() +*/ + +void QObject::childEvent(QChildEvent * /* event */) +{ +} + + +/*! + This event handler can be reimplemented in a subclass to receive + custom events. Custom events are user-defined events with a type + value at least as large as the QEvent::User item of the + QEvent::Type enum, and is typically a QEvent subclass. The event + is passed in the \a event parameter. + + \sa event(), QEvent +*/ +void QObject::customEvent(QEvent * /* event */) +{ +} + + + +/*! + Filters events if this object has been installed as an event + filter for the \a watched object. + + In your reimplementation of this function, if you want to filter + the \a event out, i.e. stop it being handled further, return + true; otherwise return false. + + Example: + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 6 + + Notice in the example above that unhandled events are passed to + the base class's eventFilter() function, since the base class + might have reimplemented eventFilter() for its own internal + purposes. + + \warning If you delete the receiver object in this function, be + sure to return true. Otherwise, Qt will forward the event to the + deleted object and the program might crash. + + \sa installEventFilter() +*/ + +bool QObject::eventFilter(QObject * /* watched */, QEvent * /* event */) +{ + return false; +} + +/*! + \fn bool QObject::signalsBlocked() const + + Returns true if signals are blocked; otherwise returns false. + + Signals are not blocked by default. + + \sa blockSignals() +*/ + +/*! + If \a block is true, signals emitted by this object are blocked + (i.e., emitting a signal will not invoke anything connected to it). + If \a block is false, no such blocking will occur. + + The return value is the previous value of signalsBlocked(). + + Note that the destroyed() signal will be emitted even if the signals + for this object have been blocked. + + \sa signalsBlocked() +*/ + +bool QObject::blockSignals(bool block) +{ + Q_D(QObject); + bool previous = d->blockSig; + d->blockSig = block; + return previous; +} + +/*! + Returns the thread in which the object lives. + + \sa moveToThread() +*/ +QThread *QObject::thread() const +{ + return d_func()->threadData->thread; +} + +/*! + Changes the thread affinity for this object and its children. The + object cannot be moved if it has a parent. Event processing will + continue in the \a targetThread. + + To move an object to the main thread, use QApplication::instance() + to retrieve a pointer to the current application, and then use + QApplication::thread() to retrieve the thread in which the + application lives. For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 7 + + If \a targetThread is zero, all event processing for this object + and its children stops. + + Note that all active timers for the object will be reset. The + timers are first stopped in the current thread and restarted (with + the same interval) in the \a targetThread. As a result, constantly + moving an object between threads can postpone timer events + indefinitely. + + A QEvent::ThreadChange event is sent to this object just before + the thread affinity is changed. You can handle this event to + perform any special processing. Note that any new events that are + posted to this object will be handled in the \a targetThread. + + \warning This function is \e not thread-safe; the current thread + must be same as the current thread affinity. In other words, this + function can only "push" an object from the current thread to + another thread, it cannot "pull" an object from any arbitrary + thread to the current thread. + + \sa thread() + */ +void QObject::moveToThread(QThread *targetThread) +{ + Q_D(QObject); + + if (d->threadData->thread == targetThread) { + // object is already in this thread + return; + } + + if (d->parent != 0) { + qWarning("QObject::moveToThread: Cannot move objects with a parent"); + return; + } + if (d->isWidget) { + qWarning("QObject::moveToThread: Widgets cannot be moved to a new thread"); + return; + } + + QThreadData *currentData = QThreadData::current(); + QThreadData *targetData = targetThread ? QThreadData::get2(targetThread) : new QThreadData(0); + if (d->threadData->thread == 0 && currentData == targetData) { + // one exception to the rule: we allow moving objects with no thread affinity to the current thread + currentData = d->threadData; + } else if (d->threadData != currentData) { + qWarning("QObject::moveToThread: Current thread (%p) is not the object's thread (%p).\n" + "Cannot move to target thread (%p)\n", + currentData->thread, d->threadData->thread, targetData->thread); + +#ifdef Q_WS_MAC + qWarning("On Mac OS X, you might be loading two sets of Qt binaries into the same process. " + "Check that all plugins are compiled against the right Qt binaries. Export " + "DYLD_PRINT_LIBRARIES=1 and check that only one set of binaries are being loaded."); +#endif + + return; + } + + // prepare to move + d->moveToThread_helper(); + + QOrderedMutexLocker locker(¤tData->postEventList.mutex, + &targetData->postEventList.mutex); + + // keep currentData alive (since we've got it locked) + currentData->ref(); + + // move the object + d_func()->setThreadData_helper(currentData, targetData); + + locker.unlock(); + + // now currentData can commit suicide if it wants to + currentData->deref(); +} + +void QObjectPrivate::moveToThread_helper() +{ + Q_Q(QObject); + QEvent e(QEvent::ThreadChange); + QCoreApplication::sendEvent(q, &e); + for (int i = 0; i < children.size(); ++i) { + QObject *child = children.at(i); + child->d_func()->moveToThread_helper(); + } +} + +void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData *targetData) +{ + Q_Q(QObject); + + // move posted events + int eventsMoved = 0; + for (int i = 0; i < currentData->postEventList.size(); ++i) { + const QPostEvent &pe = currentData->postEventList.at(i); + if (!pe.event) + continue; + if (pe.receiver == q) { + // move this post event to the targetList + targetData->postEventList.append(pe); + const_cast(pe).event = 0; + ++eventsMoved; + } + } + if (eventsMoved > 0 && targetData->eventDispatcher) { + targetData->canWait = false; + targetData->eventDispatcher->wakeUp(); + } + + // the current emitting thread shouldn't restore currentSender after calling moveToThread() + if (currentSender) + currentSender->ref = 0; + currentSender = 0; + +#ifdef QT_JAMBI_BUILD + // the current event thread also shouldn't restore the delete watch + inEventHandler = false; + + if (deleteWatch) + *deleteWatch = 1; + deleteWatch = 0; +#endif + + // set new thread data + targetData->ref(); + threadData->deref(); + threadData = targetData; + + for (int i = 0; i < children.size(); ++i) { + QObject *child = children.at(i); + child->d_func()->setThreadData_helper(currentData, targetData); + } +} + +void QObjectPrivate::_q_reregisterTimers(void *pointer) +{ + Q_Q(QObject); + QList > *timerList = reinterpret_cast > *>(pointer); + QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher; + for (int i = 0; i < timerList->size(); ++i) { + const QPair &pair = timerList->at(i); + eventDispatcher->registerTimer(pair.first, pair.second, q); + } + delete timerList; +} + + +// +// The timer flag hasTimer is set when startTimer is called. +// It is not reset when killing the timer because more than +// one timer might be active. +// + +/*! + Starts a timer and returns a timer identifier, or returns zero if + it could not start a timer. + + A timer event will occur every \a interval milliseconds until + killTimer() is called. If \a interval is 0, then the timer event + occurs once every time there are no more window system events to + process. + + The virtual timerEvent() function is called with the QTimerEvent + event parameter class when a timer event occurs. Reimplement this + function to get timer events. + + If multiple timers are running, the QTimerEvent::timerId() can be + used to find out which timer was activated. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 8 + + Note that QTimer's accuracy depends on the underlying operating + system and hardware. Most platforms support an accuracy of 20 + milliseconds; some provide more. If Qt is unable to deliver the + requested number of timer events, it will silently discard some. + + The QTimer class provides a high-level programming interface with + single-shot timers and timer signals instead of events. There is + also a QBasicTimer class that is more lightweight than QTimer and + less clumsy than using timer IDs directly. + + \sa timerEvent(), killTimer(), QTimer::singleShot() +*/ + +int QObject::startTimer(int interval) +{ + Q_D(QObject); + + if (interval < 0) { + qWarning("QObject::startTimer: QTimer cannot have a negative interval"); + return 0; + } + + d->pendTimer = true; // set timer flag + + if (!d->threadData->eventDispatcher) { + qWarning("QObject::startTimer: QTimer can only be used with threads started with QThread"); + return 0; + } + return d->threadData->eventDispatcher->registerTimer(interval, this); +} + +/*! + Kills the timer with timer identifier, \a id. + + The timer identifier is returned by startTimer() when a timer + event is started. + + \sa timerEvent(), startTimer() +*/ + +void QObject::killTimer(int id) +{ + Q_D(QObject); + if (d->threadData->eventDispatcher) + d->threadData->eventDispatcher->unregisterTimer(id); +} + + +/*! + \fn QObject *QObject::parent() const + + Returns a pointer to the parent object. + + \sa children() +*/ + +/*! + \fn const QObjectList &QObject::children() const + + Returns a list of child objects. + The QObjectList class is defined in the \c{} header + file as the following: + + \quotefromfile src/corelib/kernel/qobject.h + \skipto /typedef .*QObjectList/ + \printuntil QObjectList + + The first child added is the \l{QList::first()}{first} object in + the list and the last child added is the \l{QList::last()}{last} + object in the list, i.e. new children are appended at the end. + + Note that the list order changes when QWidget children are + \l{QWidget::raise()}{raised} or \l{QWidget::lower()}{lowered}. A + widget that is raised becomes the last object in the list, and a + widget that is lowered becomes the first object in the list. + + \sa findChild(), findChildren(), parent(), setParent() +*/ + +#ifdef QT3_SUPPORT +static void objSearch(QObjectList &result, + const QObjectList &list, + const char *inheritsClass, + bool onlyWidgets, + const char *objName, + QRegExp *rx, + bool recurse) +{ + for (int i = 0; i < list.size(); ++i) { + QObject *obj = list.at(i); + if (!obj) + continue; + bool ok = true; + if (onlyWidgets) + ok = obj->isWidgetType(); + else if (inheritsClass && !obj->inherits(inheritsClass)) + ok = false; + if (ok) { + if (objName) + ok = (obj->objectName() == QLatin1String(objName)); +#ifndef QT_NO_REGEXP + else if (rx) + ok = (rx->indexIn(obj->objectName()) != -1); +#endif + } + if (ok) // match! + result.append(obj); + if (recurse) { + QObjectList clist = obj->children(); + if (!clist.isEmpty()) + objSearch(result, clist, inheritsClass, + onlyWidgets, objName, rx, recurse); + } + } +} + +/*! + \internal + + Searches the children and optionally grandchildren of this object, + and returns a list of those objects that are named or that match + \a objName and inherit \a inheritsClass. If \a inheritsClass is 0 + (the default), all classes match. If \a objName is 0 (the + default), all object names match. + + If \a regexpMatch is true (the default), \a objName is a regular + expression that the objects's names must match. The syntax is that + of a QRegExp. If \a regexpMatch is false, \a objName is a string + and object names must match it exactly. + + Note that \a inheritsClass uses single inheritance from QObject, + the way inherits() does. According to inherits(), QWidget + inherits QObject but not QPaintDevice. This does not quite match + reality, but is the best that can be done on the wide variety of + compilers Qt supports. + + Finally, if \a recursiveSearch is true (the default), queryList() + searches \e{n}th-generation as well as first-generation children. + + If all this seems a bit complex for your needs, the simpler + child() function may be what you want. + + This somewhat contrived example disables all the buttons in this + window: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 9 + + \warning Delete the list as soon you have finished using it. The + list contains pointers that may become invalid at almost any time + without notice (as soon as the user closes a window you may have + dangling pointers, for example). + + \sa child() children(), parent(), inherits(), objectName(), QRegExp +*/ + +QObjectList QObject::queryList(const char *inheritsClass, + const char *objName, + bool regexpMatch, + bool recursiveSearch) const +{ + Q_D(const QObject); + QObjectList list; + bool onlyWidgets = (inheritsClass && qstrcmp(inheritsClass, "QWidget") == 0); +#ifndef QT_NO_REGEXP + if (regexpMatch && objName) { // regexp matching + QRegExp rx(QString::fromLatin1(objName)); + objSearch(list, d->children, inheritsClass, onlyWidgets, 0, &rx, recursiveSearch); + } else +#endif + { + objSearch(list, d->children, inheritsClass, onlyWidgets, objName, 0, recursiveSearch); + } + return list; +} +#endif + +/*! + \fn T *QObject::findChild(const QString &name) const + + Returns the child of this object that can be cast into type T and + that is called \a name, or 0 if there is no such object. + Omitting the \a name argument causes all object names to be matched. + The search is performed recursively. + + If there is more than one child matching the search, the most + direct ancestor is returned. If there are several direct + ancestors, it is undefined which one will be returned. In that + case, findChildren() should be used. + + This example returns a child \l{QPushButton} of \c{parentWidget} + named \c{"button1"}: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 10 + + This example returns a \l{QListWidget} child of \c{parentWidget}: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 11 + + \sa findChildren() +*/ + +/*! + \fn QList QObject::findChildren(const QString &name) const + + Returns all children of this object with the given \a name that can be + cast to type T, or an empty list if there are no such objects. + Omitting the \a name argument causes all object names to be matched. + The search is performed recursively. + + The following example shows how to find a list of child \l{QWidget}s of + the specified \c{parentWidget} named \c{widgetname}: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 12 + + This example returns all \c{QPushButton}s that are children of \c{parentWidget}: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 13 + + \sa findChild() +*/ + +/*! + \fn QList QObject::findChildren(const QRegExp ®Exp) const + \overload findChildren() + + Returns the children of this object that can be cast to type T + and that have names matching the regular expression \a regExp, + or an empty list if there are no such objects. + The search is performed recursively. +*/ + +/*! + \fn T qFindChild(const QObject *obj, const QString &name) + \relates QObject + \obsolete + + This function is equivalent to + \a{obj}->\l{QObject::findChild()}{findChild}(\a name). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QObject::findChild() +*/ + +/*! + \fn QList qFindChildren(const QObject *obj, const QString &name) + \relates QObject + \obsolete + + This function is equivalent to + \a{obj}->\l{QObject::findChildren()}{findChildren}(\a name). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QObject::findChildren() +*/ + +/*! + \fn QList qFindChildren(const QObject *obj, const QRegExp ®Exp) + \relates QObject + \overload qFindChildren() + + This function is equivalent to + \a{obj}->\l{QObject::findChildren()}{findChildren}(\a regExp). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QObject::findChildren() +*/ + +/*! + \internal + \fn T qFindChild(const QObject *obj, const QString &name = QString(), T dummy = 0) + \relates QObject + \overload qFindChildren() + + This function is equivalent to + \a{obj}->\l{QObject::findChild()}{findChild}(\a name). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QObject::findChild() +*/ + +/*! + \internal + \fn QList qFindChildren(const QObject *obj, const QString &name = QString(), T dummy = 0) + \relates QObject + \overload qFindChildren() + + This function is equivalent to + \a{obj}->\l{QObject::findChildren()}{findChildren}(\a name). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QObject::findChildren() +*/ + +/*! + \internal +*/ +void qt_qFindChildren_helper(const QObject *parent, const QString &name, const QRegExp *re, + const QMetaObject &mo, QList *list) +{ + if (!parent || !list) + return; + const QObjectList &children = parent->children(); + QObject *obj; + for (int i = 0; i < children.size(); ++i) { + obj = children.at(i); + if (mo.cast(obj)) { + if (re) { + if (re->indexIn(obj->objectName()) != -1) + list->append(obj); + } else { + if (name.isNull() || obj->objectName() == name) + list->append(obj); + } + } + qt_qFindChildren_helper(obj, name, re, mo, list); + } +} + +/*! \internal + */ +QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo) +{ + if (!parent) + return 0; + const QObjectList &children = parent->children(); + QObject *obj; + int i; + for (i = 0; i < children.size(); ++i) { + obj = children.at(i); + if (mo.cast(obj) && (name.isNull() || obj->objectName() == name)) + return obj; + } + for (i = 0; i < children.size(); ++i) { + obj = qt_qFindChild_helper(children.at(i), name, mo); + if (obj) + return obj; + } + return 0; +} + +/*! + Makes the object a child of \a parent. + + \sa QWidget::setParent() +*/ + +void QObject::setParent(QObject *parent) +{ + Q_D(QObject); + Q_ASSERT(!d->isWidget); + d->setParent_helper(parent); +} + +void QObjectPrivate::deleteChildren() +{ + const bool reallyWasDeleted = wasDeleted; + wasDeleted = true; + // delete children objects + // don't use qDeleteAll as the destructor of the child might + // delete siblings + for (int i = 0; i < children.count(); ++i) { + currentChildBeingDeleted = children.at(i); + children[i] = 0; + delete currentChildBeingDeleted; + } + children.clear(); + currentChildBeingDeleted = 0; + wasDeleted = reallyWasDeleted; +} + +void QObjectPrivate::setParent_helper(QObject *o) +{ + Q_Q(QObject); + if (o == parent) + return; + if (parent) { + QObjectPrivate *parentD = parent->d_func(); + if (parentD->wasDeleted && wasDeleted + && parentD->currentChildBeingDeleted == q) { + // don't do anything since QObjectPrivate::deleteChildren() already + // cleared our entry in parentD->children. + } else { + const int index = parentD->children.indexOf(q); + if (parentD->wasDeleted) { + parentD->children[index] = 0; + } else { + parentD->children.removeAt(index); + if (sendChildEvents && parentD->receiveChildEvents) { + QChildEvent e(QEvent::ChildRemoved, q); + QCoreApplication::sendEvent(parent, &e); + } + } + } + } + parent = o; + if (parent) { + // object hierarchies are constrained to a single thread + if (threadData != parent->d_func()->threadData) { + qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread"); + parent = 0; + return; + } + parent->d_func()->children.append(q); + if(sendChildEvents && parent->d_func()->receiveChildEvents) { + if (!isWidget) { + QChildEvent e(QEvent::ChildAdded, q); + QCoreApplication::sendEvent(parent, &e); +#ifdef QT3_SUPPORT + if (QCoreApplicationPrivate::useQt3Support) { + if (parent->d_func()->pendingChildInsertedEvents.isEmpty()) { + QCoreApplication::postEvent(parent, + new QEvent(QEvent::ChildInsertedRequest), + Qt::HighEventPriority); + } + parent->d_func()->pendingChildInsertedEvents.append(q); + } +#endif + } + } + } + if (!wasDeleted && declarativeData) + QAbstractDeclarativeData::parentChanged(declarativeData, q, o); +} + +/*! + \fn void QObject::installEventFilter(QObject *filterObj) + + Installs an event filter \a filterObj on this object. For example: + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 14 + + An event filter is an object that receives all events that are + sent to this object. The filter can either stop the event or + forward it to this object. The event filter \a filterObj receives + events via its eventFilter() function. The eventFilter() function + must return true if the event should be filtered, (i.e. stopped); + otherwise it must return false. + + If multiple event filters are installed on a single object, the + filter that was installed last is activated first. + + Here's a \c KeyPressEater class that eats the key presses of its + monitored objects: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 15 + + And here's how to install it on two widgets: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 16 + + The QShortcut class, for example, uses this technique to intercept + shortcut key presses. + + \warning If you delete the receiver object in your eventFilter() + function, be sure to return true. If you return false, Qt sends + the event to the deleted object and the program will crash. + + Note that the filtering object must be in the same thread as this + object. If \a filterObj is in a different thread, this function does + nothing. If either \a filterObj or this object are moved to a different + thread after calling this function, the event filter will not be + called until both objects have the same thread affinity again (it + is \e not removed). + + \sa removeEventFilter(), eventFilter(), event() +*/ + +void QObject::installEventFilter(QObject *obj) +{ + Q_D(QObject); + if (!obj) + return; + if (d->threadData != obj->d_func()->threadData) { + qWarning("QObject::installEventFilter(): Cannot filter events for objects in a different thread."); + return; + } + + // clean up unused items in the list + d->eventFilters.removeAll((QObject*)0); + d->eventFilters.removeAll(obj); + d->eventFilters.prepend(obj); +} + +/*! + Removes an event filter object \a obj from this object. The + request is ignored if such an event filter has not been installed. + + All event filters for this object are automatically removed when + this object is destroyed. + + It is always safe to remove an event filter, even during event + filter activation (i.e. from the eventFilter() function). + + \sa installEventFilter(), eventFilter(), event() +*/ + +void QObject::removeEventFilter(QObject *obj) +{ + Q_D(QObject); + for (int i = 0; i < d->eventFilters.count(); ++i) { + if (d->eventFilters.at(i) == obj) + d->eventFilters[i] = 0; + } +} + + +/*! + \fn QObject::destroyed(QObject *obj) + + This signal is emitted immediately before the object \a obj is + destroyed, and can not be blocked. + + All the objects's children are destroyed immediately after this + signal is emitted. + + \sa deleteLater(), QPointer +*/ + +/*! + Schedules this object for deletion. + + The object will be deleted when control returns to the event + loop. If the event loop is not running when this function is + called (e.g. deleteLater() is called on an object before + QCoreApplication::exec()), the object will be deleted once the + event loop is started. + + Note that entering and leaving a new event loop (e.g., by opening a modal + dialog) will \e not perform the deferred deletion; for the object to be + deleted, the control must return to the event loop from which + deleteLater() was called. + + \bold{Note:} It is safe to call this function more than once; when the + first deferred deletion event is delivered, any pending events for the + object are removed from the event queue. + + \sa destroyed(), QPointer +*/ +void QObject::deleteLater() +{ + QCoreApplication::postEvent(this, new QEvent(QEvent::DeferredDelete)); +} + +/*! + \fn QString QObject::tr(const char *sourceText, const char *disambiguation, int n) + \reentrant + + Returns a translated version of \a sourceText, optionally based on a + \a disambiguation string and value of \a n for strings containing plurals; + otherwise returns \a sourceText itself if no appropriate translated string + is available. + + Example: + \snippet mainwindows/sdi/mainwindow.cpp implicit tr context + \dots + + If the same \a sourceText is used in different roles within the + same context, an additional identifying string may be passed in + \a disambiguation (0 by default). In Qt 4.4 and earlier, this was + the preferred way to pass comments to translators. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 17 + \dots + + See \l{Writing Source Code for Translation} for a detailed description of + Qt's translation mechanisms in general, and the + \l{Writing Source Code for Translation#Disambiguation}{Disambiguation} + section for information on disambiguation. + + \warning This method is reentrant only if all translators are + installed \e before calling this method. Installing or removing + translators while performing translations is not supported. Doing + so will probably result in crashes or other undesirable behavior. + + \sa trUtf8(), QApplication::translate(), QTextCodec::setCodecForTr(), {Internationalization with Qt} +*/ + +/*! + \fn QString QObject::trUtf8(const char *sourceText, const char *disambiguation, int n) + \reentrant + + Returns a translated version of \a sourceText, or + QString::fromUtf8(\a sourceText) if there is no appropriate + version. It is otherwise identical to tr(\a sourceText, \a + disambiguation, \a n). + + Note that using the Utf8 variants of the translation functions + is not required if \c CODECFORTR is already set to UTF-8 in the + qmake project file and QTextCodec::setCodecForTr("UTF-8") is + used. + + \warning This method is reentrant only if all translators are + installed \e before calling this method. Installing or removing + translators while performing translations is not supported. Doing + so will probably result in crashes or other undesirable behavior. + + \warning For portability reasons, we recommend that you use + escape sequences for specifying non-ASCII characters in string + literals to trUtf8(). For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 20 + + \sa tr(), QApplication::translate(), {Internationalization with Qt} +*/ + + + + +/***************************************************************************** + Signals and slots + *****************************************************************************/ + + +const int flagged_locations_count = 2; +static const char* flagged_locations[flagged_locations_count] = {0}; + +const char *qFlagLocation(const char *method) +{ + static int idx = 0; + flagged_locations[idx] = method; + idx = (idx+1) % flagged_locations_count; + return method; +} + +static int extract_code(const char *member) +{ + // extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE + return (((int)(*member) - '0') & 0x3); +} + +static const char * extract_location(const char *member) +{ + for (int i = 0; i < flagged_locations_count; ++i) { + if (member == flagged_locations[i]) { + // signature includes location information after the first null-terminator + const char *location = member + qstrlen(member) + 1; + if (*location != '\0') + return location; + return 0; + } + } + return 0; +} + +static bool check_signal_macro(const QObject *sender, const char *signal, + const char *func, const char *op) +{ + int sigcode = extract_code(signal); + if (sigcode != QSIGNAL_CODE) { + if (sigcode == QSLOT_CODE) + qWarning("Object::%s: Attempt to %s non-signal %s::%s", + func, op, sender->metaObject()->className(), signal+1); + else + qWarning("Object::%s: Use the SIGNAL macro to %s %s::%s", + func, op, sender->metaObject()->className(), signal); + return false; + } + return true; +} + +static bool check_method_code(int code, const QObject *object, + const char *method, const char *func) +{ + if (code != QSLOT_CODE && code != QSIGNAL_CODE) { + qWarning("Object::%s: Use the SLOT or SIGNAL macro to " + "%s %s::%s", func, func, object->metaObject()->className(), method); + return false; + } + return true; +} + +static void err_method_notfound(const QObject *object, + const char *method, const char *func) +{ + const char *type = "method"; + switch (extract_code(method)) { + case QSLOT_CODE: type = "slot"; break; + case QSIGNAL_CODE: type = "signal"; break; + } + const char *loc = extract_location(method); + if (strchr(method,')') == 0) // common typing mistake + qWarning("Object::%s: Parentheses expected, %s %s::%s%s%s", + func, type, object->metaObject()->className(), method+1, + loc ? " in ": "", loc ? loc : ""); + else + qWarning("Object::%s: No such %s %s::%s%s%s", + func, type, object->metaObject()->className(), method+1, + loc ? " in ": "", loc ? loc : ""); + +} + + +static void err_info_about_objects(const char * func, + const QObject * sender, + const QObject * receiver) +{ + QString a = sender ? sender->objectName() : QString(); + QString b = receiver ? receiver->objectName() : QString(); + if (!a.isEmpty()) + qWarning("Object::%s: (sender name: '%s')", func, a.toLocal8Bit().data()); + if (!b.isEmpty()) + qWarning("Object::%s: (receiver name: '%s')", func, b.toLocal8Bit().data()); +} + +/*! + Returns a pointer to the object that sent the signal, if called in + a slot activated by a signal; otherwise it returns 0. The pointer + is valid only during the execution of the slot that calls this + function from this object's thread context. + + The pointer returned by this function becomes invalid if the + sender is destroyed, or if the slot is disconnected from the + sender's signal. + + \warning This function violates the object-oriented principle of + modularity. However, getting access to the sender might be useful + when many signals are connected to a single slot. + + \warning As mentioned above, the return value of this function is + not valid when the slot is called via a Qt::DirectConnection from + a thread different from this object's thread. Do not use this + function in this type of scenario. + + \sa senderSignalIndex(), QSignalMapper +*/ + +QObject *QObject::sender() const +{ + Q_D(const QObject); + + QMutexLocker locker(signalSlotLock(this)); + if (!d->currentSender) + return 0; + + for (QObjectPrivate::Connection *c = d->senders; c; c = c->next) { + if (c->sender == d->currentSender->sender) + return d->currentSender->sender; + } + + return 0; +} + +/*! + \since 4.8 + + Returns the meta-method index of the signal that called the currently + executing slot, which is a member of the class returned by sender(). + If called outside of a slot activated by a signal, -1 is returned. + + For signals with default parameters, this function will always return + the index with all parameters, regardless of which was used with + connect(). For example, the signal \c {destroyed(QObject *obj = 0)} + will have two different indexes (with and without the parameter), but + this function will always return the index with a parameter. This does + not apply when overloading signals with different parameters. + + \warning This function violates the object-oriented principle of + modularity. However, getting access to the signal index might be useful + when many signals are connected to a single slot. + + \warning The return value of this function is not valid when the slot + is called via a Qt::DirectConnection from a thread different from this + object's thread. Do not use this function in this type of scenario. + + \sa sender(), QMetaObject::indexOfSignal(), QMetaObject::method() +*/ + +int QObject::senderSignalIndex() const +{ + Q_D(const QObject); + + QMutexLocker locker(signalSlotLock(this)); + if (!d->currentSender) + return -1; + + for (QObjectPrivate::Connection *c = d->senders; c; c = c->next) { + if (c->sender == d->currentSender->sender) + return d->currentSender->signal; + } + + return -1; +} + +/*! + Returns the number of receivers connected to the \a signal. + + Since both slots and signals can be used as receivers for signals, + and the same connections can be made many times, the number of + receivers is the same as the number of connections made from this + signal. + + When calling this function, you can use the \c SIGNAL() macro to + pass a specific signal: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 21 + + As the code snippet above illustrates, you can use this function + to avoid emitting a signal that nobody listens to. + + \warning This function violates the object-oriented principle of + modularity. However, it might be useful when you need to perform + expensive initialization only if something is connected to a + signal. +*/ + +int QObject::receivers(const char *signal) const +{ + Q_D(const QObject); + int receivers = 0; + if (signal) { + QByteArray signal_name = QMetaObject::normalizedSignature(signal); + signal = signal_name; +#ifndef QT_NO_DEBUG + if (!check_signal_macro(this, signal, "receivers", "bind")) + return 0; +#endif + signal++; // skip code + int signal_index = d->signalIndex(signal); + if (signal_index < 0) { +#ifndef QT_NO_DEBUG + err_method_notfound(this, signal-1, "receivers"); +#endif + return false; + } + + Q_D(const QObject); + QMutexLocker locker(signalSlotLock(this)); + if (d->connectionLists) { + if (signal_index < d->connectionLists->count()) { + const QObjectPrivate::Connection *c = + d->connectionLists->at(signal_index).first; + while (c) { + receivers += c->receiver ? 1 : 0; + c = c->nextConnectionList; + } + } + } + } + return receivers; +} + +/*! + \internal + + This helper function calculates signal and method index for the given + member in the specified class. + + \li If member.mobj is 0 then both signalIndex and methodIndex are set to -1. + + \li If specified member is not a member of obj instance class (or one of + its parent classes) then both signalIndex and methodIndex are set to -1. + + This function is used by QObject::connect and QObject::disconnect which + are working with QMetaMethod. + + \param[out] signalIndex is set to the signal index of member. If the member + specified is not signal this variable is set to -1. + + \param[out] methodIndex is set to the method index of the member. If the + member is not a method of the object specified by obj param this variable + is set to -1. +*/ +void QMetaObjectPrivate::memberIndexes(const QObject *obj, + const QMetaMethod &member, + int *signalIndex, int *methodIndex) +{ + *signalIndex = -1; + *methodIndex = -1; + if (!obj || !member.mobj) + return; + const QMetaObject *m = obj->metaObject(); + // Check that member is member of obj class + while (m != 0 && m != member.mobj) + m = m->d.superdata; + if (!m) + return; + *signalIndex = *methodIndex = (member.handle - get(member.mobj)->methodData)/5; + + int signalOffset; + int methodOffset; + computeOffsets(m, &signalOffset, &methodOffset); + + *methodIndex += methodOffset; + if (member.methodType() == QMetaMethod::Signal) { + *signalIndex = originalClone(m, *signalIndex); + *signalIndex += signalOffset; + } else { + *signalIndex = -1; + } +} + +static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaMethod &signal, + const QMetaObject *receiver, const QMetaMethod &method) +{ + if (signal.attributes() & QMetaMethod::Compatibility) { + if (!(method.attributes() & QMetaMethod::Compatibility)) + qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", + sender->className(), signal.signature()); + } else if ((method.attributes() & QMetaMethod::Compatibility) && + method.methodType() == QMetaMethod::Signal) { + qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)", + sender->className(), signal.signature(), + receiver->className(), method.signature()); + } +} + +/*! + \threadsafe + + Creates a connection of the given \a type from the \a signal in + the \a sender object to the \a method in the \a receiver object. + Returns true if the connection succeeds; otherwise returns false. + + You must use the \c SIGNAL() and \c SLOT() macros when specifying + the \a signal and the \a method, for example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 22 + + This example ensures that the label always displays the current + scroll bar value. Note that the signal and slots parameters must not + contain any variable names, only the type. E.g. the following would + not work and return false: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 23 + + A signal can also be connected to another signal: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 24 + + In this example, the \c MyWidget constructor relays a signal from + a private member variable, and makes it available under a name + that relates to \c MyWidget. + + A signal can be connected to many slots and signals. Many signals + can be connected to one slot. + + If a signal is connected to several slots, the slots are activated + in the same order as the order the connection was made, when the + signal is emitted. + + The function returns true if it successfully connects the signal + to the slot. It will return false if it cannot create the + connection, for example, if QObject is unable to verify the + existence of either \a signal or \a method, or if their signatures + aren't compatible. + + By default, a signal is emitted for every connection you make; + two signals are emitted for duplicate connections. You can break + all of these connections with a single disconnect() call. + If you pass the Qt::UniqueConnection \a type, the connection will only + be made if it is not a duplicate. If there is already a duplicate + (exact same signal to the exact same slot on the same objects), + the connection will fail and connect will return false. + + The optional \a type parameter describes the type of connection + to establish. In particular, it determines whether a particular + signal is delivered to a slot immediately or queued for delivery + at a later time. If the signal is queued, the parameters must be + of types that are known to Qt's meta-object system, because Qt + needs to copy the arguments to store them in an event behind the + scenes. If you try to use a queued connection and get the error + message + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 25 + + call qRegisterMetaType() to register the data type before you + establish the connection. + + \sa disconnect(), sender(), qRegisterMetaType() +*/ + +bool QObject::connect(const QObject *sender, const char *signal, + const QObject *receiver, const char *method, + Qt::ConnectionType type) +{ + { + const void *cbdata[] = { sender, signal, receiver, method, &type }; + if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) + return true; + } + +#ifndef QT_NO_DEBUG + bool warnCompat = true; +#endif + if (type == Qt::AutoCompatConnection) { + type = Qt::AutoConnection; +#ifndef QT_NO_DEBUG + warnCompat = false; +#endif + } + + if (sender == 0 || receiver == 0 || signal == 0 || method == 0) { + qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", + sender ? sender->metaObject()->className() : "(null)", + (signal && *signal) ? signal+1 : "(null)", + receiver ? receiver->metaObject()->className() : "(null)", + (method && *method) ? method+1 : "(null)"); + return false; + } + QByteArray tmp_signal_name; + + if (!check_signal_macro(sender, signal, "connect", "bind")) + return false; + const QMetaObject *smeta = sender->metaObject(); + const char *signal_arg = signal; + ++signal; //skip code + 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, false); + } + if (signal_index < 0) { + // re-use tmp_signal_name and signal from above + + 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; + computeOffsets(smeta, &signalOffset, &methodOffset); + int signal_absolute_index = signal_index + methodOffset; + signal_index += signalOffset; + + QByteArray tmp_method_name; + int membcode = extract_code(method); + + if (!check_method_code(membcode, receiver, method, "connect")) + return false; + const char *method_arg = method; + ++method; // skip code + + const QMetaObject *rmeta = receiver->metaObject(); + int method_index_relative = -1; + switch (membcode) { + case QSLOT_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); + break; + case QSIGNAL_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); + break; + } + + if (method_index_relative < 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_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); + if (method_index_relative < 0) + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true); + break; + case QSIGNAL_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); + if (method_index_relative < 0) + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true); + break; + } + } + + if (method_index_relative < 0) { + err_method_notfound(receiver, method_arg, "connect"); + err_info_about_objects("connect", sender, receiver); + return false; + } + + if (!QMetaObject::checkConnectArgs(signal, method)) { + qWarning("QObject::connect: Incompatible sender/receiver arguments" + "\n %s::%s --> %s::%s", + sender->metaObject()->className(), signal, + receiver->metaObject()->className(), method); + return false; + } + + int *types = 0; + if ((type == Qt::QueuedConnection) + && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes()))) + return false; + +#ifndef QT_NO_DEBUG + if (warnCompat) { + QMetaMethod smethod = smeta->method(signal_absolute_index); + QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset()); + check_and_warn_compat(smeta, smethod, rmeta, rmethod); + } +#endif + if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types)) + return false; + const_cast(sender)->connectNotify(signal - 1); + return true; +} + +/*! + \since 4.8 + + Creates a connection of the given \a type from the \a signal in + the \a sender object to the \a method in the \a receiver object. + Returns true if the connection succeeds; otherwise returns false. + + This function works in the same way as + connect(const QObject *sender, const char *signal, + const QObject *receiver, const char *method, + Qt::ConnectionType type) + but it uses QMetaMethod to specify signal and method. + + \see connect(const QObject *sender, const char *signal, + const QObject *receiver, const char *method, + Qt::ConnectionType type) + */ +bool QObject::connect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &method, + Qt::ConnectionType type) +{ +#ifndef QT_NO_DEBUG + bool warnCompat = true; +#endif + if (type == Qt::AutoCompatConnection) { + type = Qt::AutoConnection; +#ifndef QT_NO_DEBUG + warnCompat = false; +#endif + } + + if (sender == 0 + || receiver == 0 + || signal.methodType() != QMetaMethod::Signal + || method.methodType() == QMetaMethod::Constructor) { + qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", + sender ? sender->metaObject()->className() : "(null)", + signal.signature(), + receiver ? receiver->metaObject()->className() : "(null)", + method.signature() ); + return false; + } + + // Reconstructing SIGNAL() macro result for signal.signature() string + QByteArray signalSignature; + signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.append((char)(QSIGNAL_CODE + '0')); + signalSignature.append(signal.signature()); + + { + QByteArray methodSignature; + methodSignature.reserve(qstrlen(method.signature())+1); + methodSignature.append((char)(method.methodType() == QMetaMethod::Slot ? QSLOT_CODE + : method.methodType() == QMetaMethod::Signal ? QSIGNAL_CODE : 0 + '0')); + methodSignature.append(method.signature()); + const void *cbdata[] = { sender, signalSignature.constData(), receiver, methodSignature.constData(), &type }; + if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) + return true; + } + + + int signal_index; + int method_index; + { + int dummy; + QMetaObjectPrivate::memberIndexes(sender, signal, &signal_index, &dummy); + QMetaObjectPrivate::memberIndexes(receiver, method, &dummy, &method_index); + } + + const QMetaObject *smeta = sender->metaObject(); + const QMetaObject *rmeta = receiver->metaObject(); + if (signal_index == -1) { + qWarning("QObject::connect: Can't find signal %s on instance of class %s", + signal.signature(), smeta->className()); + return false; + } + if (method_index == -1) { + qWarning("QObject::connect: Can't find method %s on instance of class %s", + method.signature(), rmeta->className()); + return false; + } + + if (!QMetaObject::checkConnectArgs(signal.signature(), method.signature())) { + qWarning("QObject::connect: Incompatible sender/receiver arguments" + "\n %s::%s --> %s::%s", + smeta->className(), signal.signature(), + rmeta->className(), method.signature()); + return false; + } + + int *types = 0; + if ((type == Qt::QueuedConnection) + && !(types = queuedConnectionTypes(signal.parameterTypes()))) + return false; + +#ifndef QT_NO_DEBUG + if (warnCompat) + check_and_warn_compat(smeta, signal, rmeta, method); +#endif + if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, 0, type, types)) + return false; + + const_cast(sender)->connectNotify(signalSignature.constData()); + return true; +} + +/*! + \fn bool QObject::connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type) const + \overload connect() + \threadsafe + + Connects \a signal from the \a sender object to this object's \a + method. + + Equivalent to connect(\a sender, \a signal, \c this, \a method, \a type). + + Every connection you make emits a signal, so duplicate connections emit + two signals. You can break a connection using disconnect(). + + \sa disconnect() +*/ + +/*! + \threadsafe + + Disconnects \a signal in object \a sender from \a method in object + \a receiver. Returns true if the connection is successfully broken; + otherwise returns false. + + A signal-slot connection is removed when either of the objects + involved are destroyed. + + disconnect() is typically used in three ways, as the following + examples demonstrate. + \list 1 + \i Disconnect everything connected to an object's signals: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 26 + + equivalent to the non-static overloaded function + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 27 + + \i Disconnect everything connected to a specific signal: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 28 + + equivalent to the non-static overloaded function + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 29 + + \i Disconnect a specific receiver: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 30 + + equivalent to the non-static overloaded function + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 31 + + \endlist + + 0 may be used as a wildcard, meaning "any signal", "any receiving + object", or "any slot in the receiving object", respectively. + + The \a sender may never be 0. (You cannot disconnect signals from + more than one object in a single call.) + + If \a signal is 0, it disconnects \a receiver and \a method from + any signal. If not, only the specified signal is disconnected. + + If \a receiver is 0, it disconnects anything connected to \a + signal. If not, slots in objects other than \a receiver are not + disconnected. + + If \a method is 0, it disconnects anything that is connected to \a + receiver. If not, only slots named \a method will be disconnected, + and all other slots are left alone. The \a method must be 0 if \a + receiver is left out, so you cannot disconnect a + specifically-named slot on all objects. + + \sa connect() +*/ +bool QObject::disconnect(const QObject *sender, const char *signal, + const QObject *receiver, const char *method) +{ + if (sender == 0 || (receiver == 0 && method != 0)) { + qWarning("Object::disconnect: Unexpected null parameter"); + return false; + } + + { + const void *cbdata[] = { sender, signal, receiver, method }; + if (QInternal::activateCallbacks(QInternal::DisconnectCallback, (void **) cbdata)) + return true; + } + + const char *signal_arg = signal; + QByteArray signal_name; + bool signal_found = false; + if (signal) { + QT_TRY { + signal_name = QMetaObject::normalizedSignature(signal); + signal = signal_name.constData(); + } QT_CATCH (const std::bad_alloc &) { + // if the signal is already normalized, we can continue. + if (sender->metaObject()->indexOfSignal(signal + 1) == -1) + QT_RETHROW; + } + + if (!check_signal_macro(sender, signal, "disconnect", "unbind")) + return false; + signal++; // skip code + } + + QByteArray method_name; + const char *method_arg = method; + int membcode = -1; + bool method_found = false; + if (method) { + QT_TRY { + method_name = QMetaObject::normalizedSignature(method); + method = method_name.constData(); + } QT_CATCH(const std::bad_alloc &) { + // if the method is already normalized, we can continue. + if (receiver->metaObject()->indexOfMethod(method + 1) == -1) + QT_RETHROW; + } + + membcode = extract_code(method); + if (!check_method_code(membcode, receiver, method, "disconnect")) + return false; + method++; // skip code + } + + /* We now iterate through all the sender's and receiver's meta + * objects in order to also disconnect possibly shadowed signals + * and slots with the same signature. + */ + bool res = false; + const QMetaObject *smeta = sender->metaObject(); + do { + int signal_index = -1; + if (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); + int signalOffset, methodOffset; + computeOffsets(smeta, &signalOffset, &methodOffset); + signal_index += signalOffset; + signal_found = true; + } + + if (!method) { + res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, -1); + } else { + const QMetaObject *rmeta = receiver->metaObject(); + do { + int method_index = rmeta->indexOfMethod(method); + if (method_index >= 0) + while (method_index < rmeta->methodOffset()) + rmeta = rmeta->superClass(); + if (method_index < 0) + break; + res |= QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index); + method_found = true; + } while ((rmeta = rmeta->superClass())); + } + } while (signal && (smeta = smeta->superClass())); + + if (signal && !signal_found) { + err_method_notfound(sender, signal_arg, "disconnect"); + err_info_about_objects("disconnect", sender, receiver); + } else if (method && !method_found) { + err_method_notfound(receiver, method_arg, "disconnect"); + err_info_about_objects("disconnect", sender, receiver); + } + if (res) + const_cast(sender)->disconnectNotify(signal ? (signal - 1) : 0); + return res; +} + +/*! + \since 4.8 + + Disconnects \a signal in object \a sender from \a method in object + \a receiver. Returns true if the connection is successfully broken; + otherwise returns false. + + This function provides the same posibilities like + disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method) + but uses QMetaMethod to represent the signal and the method to be disconnected. + + Additionally this function returnsfalse and no signals and slots disconnected + if: + \list 1 + + \i \a signal is not a member of sender class or one of its parent classes. + + \i \a method is not a member of receiver class or one of its parent classes. + + \i \a signal instance represents not a signal. + + \endlist + + QMetaMethod() may be used as wildcard in the meaning "any signal" or "any slot in receiving object". + In the same way 0 can be used for \a receiver in the meaning "any receiving object". In this case + method shoud also be QMetaMethod(). \a sender parameter should be never 0. + + \see disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method) + */ +bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &method) +{ + if (sender == 0 || (receiver == 0 && method.mobj != 0)) { + qWarning("Object::disconnect: Unexpected null parameter"); + return false; + } + if (signal.mobj) { + if(signal.methodType() != QMetaMethod::Signal) { + qWarning("Object::%s: Attempt to %s non-signal %s::%s", + "disconnect","unbind", + sender->metaObject()->className(), signal.signature()); + return false; + } + } + if (method.mobj) { + if(method.methodType() == QMetaMethod::Constructor) { + qWarning("QObject::disconect: cannot use constructor as argument %s::%s", + receiver->metaObject()->className(), method.signature()); + return false; + } + } + + // Reconstructing SIGNAL() macro result for signal.signature() string + QByteArray signalSignature; + if (signal.mobj) { + signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.append((char)(QSIGNAL_CODE + '0')); + signalSignature.append(signal.signature()); + } + + { + QByteArray methodSignature; + if (method.mobj) { + methodSignature.reserve(qstrlen(method.signature())+1); + methodSignature.append((char)(method.methodType() == QMetaMethod::Slot ? QSLOT_CODE + : method.methodType() == QMetaMethod::Signal ? QSIGNAL_CODE : 0 + '0')); + methodSignature.append(method.signature()); + } + const void *cbdata[] = { sender, signal.mobj ? signalSignature.constData() : 0, + receiver, method.mobj ? methodSignature.constData() : 0 }; + if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) + return true; + } + + int signal_index; + int method_index; + { + int dummy; + QMetaObjectPrivate::memberIndexes(sender, signal, &signal_index, &dummy); + QMetaObjectPrivate::memberIndexes(receiver, method, &dummy, &method_index); + } + // If we are here sender is not null. If signal is not null while signal_index + // is -1 then this signal is not a member of sender. + if (signal.mobj && signal_index == -1) { + qWarning("QObject::disconect: signal %s not found on class %s", + signal.signature(), sender->metaObject()->className()); + return false; + } + // If this condition is true then method is not a member of receeiver. + if (receiver && method.mobj && method_index == -1) { + qWarning("QObject::disconect: method %s not found on class %s", + method.signature(), receiver->metaObject()->className()); + return false; + } + + if (!QMetaObjectPrivate::disconnect(sender, signal_index, receiver, method_index)) + return false; + + const_cast(sender)->disconnectNotify(method.mobj ? signalSignature.constData() : 0); + return true; +} + +/*! + \threadsafe + + \fn bool QObject::disconnect(const char *signal, const QObject *receiver, const char *method) + \overload disconnect() + + Disconnects \a signal from \a method of \a receiver. + + A signal-slot connection is removed when either of the objects + involved are destroyed. +*/ + +/*! + \fn bool QObject::disconnect(const QObject *receiver, const char *method) + \overload disconnect() + + Disconnects all signals in this object from \a receiver's \a + method. + + A signal-slot connection is removed when either of the objects + involved are destroyed. +*/ + + +/*! + \fn void QObject::connectNotify(const char *signal) + + This virtual function is called when something has been connected + to \a signal in this object. + + If you want to compare \a signal with a specific signal, use + QLatin1String and the \c SIGNAL() macro as follows: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 32 + + If the signal contains multiple parameters or parameters that + contain spaces, call QMetaObject::normalizedSignature() on + the result of the \c SIGNAL() macro. + + \warning This function violates the object-oriented principle of + modularity. However, it might be useful when you need to perform + expensive initialization only if something is connected to a + signal. + + \sa connect(), disconnectNotify() +*/ + +void QObject::connectNotify(const char *) +{ +} + +/*! + \fn void QObject::disconnectNotify(const char *signal) + + This virtual function is called when something has been + disconnected from \a signal in this object. + + See connectNotify() for an example of how to compare + \a signal with a specific signal. + + \warning This function violates the object-oriented principle of + modularity. However, it might be useful for optimizing access to + expensive resources. + + \sa disconnect(), connectNotify() +*/ + +void QObject::disconnectNotify(const char *) +{ +} + +/* \internal + convert a signal index from the method range to the signal range + */ +static int methodIndexToSignalIndex(const QMetaObject *metaObject, int signal_index) +{ + if (signal_index < 0) + return signal_index; + while (metaObject && metaObject->methodOffset() > signal_index) + metaObject = metaObject->superClass(); + + if (metaObject) { + int signalOffset, methodOffset; + computeOffsets(metaObject, &signalOffset, &methodOffset); + if (signal_index < metaObject->methodCount()) + signal_index = QMetaObjectPrivate::originalClone(metaObject, signal_index - methodOffset) + signalOffset; + else + signal_index = signal_index - methodOffset + signalOffset; + } + return signal_index; +} + +/*!\internal + \a types is a 0-terminated vector of meta types for queued + connections. + + if \a signal_index is -1, then we effectively connect *all* signals + from the sender to the receiver's slot + */ +bool QMetaObject::connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, int type, int *types) +{ + signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index); + return QMetaObjectPrivate::connect(sender, signal_index, + receiver, method_index, + 0, //FIXME, we could speed this connection up by computing the relative index + type, types); +} + +/*! \internal + Same as the QMetaObject::connect, but \a signal_index must be the result of QObjectPrivate::signalIndex + + method_index is relative to the rmeta metaobject, if rmeta is null, then it is absolute index + */ +bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, + const QMetaObject *rmeta, int type, int *types) +{ + QObject *s = const_cast(sender); + QObject *r = const_cast(receiver); + + int method_offset = rmeta ? rmeta->methodOffset() : 0; + QObjectPrivate::StaticMetaCallFunction callFunction = + (rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata) + ? reinterpret_cast(rmeta->d.extradata)->static_metacall : 0; + + QOrderedMutexLocker locker(signalSlotLock(sender), + signalSlotLock(receiver)); + + if (type & Qt::UniqueConnection) { + QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists; + if (connectionLists && connectionLists->count() > signal_index) { + const QObjectPrivate::Connection *c2 = + (*connectionLists)[signal_index].first; + + int method_index_absolute = method_index + method_offset; + + while (c2) { + if (c2->receiver == receiver && c2->method() == method_index_absolute) + return false; + c2 = c2->nextConnectionList; + } + } + type &= Qt::UniqueConnection - 1; + } + + QObjectPrivate::Connection *c = new QObjectPrivate::Connection; + c->sender = s; + c->receiver = r; + c->method_relative = method_index; + c->method_offset = method_offset; + c->connectionType = type; + c->argumentTypes = types; + c->nextConnectionList = 0; + c->callFunction = callFunction; + + QT_TRY { + QObjectPrivate::get(s)->addConnection(signal_index, c); + } QT_CATCH(...) { + delete c; + QT_RETHROW; + } + + c->prev = &(QObjectPrivate::get(r)->senders); + c->next = *c->prev; + *c->prev = c; + if (c->next) + c->next->prev = &c->next; + + QObjectPrivate *const sender_d = QObjectPrivate::get(s); + if (signal_index < 0) { + sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0; + } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) { + sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f)); + } + + return true; +} + +/*!\internal + */ +bool QMetaObject::disconnect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index) +{ + signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index); + return QMetaObjectPrivate::disconnect(sender, signal_index, + receiver, method_index); +} + +/*!\internal + +Disconnect a single signal connection. If QMetaObject::connect() has been called +multiple times for the same sender, signal_index, receiver and method_index only +one of these connections will be removed. + */ +bool QMetaObject::disconnectOne(const QObject *sender, int signal_index, + const QObject *receiver, int method_index) +{ + signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index); + return QMetaObjectPrivate::disconnect(sender, signal_index, + receiver, method_index, + QMetaObjectPrivate::DisconnectOne); +} + +/*! \internal + Helper function to remove the connection from the senders list and setting the receivers to 0 + */ +bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c, + const QObject *receiver, int method_index, + QMutex *senderMutex, DisconnectType disconnectType) +{ + bool success = false; + while (c) { + if (c->receiver + && (receiver == 0 || (c->receiver == receiver + && (method_index < 0 || c->method() == method_index)))) { + bool needToUnlock = false; + QMutex *receiverMutex = 0; + if (!receiver) { + receiverMutex = signalSlotLock(c->receiver); + // need to relock this receiver and sender in the correct order + needToUnlock = QOrderedMutexLocker::relock(senderMutex, receiverMutex); + } + if (c->receiver) { + *c->prev = c->next; + if (c->next) + c->next->prev = c->prev; + } + + if (needToUnlock) + receiverMutex->unlockInline(); + + c->receiver = 0; + + success = true; + + if (disconnectType == DisconnectOne) + return success; + } + c = c->nextConnectionList; + } + return success; +} + +/*! \internal + Same as the QMetaObject::disconnect, but \a signal_index must be the result of QObjectPrivate::signalIndex + */ +bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, + DisconnectType disconnectType) +{ + if (!sender) + return false; + + QObject *s = const_cast(sender); + + QMutex *senderMutex = signalSlotLock(sender); + QMutex *receiverMutex = receiver ? signalSlotLock(receiver) : 0; + QOrderedMutexLocker locker(senderMutex, receiverMutex); + + QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists; + if (!connectionLists) + return false; + + // prevent incoming connections changing the connectionLists while unlocked + ++connectionLists->inUse; + + bool success = false; + if (signal_index < 0) { + // remove from all connection lists + for (signal_index = -1; signal_index < connectionLists->count(); ++signal_index) { + QObjectPrivate::Connection *c = + (*connectionLists)[signal_index].first; + if (disconnectHelper(c, receiver, method_index, senderMutex, disconnectType)) { + success = true; + connectionLists->dirty = true; + } + } + } else if (signal_index < connectionLists->count()) { + QObjectPrivate::Connection *c = + (*connectionLists)[signal_index].first; + if (disconnectHelper(c, receiver, method_index, senderMutex, disconnectType)) { + success = true; + connectionLists->dirty = true; + } + } + + --connectionLists->inUse; + Q_ASSERT(connectionLists->inUse >= 0); + if (connectionLists->orphaned && !connectionLists->inUse) + delete connectionLists; + + return success; +} + +/*! + \fn void QMetaObject::connectSlotsByName(QObject *object) + + Searches recursively for all child objects of the given \a object, and connects + matching signals from them to slots of \a object that follow the following form: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 33 + + Let's assume our object has a child object of type QPushButton with + the \l{QObject::objectName}{object name} \c{button1}. The slot to catch the + button's \c{clicked()} signal would be: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 34 + + \sa QObject::setObjectName() + */ +void QMetaObject::connectSlotsByName(QObject *o) +{ + if (!o) + return; + const QMetaObject *mo = o->metaObject(); + Q_ASSERT(mo); + const QObjectList list = o->findChildren(QString()); + for (int i = 0; i < mo->methodCount(); ++i) { + const char *slot = mo->method(i).signature(); + Q_ASSERT(slot); + if (slot[0] != 'o' || slot[1] != 'n' || slot[2] != '_') + continue; + bool foundIt = false; + for(int j = 0; j < list.count(); ++j) { + const QObject *co = list.at(j); + QByteArray objName = co->objectName().toAscii(); + int len = objName.length(); + if (!len || qstrncmp(slot + 3, objName.data(), len) || slot[len+3] != '_') + continue; + int sigIndex = co->d_func()->signalIndex(slot + len + 4); + if (sigIndex < 0) { // search for compatible signals + const QMetaObject *smo = co->metaObject(); + int slotlen = qstrlen(slot + len + 4) - 1; + for (int k = 0; k < co->metaObject()->methodCount(); ++k) { + QMetaMethod method = smo->method(k); + if (method.methodType() != QMetaMethod::Signal) + continue; + + if (!qstrncmp(method.signature(), slot + len + 4, slotlen)) { + int signalOffset, methodOffset; + computeOffsets(method.enclosingMetaObject(), &signalOffset, &methodOffset); + sigIndex = k + - methodOffset + signalOffset; + break; + } + } + } + if (sigIndex < 0) + continue; + if (QMetaObjectPrivate::connect(co, sigIndex, o, i)) { + foundIt = true; + break; + } + } + if (foundIt) { + // we found our slot, now skip all overloads + while (mo->method(i + 1).attributes() & QMetaMethod::Cloned) + ++i; + } else if (!(mo->method(i).attributes() & QMetaMethod::Cloned)) { + qWarning("QMetaObject::connectSlotsByName: No matching signal for %s", slot); + } + } +} + +static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv) +{ + if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) { + QMetaMethod m = sender->metaObject()->method(signal); + int *tmp = queuedConnectionTypes(m.parameterTypes()); + if (!tmp) // cannot queue arguments + tmp = &DIRECT_CONNECTION_ONLY; + if (!c->argumentTypes.testAndSetOrdered(0, tmp)) { + if (tmp != &DIRECT_CONNECTION_ONLY) + delete [] tmp; + } + } + if (c->argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate + return; + int nargs = 1; // include return type + while (c->argumentTypes[nargs-1]) + ++nargs; + int *types = (int *) qMalloc(nargs*sizeof(int)); + Q_CHECK_PTR(types); + void **args = (void **) qMalloc(nargs*sizeof(void *)); + Q_CHECK_PTR(args); + types[0] = 0; // return type + args[0] = 0; // return value + for (int n = 1; n < nargs; ++n) + args[n] = QMetaType::construct((types[n] = c->argumentTypes[n-1]), argv[n]); + QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method_offset, + c->method_relative, + c->callFunction, + sender, signal, nargs, + types, args)); +} + + +/*!\internal + \obsolete. + Used to be called from QMetaObject::activate(QObject *, QMetaObject *, int, int, void **) before Qt 4.6 + */ +void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv) +{ + Q_UNUSED(to_signal_index); + activate(sender, from_signal_index, argv); +} + +/*!\internal + */ +void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index, + void **argv) +{ + int signalOffset; + int methodOffset; + computeOffsets(m, &signalOffset, &methodOffset); + + int signal_index = signalOffset + local_signal_index; + + if (!sender->d_func()->isSignalConnected(signal_index)) + return; // nothing connected to these signals, and no spy + + if (sender->d_func()->blockSig) + return; + + int signal_absolute_index = methodOffset + local_signal_index; + + void *empty_argv[] = { 0 }; + if (qt_signal_spy_callback_set.signal_begin_callback != 0) { + qt_signal_spy_callback_set.signal_begin_callback(sender, signal_absolute_index, + argv ? argv : empty_argv); + } + + Qt::HANDLE currentThreadId = QThread::currentThreadId(); + + QMutexLocker locker(signalSlotLock(sender)); + QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists; + if (!connectionLists) { + locker.unlock(); + if (qt_signal_spy_callback_set.signal_end_callback != 0) + qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index); + return; + } + ++connectionLists->inUse; + + + const QObjectPrivate::ConnectionList *list; + if (signal_index < connectionLists->count()) + list = &connectionLists->at(signal_index); + else + list = &connectionLists->allsignals; + + do { + QObjectPrivate::Connection *c = list->first; + if (!c) continue; + // We need to check against last here to ensure that signals added + // during the signal emission are not emitted in this emission. + QObjectPrivate::Connection *last = list->last; + + do { + if (!c->receiver) + continue; + + QObject * const receiver = c->receiver; + const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId; + + // determine if this connection should be sent immediately or + // put into the event queue + if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread) + || (c->connectionType == Qt::QueuedConnection)) { + queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv); + continue; +#ifndef QT_NO_THREAD + } else if (c->connectionType == Qt::BlockingQueuedConnection) { + locker.unlock(); + if (receiverInSameThread) { + qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: " + "Sender is %s(%p), receiver is %s(%p)", + sender->metaObject()->className(), sender, + receiver->metaObject()->className(), receiver); + } + QSemaphore semaphore; + QCoreApplication::postEvent(receiver, new QMetaCallEvent(c->method_offset, c->method_relative, + c->callFunction, + sender, signal_absolute_index, + 0, 0, + argv ? argv : empty_argv, + &semaphore)); + semaphore.acquire(); + locker.relock(); + continue; +#endif + } + + QObjectPrivate::Sender currentSender; + QObjectPrivate::Sender *previousSender = 0; + if (receiverInSameThread) { + currentSender.sender = sender; + currentSender.signal = signal_absolute_index; + currentSender.ref = 1; + previousSender = QObjectPrivate::setCurrentSender(receiver, ¤tSender); + } + const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction; + const int method_relative = c->method_relative; + if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) { + //we compare the vtable to make sure we are not in the destructor of the object. + locker.unlock(); + if (qt_signal_spy_callback_set.slot_begin_callback != 0) + qt_signal_spy_callback_set.slot_begin_callback(receiver, c->method(), argv ? argv : empty_argv); + + callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv); + + if (qt_signal_spy_callback_set.slot_end_callback != 0) + qt_signal_spy_callback_set.slot_end_callback(receiver, c->method()); + locker.relock(); + } else { + const int method = method_relative + c->method_offset; + locker.unlock(); + + if (qt_signal_spy_callback_set.slot_begin_callback != 0) { + qt_signal_spy_callback_set.slot_begin_callback(receiver, + method, + argv ? argv : empty_argv); + } + +#if defined(QT_NO_EXCEPTIONS) + metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); +#else + QT_TRY { + metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); + } QT_CATCH(...) { + locker.relock(); + if (receiverInSameThread) + QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); + + --connectionLists->inUse; + Q_ASSERT(connectionLists->inUse >= 0); + if (connectionLists->orphaned && !connectionLists->inUse) + delete connectionLists; + QT_RETHROW; + } +#endif + + if (qt_signal_spy_callback_set.slot_end_callback != 0) + qt_signal_spy_callback_set.slot_end_callback(receiver, method); + + locker.relock(); + } + + if (receiverInSameThread) + QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); + + if (connectionLists->orphaned) + break; + } while (c != last && (c = c->nextConnectionList) != 0); + + if (connectionLists->orphaned) + break; + } while (list != &connectionLists->allsignals && + //start over for all signals; + ((list = &connectionLists->allsignals), true)); + + --connectionLists->inUse; + Q_ASSERT(connectionLists->inUse >= 0); + if (connectionLists->orphaned) { + if (!connectionLists->inUse) + delete connectionLists; + } else if (connectionLists->dirty) { + sender->d_func()->cleanConnectionLists(); + } + + locker.unlock(); + + if (qt_signal_spy_callback_set.signal_end_callback != 0) + qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index); + +} + +/*!\internal + Obsolete. (signal_index comes from indexOfMethod()) +*/ +void QMetaObject::activate(QObject *sender, int signal_index, void **argv) +{ + const QMetaObject *mo = sender->metaObject(); + while (mo->methodOffset() > signal_index) + mo = mo->superClass(); + activate(sender, mo, signal_index - mo->methodOffset(), argv); +} + +/*!\internal + Obsolete, called by moc generated code before Qt 4.6 for cloned signals + But since Qt 4.6, all clones are connected to their original + */ +void QMetaObject::activate(QObject *sender, const QMetaObject *m, + int from_local_signal_index, int to_local_signal_index, void **argv) +{ + Q_UNUSED(to_local_signal_index); + Q_ASSERT(from_local_signal_index == QMetaObjectPrivate::originalClone(m, to_local_signal_index)); + activate(sender, m, from_local_signal_index, argv); +} + +/*! \internal + Returns the signal index used in the internal connectionLists vector. + + It is different from QMetaObject::indexOfSignal(): indexOfSignal is the same as indexOfMethod + while QObjectPrivate::signalIndex is smaller because it doesn't give index to slots. +*/ +int QObjectPrivate::signalIndex(const char *signalName) const +{ + Q_Q(const QObject); + const QMetaObject *base = q->metaObject(); + 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); + int signalOffset, methodOffset; + computeOffsets(base, &signalOffset, &methodOffset); + return relative_index + signalOffset; +} + +/***************************************************************************** + Properties + *****************************************************************************/ + +#ifndef QT_NO_PROPERTIES + +/*! + Sets the value of the object's \a name property to \a value. + + If the property is defined in the class using Q_PROPERTY then + true is returned on success and false otherwise. If the property + is not defined using Q_PROPERTY, and therefore not listed in the + meta-object, it is added as a dynamic property and false is returned. + + Information about all available properties is provided through the + metaObject() and dynamicPropertyNames(). + + Dynamic properties can be queried again using property() and can be + removed by setting the property value to an invalid QVariant. + Changing the value of a dynamic property causes a QDynamicPropertyChangeEvent + to be sent to the object. + + \bold{Note:} Dynamic properties starting with "_q_" are reserved for internal + purposes. + + \sa property(), metaObject(), dynamicPropertyNames() +*/ +bool QObject::setProperty(const char *name, const QVariant &value) +{ + Q_D(QObject); + const QMetaObject* meta = metaObject(); + if (!name || !meta) + return false; + + int id = meta->indexOfProperty(name); + if (id < 0) { + if (!d->extraData) + d->extraData = new QObjectPrivate::ExtraData; + + const int idx = d->extraData->propertyNames.indexOf(name); + + if (!value.isValid()) { + if (idx == -1) + return false; + d->extraData->propertyNames.removeAt(idx); + d->extraData->propertyValues.removeAt(idx); + } else { + if (idx == -1) { + d->extraData->propertyNames.append(name); + d->extraData->propertyValues.append(value); + } else { + d->extraData->propertyValues[idx] = value; + } + } + + QDynamicPropertyChangeEvent ev(name); + QCoreApplication::sendEvent(this, &ev); + + return false; + } + QMetaProperty p = meta->property(id); +#ifndef QT_NO_DEBUG + if (!p.isWritable()) + qWarning("%s::setProperty: Property \"%s\" invalid," + " read-only or does not exist", metaObject()->className(), name); +#endif + return p.write(this, value); +} + +/*! + Returns the value of the object's \a name property. + + If no such property exists, the returned variant is invalid. + + Information about all available properties is provided through the + metaObject() and dynamicPropertyNames(). + + \sa setProperty(), QVariant::isValid(), metaObject(), dynamicPropertyNames() +*/ +QVariant QObject::property(const char *name) const +{ + Q_D(const QObject); + const QMetaObject* meta = metaObject(); + if (!name || !meta) + return QVariant(); + + int id = meta->indexOfProperty(name); + if (id < 0) { + if (!d->extraData) + return QVariant(); + const int i = d->extraData->propertyNames.indexOf(name); + return d->extraData->propertyValues.value(i); + } + QMetaProperty p = meta->property(id); +#ifndef QT_NO_DEBUG + if (!p.isReadable()) + qWarning("%s::property: Property \"%s\" invalid or does not exist", + metaObject()->className(), name); +#endif + return p.read(this); +} + +/*! + \since 4.2 + + Returns the names of all properties that were dynamically added to + the object using setProperty(). +*/ +QList QObject::dynamicPropertyNames() const +{ + Q_D(const QObject); + if (d->extraData) + return d->extraData->propertyNames; + return QList(); +} + +#endif // QT_NO_PROPERTIES + + +/***************************************************************************** + QObject debugging output routines. + *****************************************************************************/ + +static void dumpRecursive(int level, QObject *object) +{ +#if defined(QT_DEBUG) + if (object) { + QByteArray buf; + buf.fill(' ', level / 2 * 8); + if (level % 2) + buf += " "; + QString name = object->objectName(); + QString flags = QLatin1String(""); +#if 0 + if (qApp->focusWidget() == object) + flags += 'F'; + if (object->isWidgetType()) { + QWidget * w = (QWidget *)object; + if (w->isVisible()) { + QString t("<%1,%2,%3,%4>"); + flags += t.arg(w->x()).arg(w->y()).arg(w->width()).arg(w->height()); + } else { + flags += 'I'; + } + } +#endif + qDebug("%s%s::%s %s", (const char*)buf, object->metaObject()->className(), name.toLocal8Bit().data(), + flags.toLatin1().data()); + QObjectList children = object->children(); + if (!children.isEmpty()) { + for (int i = 0; i < children.size(); ++i) + dumpRecursive(level+1, children.at(i)); + } + } +#else + Q_UNUSED(level) + Q_UNUSED(object) +#endif +} + +/*! + Dumps a tree of children to the debug output. + + This function is useful for debugging, but does nothing if the + library has been compiled in release mode (i.e. without debugging + information). + + \sa dumpObjectInfo() +*/ + +void QObject::dumpObjectTree() +{ + dumpRecursive(0, this); +} + +/*! + Dumps information about signal connections, etc. for this object + to the debug output. + + This function is useful for debugging, but does nothing if the + library has been compiled in release mode (i.e. without debugging + information). + + \sa dumpObjectTree() +*/ + +void QObject::dumpObjectInfo() +{ +#if defined(QT_DEBUG) + qDebug("OBJECT %s::%s", metaObject()->className(), + objectName().isEmpty() ? "unnamed" : objectName().toLocal8Bit().data()); + + Q_D(QObject); + QMutexLocker locker(signalSlotLock(this)); + + // first, look for connections where this object is the sender + qDebug(" SIGNALS OUT"); + + if (d->connectionLists) { + int offset = 0; + int offsetToNextMetaObject = 0; + for (int signal_index = 0; signal_index < d->connectionLists->count(); ++signal_index) { + if (signal_index >= offsetToNextMetaObject) { + const QMetaObject *mo = metaObject(); + int signalOffset, methodOffset; + computeOffsets(mo, &signalOffset, &methodOffset); + while (signalOffset > signal_index) { + mo = mo->superClass(); + offsetToNextMetaObject = signalOffset; + computeOffsets(mo, &signalOffset, &methodOffset); + } + offset = methodOffset - signalOffset; + } + const QMetaMethod signal = metaObject()->method(signal_index + offset); + qDebug(" signal: %s", signal.signature()); + + // receivers + const QObjectPrivate::Connection *c = + d->connectionLists->at(signal_index).first; + while (c) { + if (!c->receiver) { + qDebug(" "); + c = c->nextConnectionList; + continue; + } + const QMetaObject *receiverMetaObject = c->receiver->metaObject(); + const QMetaMethod method = receiverMetaObject->method(c->method()); + qDebug(" --> %s::%s %s", + receiverMetaObject->className(), + c->receiver->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver->objectName()), + method.signature()); + c = c->nextConnectionList; + } + } + } else { + qDebug( " " ); + } + + // now look for connections where this object is the receiver + qDebug(" SIGNALS IN"); + + if (d->senders) { + for (QObjectPrivate::Connection *s = d->senders; s; s = s->next) { + const QMetaMethod slot = metaObject()->method(s->method()); + qDebug(" <-- %s::%s %s", + s->sender->metaObject()->className(), + s->sender->objectName().isEmpty() ? "unnamed" : qPrintable(s->sender->objectName()), + slot.signature()); + } + } else { + qDebug(" "); + } +#endif +} + +#ifndef QT_NO_USERDATA +/*!\internal + */ +uint QObject::registerUserData() +{ + static int user_data_registration = 0; + return user_data_registration++; +} + +/*!\internal + */ +QObjectUserData::~QObjectUserData() +{ +} + +/*!\internal + */ +void QObject::setUserData(uint id, QObjectUserData* data) +{ + Q_D(QObject); + if (!d->extraData) + d->extraData = new QObjectPrivate::ExtraData; + + if (d->extraData->userData.size() <= (int) id) + d->extraData->userData.resize((int) id + 1); + d->extraData->userData[id] = data; +} + +/*!\internal + */ +QObjectUserData* QObject::userData(uint id) const +{ + Q_D(const QObject); + if (!d->extraData) + return 0; + if ((int)id < d->extraData->userData.size()) + return d->extraData->userData.at(id); + return 0; +} + +#endif // QT_NO_USERDATA + + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QObject *o) { +#ifndef Q_BROKEN_DEBUG_STREAM + if (!o) + return dbg << "QObject(0x0) "; + dbg.nospace() << o->metaObject()->className() << '(' << (void *)o; + if (!o->objectName().isEmpty()) + dbg << ", name = " << o->objectName(); + dbg << ')'; + return dbg.space(); +#else + qWarning("This compiler doesn't support streaming QObject to QDebug"); + return dbg; + Q_UNUSED(o); +#endif +} +#endif + +/*! + \fn void QObject::insertChild(QObject *object) + + Use setParent() instead, i.e., call object->setParent(this). +*/ + +/*! + \fn void QObject::removeChild(QObject *object) + + Use setParent() instead, i.e., call object->setParent(0). +*/ + +/*! + \fn bool QObject::isA(const char *className) const + + Compare \a className with the object's metaObject()->className() instead. +*/ + +/*! + \fn const char *QObject::className() const + + Use metaObject()->className() instead. +*/ + +/*! + \fn const char *QObject::name() const + + Use objectName() instead. +*/ + +/*! + \fn const char *QObject::name(const char *defaultName) const + + Use objectName() instead. +*/ + +/*! + \fn void QObject::setName(const char *name) + + Use setObjectName() instead. +*/ + +/*! + \fn bool QObject::checkConnectArgs(const char *signal, const + QObject *object, const char *method) + + Use QMetaObject::checkConnectArgs() instead. +*/ + +/*! + \fn QByteArray QObject::normalizeSignalSlot(const char *signalSlot) + + Use QMetaObject::normalizedSignature() instead. +*/ + +/*! + \fn const char *QMetaObject::superClassName() const + + \internal +*/ + +/*! + \macro Q_CLASSINFO(Name, Value) + \relates QObject + + This macro associates extra information to the class, which is + available using QObject::metaObject(). Except for the ActiveQt + extension, Qt doesn't use this information. + + The extra information takes the form of a \a Name string and a \a + Value literal string. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 35 + + \sa QMetaObject::classInfo() +*/ + +/*! + \macro Q_INTERFACES(...) + \relates QObject + + This macro tells Qt which interfaces the class implements. This + is used when implementing plugins. + + Example: + + \snippet examples/tools/plugandpaintplugins/basictools/basictoolsplugin.h 1 + \dots + \snippet examples/tools/plugandpaintplugins/basictools/basictoolsplugin.h 3 + + See the \l{tools/plugandpaintplugins/basictools}{Plug & Paint + Basic Tools} example for details. + + \sa Q_DECLARE_INTERFACE(), Q_EXPORT_PLUGIN2(), {How to Create Qt Plugins} +*/ + +/*! + \macro Q_PROPERTY(...) + \relates QObject + + This macro is used for declaring properties in classes that + inherit QObject. Properties behave like class data members, but + they have additional features accessible through the \l + {Meta-Object System}. + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 36 + + The property name and type and the \c READ function are required. + The type can be any type supported by QVariant, or it can be a + user-defined type. The other items are optional, but a \c WRITE + function is common. The attributes default to true except \c USER, + which defaults to false. + + For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 37 + + For more details about how to use this macro, and a more detailed + example of its use, see the discussion on \l {Qt's Property System}. + + \sa {Qt's Property System} +*/ + +/*! + \macro Q_ENUMS(...) + \relates QObject + + This macro registers one or several enum types to the meta-object + system. + + For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 38 + + If you want to register an enum that is declared in another class, + the enum must be fully qualified with the name of the class + defining it. In addition, the class \e defining the enum has to + inherit QObject as well as declare the enum using Q_ENUMS(). + + \sa {Qt's Property System} +*/ + +/*! + \macro Q_FLAGS(...) + \relates QObject + + This macro registers one or several \l{QFlags}{flags types} to the + meta-object system. It is typically used in a class definition to declare + that values of a given enum can be used as flags and combined using the + bitwise OR operator. + + For example, in QLibrary, the \l{QLibrary::LoadHints}{LoadHints} flag is + declared in the following way: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 39a + + The declaration of the flags themselves is performed in the public section + of the QLibrary class itself, using the \l Q_DECLARE_FLAGS() macro: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 39b + + \note This macro takes care of registering individual flag values + with the meta-object system, so it is unnecessary to use Q_ENUMS() + in addition to this macro. + + \sa {Qt's Property System} +*/ + +/*! + \macro Q_OBJECT + \relates QObject + + The Q_OBJECT macro must appear in the private section of a class + definition that declares its own signals and slots or that uses + other services provided by Qt's meta-object system. + + For example: + + \snippet doc/src/snippets/signalsandslots/signalsandslots.h 1 + \codeline + \snippet doc/src/snippets/signalsandslots/signalsandslots.h 2 + \snippet doc/src/snippets/signalsandslots/signalsandslots.h 3 + + \note This macro requires the class to be a subclass of QObject. Use + Q_GADGET instead of Q_OBJECT to enable the meta object system's support + for enums in a class that is not a QObject subclass. Q_GADGET makes a + class member, \c{staticMetaObject}, available. + \c{staticMetaObject} is of type QMetaObject and provides access to the + enums declared with Q_ENUMS. + Q_GADGET is provided only for C++. + + \sa {Meta-Object System}, {Signals and Slots}, {Qt's Property System} +*/ + +/*! + \macro Q_SIGNALS + \relates QObject + + Use this macro to replace the \c signals keyword in class + declarations, when you want to use Qt Signals and Slots with a + \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}. + + The macro is normally used when \c no_keywords is specified with + the \c CONFIG variable in the \c .pro file, but it can be used + even when \c no_keywords is \e not specified. +*/ + +/*! + \macro Q_SIGNAL + \relates QObject + + This is an additional macro that allows you to mark a single + function as a signal. It can be quite useful, especially when you + use a 3rd-party source code parser which doesn't understand a \c + signals or \c Q_SIGNALS groups. + + Use this macro to replace the \c signals keyword in class + declarations, when you want to use Qt Signals and Slots with a + \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}. + + The macro is normally used when \c no_keywords is specified with + the \c CONFIG variable in the \c .pro file, but it can be used + even when \c no_keywords is \e not specified. +*/ + +/*! + \macro Q_SLOTS + \relates QObject + + Use this macro to replace the \c slots keyword in class + declarations, when you want to use Qt Signals and Slots with a + \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}. + + The macro is normally used when \c no_keywords is specified with + the \c CONFIG variable in the \c .pro file, but it can be used + even when \c no_keywords is \e not specified. +*/ + +/*! + \macro Q_SLOT + \relates QObject + + This is an additional macro that allows you to mark a single + function as a slot. It can be quite useful, especially when you + use a 3rd-party source code parser which doesn't understand a \c + slots or \c Q_SLOTS groups. + + Use this macro to replace the \c slots keyword in class + declarations, when you want to use Qt Signals and Slots with a + \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}. + + The macro is normally used when \c no_keywords is specified with + the \c CONFIG variable in the \c .pro file, but it can be used + even when \c no_keywords is \e not specified. +*/ + +/*! + \macro Q_EMIT + \relates QObject + + Use this macro to replace the \c emit keyword for emitting + signals, when you want to use Qt Signals and Slots with a + \l{3rd Party Signals and Slots} {3rd party signal/slot mechanism}. + + The macro is normally used when \c no_keywords is specified with + the \c CONFIG variable in the \c .pro file, but it can be used + even when \c no_keywords is \e not specified. +*/ + +/*! + \macro Q_INVOKABLE + \relates QObject + + Apply this macro to definitions of member functions to allow them to + be invoked via the meta-object system. The macro is written before + the return type, as shown in the following example: + + \snippet snippets/qmetaobject-invokable/window.h Window class with invokable method + + The \c invokableMethod() function is marked up using Q_INVOKABLE, causing + it to be registered with the meta-object system and enabling it to be + invoked using QMetaObject::invokeMethod(). + Since \c normalMethod() function is not registered in this way, it cannot + be invoked using QMetaObject::invokeMethod(). +*/ + +/*! + \typedef QObjectList + \relates QObject + + Synonym for QList. +*/ + +void qDeleteInEventHandler(QObject *o) +{ +#ifdef QT_JAMBI_BUILD + if (!o) + return; + QObjectPrivate::get(o)->inEventHandler = false; +#endif + delete o; +} + + +QT_END_NAMESPACE + +#include "moc_qobject.cpp" diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h new file mode 100644 index 0000000000..0ad73f5fe9 --- /dev/null +++ b/src/corelib/kernel/qobject.h @@ -0,0 +1,411 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QOBJECT_H +#define QOBJECT_H + +#ifndef QT_NO_QOBJECT + +#include +#include +#include +#include +#ifdef QT_INCLUDE_COMPAT +#include +#endif +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QEvent; +class QTimerEvent; +class QChildEvent; +struct QMetaObject; +class QVariant; +class QObjectPrivate; +class QObject; +class QThread; +class QWidget; +#ifndef QT_NO_REGEXP +class QRegExp; +#endif +#ifndef QT_NO_USERDATA +class QObjectUserData; +#endif + +typedef QList QObjectList; + +Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, const QString &name, const QRegExp *re, + const QMetaObject &mo, QList *list); +Q_CORE_EXPORT QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo); + +class +#if defined(__INTEL_COMPILER) && defined(Q_OS_WIN) +Q_CORE_EXPORT +#endif +QObjectData { +public: + virtual ~QObjectData() = 0; + QObject *q_ptr; + QObject *parent; + QObjectList children; + + uint isWidget : 1; + uint pendTimer : 1; + uint blockSig : 1; + uint wasDeleted : 1; + uint ownObjectName : 1; + uint sendChildEvents : 1; + uint receiveChildEvents : 1; + uint inEventHandler : 1; //only used if QT_JAMBI_BUILD + uint inThreadChangeEvent : 1; + uint hasGuards : 1; //true iff there is one or more QPointer attached to this object + uint unused : 22; + int postedEvents; + QMetaObject *metaObject; // assert dynamic +}; + + +class Q_CORE_EXPORT QObject +{ + Q_OBJECT + Q_PROPERTY(QString objectName READ objectName WRITE setObjectName) + Q_DECLARE_PRIVATE(QObject) + +public: + Q_INVOKABLE explicit QObject(QObject *parent=0); + virtual ~QObject(); + + virtual bool event(QEvent *); + virtual bool eventFilter(QObject *, QEvent *); + +#ifdef qdoc + static QString tr(const char *sourceText, const char *comment = 0, int n = -1); + static QString trUtf8(const char *sourceText, const char *comment = 0, int n = -1); + virtual const QMetaObject *metaObject() const; + static const QMetaObject staticMetaObject; +#endif +#ifdef QT_NO_TRANSLATION + static QString tr(const char *sourceText, const char *, int) + { return QString::fromLatin1(sourceText); } + static QString tr(const char *sourceText, const char * = 0) + { return QString::fromLatin1(sourceText); } +#ifndef QT_NO_TEXTCODEC + static QString trUtf8(const char *sourceText, const char *, int) + { return QString::fromUtf8(sourceText); } + static QString trUtf8(const char *sourceText, const char * = 0) + { return QString::fromUtf8(sourceText); } +#endif +#endif //QT_NO_TRANSLATION + + QString objectName() const; + void setObjectName(const QString &name); + + inline bool isWidgetType() const { return d_ptr->isWidget; } + + inline bool signalsBlocked() const { return d_ptr->blockSig; } + bool blockSignals(bool b); + + QThread *thread() const; + void moveToThread(QThread *thread); + + int startTimer(int interval); + void killTimer(int id); + + template + inline T findChild(const QString &aName = QString()) const + { return static_cast(qt_qFindChild_helper(this, aName, reinterpret_cast(0)->staticMetaObject)); } + + template + inline QList findChildren(const QString &aName = QString()) const + { + QList list; + union { + QList *typedList; + QList *voidList; + } u; + u.typedList = &list; + qt_qFindChildren_helper(this, aName, 0, reinterpret_cast(0)->staticMetaObject, u.voidList); + return list; + } + +#ifndef QT_NO_REGEXP + template + inline QList findChildren(const QRegExp &re) const + { + QList list; + union { + QList *typedList; + QList *voidList; + } u; + u.typedList = &list; + qt_qFindChildren_helper(this, QString(), &re, reinterpret_cast(0)->staticMetaObject, u.voidList); + return list; + } +#endif + +#ifdef QT3_SUPPORT + QT3_SUPPORT QObject *child(const char *objName, const char *inheritsClass = 0, + bool recursiveSearch = true) const; + QT3_SUPPORT QObjectList queryList(const char *inheritsClass = 0, + const char *objName = 0, + bool regexpMatch = true, + bool recursiveSearch = true) const; +#endif + inline const QObjectList &children() const { return d_ptr->children; } + + void setParent(QObject *); + void installEventFilter(QObject *); + void removeEventFilter(QObject *); + + + static bool connect(const QObject *sender, const char *signal, + const QObject *receiver, const char *member, Qt::ConnectionType = +#ifdef qdoc + Qt::AutoConnection +#else +#ifdef QT3_SUPPORT + Qt::AutoCompatConnection +#else + Qt::AutoConnection +#endif +#endif + ); + + static bool connect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &method, + Qt::ConnectionType type = +#ifdef qdoc + Qt::AutoConnection +#else +#ifdef QT3_SUPPORT + Qt::AutoCompatConnection +#else + Qt::AutoConnection +#endif +#endif + ); + + inline bool connect(const QObject *sender, const char *signal, + const char *member, Qt::ConnectionType type = +#ifdef qdoc + Qt::AutoConnection +#else +#ifdef QT3_SUPPORT + Qt::AutoCompatConnection +#else + Qt::AutoConnection +#endif +#endif + ) const; + + static bool disconnect(const QObject *sender, const char *signal, + const QObject *receiver, const char *member); + static bool disconnect(const QObject *sender, const QMetaMethod &signal, + const QObject *receiver, const QMetaMethod &member); + inline bool disconnect(const char *signal = 0, + const QObject *receiver = 0, const char *member = 0) + { return disconnect(this, signal, receiver, member); } + inline bool disconnect(const QObject *receiver, const char *member = 0) + { return disconnect(this, 0, receiver, member); } + + void dumpObjectTree(); + void dumpObjectInfo(); + +#ifndef QT_NO_PROPERTIES + bool setProperty(const char *name, const QVariant &value); + QVariant property(const char *name) const; + QList dynamicPropertyNames() const; +#endif // QT_NO_PROPERTIES + +#ifndef QT_NO_USERDATA + static uint registerUserData(); + void setUserData(uint id, QObjectUserData* data); + QObjectUserData* userData(uint id) const; +#endif // QT_NO_USERDATA + +Q_SIGNALS: + void destroyed(QObject * = 0); + +public: + inline QObject *parent() const { return d_ptr->parent; } + + inline bool inherits(const char *classname) const + { return const_cast(this)->qt_metacast(classname) != 0; } + +public Q_SLOTS: + void deleteLater(); + +protected: + QObject *sender() const; + int senderSignalIndex() const; + int receivers(const char* signal) const; + + virtual void timerEvent(QTimerEvent *); + virtual void childEvent(QChildEvent *); + virtual void customEvent(QEvent *); + + virtual void connectNotify(const char *signal); + virtual void disconnectNotify(const char *signal); + +#ifdef QT3_SUPPORT +public: + QT3_SUPPORT_CONSTRUCTOR QObject(QObject *parent, const char *name); + inline QT3_SUPPORT void insertChild(QObject *o) + { if (o) o->setParent(this); } + inline QT3_SUPPORT void removeChild(QObject *o) + { if (o) o->setParent(0); } + inline QT3_SUPPORT bool isA(const char *classname) const + { return qstrcmp(classname, metaObject()->className()) == 0; } + inline QT3_SUPPORT const char *className() const { return metaObject()->className(); } + inline QT3_SUPPORT const char *name() const { return objectName().latin1_helper(); } + inline QT3_SUPPORT const char *name(const char *defaultName) const + { QString s = objectName(); return s.isEmpty()?defaultName:s.latin1_helper(); } + inline QT3_SUPPORT void setName(const char *aName) { setObjectName(QLatin1String(aName)); } +protected: + inline QT3_SUPPORT bool checkConnectArgs(const char *signal, + const QObject *, + const char *member) + { return QMetaObject::checkConnectArgs(signal, member); } + static inline QT3_SUPPORT QByteArray normalizeSignalSlot(const char *signalSlot) + { return QMetaObject::normalizedSignature(signalSlot); } +#endif + +protected: + QObject(QObjectPrivate &dd, QObject *parent = 0); + +protected: + QScopedPointer d_ptr; + + static const QMetaObject staticQtMetaObject; + + friend struct QMetaObject; + friend class QApplication; + friend class QApplicationPrivate; + friend class QCoreApplication; + friend class QCoreApplicationPrivate; + friend class QWidget; + friend class QThreadData; + +private: + Q_DISABLE_COPY(QObject) + Q_PRIVATE_SLOT(d_func(), void _q_reregisterTimers(void *)) +}; + +inline bool QObject::connect(const QObject *asender, const char *asignal, + const char *amember, Qt::ConnectionType atype) const +{ return connect(asender, asignal, this, amember, atype); } + +#ifndef QT_NO_USERDATA +class Q_CORE_EXPORT QObjectUserData { +public: + virtual ~QObjectUserData(); +}; +#endif + +#ifdef QT_DEPRECATED +template +inline QT_DEPRECATED T qFindChild(const QObject *o, const QString &name = QString()) +{ return o->findChild(name); } + +template +inline QT_DEPRECATED QList qFindChildren(const QObject *o, const QString &name = QString()) +{ + return o->findChildren(name); +} + +#ifndef QT_NO_REGEXP +template +inline QT_DEPRECATED QList qFindChildren(const QObject *o, const QRegExp &re) +{ + return o->findChildren(re); +} +#endif + +#endif //QT_DEPRECATED + +template +inline T qobject_cast(QObject *object) +{ +#if !defined(QT_NO_QOBJECT_CHECK) + reinterpret_cast(object)->qt_check_for_QOBJECT_macro(*reinterpret_cast(object)); +#endif + return static_cast(reinterpret_cast(object)->staticMetaObject.cast(object)); +} + +template +inline T qobject_cast(const QObject *object) +{ +#if !defined(QT_NO_QOBJECT_CHECK) + reinterpret_cast(object)->qt_check_for_QOBJECT_macro(*reinterpret_cast(const_cast(object))); +#endif + return static_cast(reinterpret_cast(object)->staticMetaObject.cast(object)); +} + + +template inline const char * qobject_interface_iid() +{ return 0; } + +#ifndef Q_MOC_RUN +# define Q_DECLARE_INTERFACE(IFace, IId) \ + template <> inline const char *qobject_interface_iid() \ + { return IId; } \ + template <> inline IFace *qobject_cast(QObject *object) \ + { return reinterpret_cast((object ? object->qt_metacast(IId) : 0)); } \ + template <> inline IFace *qobject_cast(const QObject *object) \ + { return reinterpret_cast((object ? const_cast(object)->qt_metacast(IId) : 0)); } +#endif // Q_MOC_RUN + +#ifndef QT_NO_DEBUG_STREAM +Q_CORE_EXPORT QDebug operator<<(QDebug, const QObject *); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + +#endif // QOBJECT_H diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h new file mode 100644 index 0000000000..2711a4b5f5 --- /dev/null +++ b/src/corelib/kernel/qobject_p.h @@ -0,0 +1,305 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QOBJECT_P_H +#define QOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qobject.h" +#include "QtCore/qpointer.h" +#include "QtCore/qsharedpointer.h" +#include "QtCore/qcoreevent.h" +#include "QtCore/qlist.h" +#include "QtCore/qvector.h" +#include "QtCore/qreadwritelock.h" +#include "QtCore/qvariant.h" + +QT_BEGIN_NAMESPACE + +class QVariant; +class QThreadData; +class QObjectConnectionListVector; +namespace QtSharedPointer { struct ExternalRefCountData; } + +/* mirrored in QtTestLib, DON'T CHANGE without prior warning */ +struct QSignalSpyCallbackSet +{ + typedef void (*BeginCallback)(QObject *caller, int method_index, void **argv); + typedef void (*EndCallback)(QObject *caller, int method_index); + BeginCallback signal_begin_callback, + slot_begin_callback; + EndCallback signal_end_callback, + slot_end_callback; +}; +void Q_CORE_EXPORT qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet &callback_set); + +extern QSignalSpyCallbackSet Q_CORE_EXPORT qt_signal_spy_callback_set; + +enum { QObjectPrivateVersion = QT_VERSION }; + +class Q_CORE_EXPORT QAbstractDeclarativeData +{ +public: + static void (*destroyed)(QAbstractDeclarativeData *, QObject *); + static void (*parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *); + static void (*objectNameChanged)(QAbstractDeclarativeData *, QObject *); +}; + +class Q_CORE_EXPORT QObjectPrivate : public QObjectData +{ + Q_DECLARE_PUBLIC(QObject) + +public: + struct ExtraData + { + ExtraData() {} +#ifndef QT_NO_USERDATA + QVector userData; +#endif + QList propertyNames; + QList propertyValues; + }; + + typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **); + struct Connection + { + QObject *sender; + QObject *receiver; + StaticMetaCallFunction callFunction; + // The next pointer for the singly-linked ConnectionList + Connection *nextConnectionList; + //senders linked list + Connection *next; + Connection **prev; + QBasicAtomicPointer argumentTypes; + ushort method_offset; + ushort method_relative; + ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking + ~Connection(); + int method() const { return method_offset + method_relative; } + }; + // ConnectionList is a singly-linked list + struct ConnectionList { + ConnectionList() : first(0), last(0) {} + Connection *first; + Connection *last; + }; + + struct Sender + { + QObject *sender; + int signal; + int ref; + }; + + + QObjectPrivate(int version = QObjectPrivateVersion); + virtual ~QObjectPrivate(); + void deleteChildren(); + + void setParent_helper(QObject *); + void moveToThread_helper(); + void setThreadData_helper(QThreadData *currentData, QThreadData *targetData); + void _q_reregisterTimers(void *pointer); + + bool isSender(const QObject *receiver, const char *signal) const; + QObjectList receiverList(const char *signal) const; + QObjectList senderList() const; + + void addConnection(int signal, Connection *c); + void cleanConnectionLists(); + +#ifdef QT3_SUPPORT + void sendPendingChildInsertedEvents(); +#endif + + static inline Sender *setCurrentSender(QObject *receiver, + Sender *sender); + static inline void resetCurrentSender(QObject *receiver, + Sender *currentSender, + Sender *previousSender); +#ifdef QT_JAMBI_BUILD + static int *setDeleteWatch(QObjectPrivate *d, int *newWatch); + static void resetDeleteWatch(QObjectPrivate *d, int *oldWatch, int deleteWatch); +#endif + static void clearGuards(QObject *); + + static QObjectPrivate *get(QObject *o) { + return o->d_func(); + } + + int signalIndex(const char *signalName) const; + inline bool isSignalConnected(uint signalIdx) const; + +public: + QString objectName; + ExtraData *extraData; // extra data set by the user + QThreadData *threadData; // id of the thread that owns the object + + QObjectConnectionListVector *connectionLists; + + Connection *senders; // linked list of connections connected to this object + Sender *currentSender; // object currently activating the object + mutable quint32 connectedSignals[2]; + +#ifdef QT3_SUPPORT + QVector< QWeakPointer > pendingChildInsertedEvents; +#else + // preserve binary compatibility with code compiled without Qt 3 support + // keeping the binary layout stable helps the Qt Creator debugger + void *unused; +#endif + + QList > eventFilters; + union { + QObject *currentChildBeingDeleted; + QAbstractDeclarativeData *declarativeData; //extra data used by the declarative module + }; + + // these objects are all used to indicate that a QObject was deleted + // plus QPointer, which keeps a separate list + QAtomicPointer sharedRefcount; +#ifdef QT_JAMBI_BUILD + int *deleteWatch; +#endif +}; + + +/*! \internal + + Returns true if the signal with index \a signal_index from object \a sender is connected. + Signals with indices above a certain range are always considered connected (see connectedSignals + in QObjectPrivate). If a signal spy is installed, all signals are considered connected. + + \a signal_index must be the index returned by QObjectPrivate::signalIndex; +*/ +inline bool QObjectPrivate::isSignalConnected(uint signal_index) const +{ + return signal_index >= sizeof(connectedSignals) * 8 + || (connectedSignals[signal_index >> 5] & (1 << (signal_index & 0x1f)) + || qt_signal_spy_callback_set.signal_begin_callback + || qt_signal_spy_callback_set.signal_end_callback); +} + +inline QObjectPrivate::Sender *QObjectPrivate::setCurrentSender(QObject *receiver, + Sender *sender) +{ + Sender *previousSender = receiver->d_func()->currentSender; + receiver->d_func()->currentSender = sender; + return previousSender; +} + +inline void QObjectPrivate::resetCurrentSender(QObject *receiver, + Sender *currentSender, + Sender *previousSender) +{ + // ref is set to zero when this object is deleted during the metacall + if (currentSender->ref == 1) + receiver->d_func()->currentSender = previousSender; + // if we've recursed, we need to tell the caller about the objects deletion + if (previousSender) + previousSender->ref = currentSender->ref; +} + + +Q_DECLARE_TYPEINFO(QObjectPrivate::Connection, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QObjectPrivate::Sender, Q_MOVABLE_TYPE); + +class QSemaphore; +class Q_CORE_EXPORT QMetaCallEvent : public QEvent +{ +public: + QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction , const QObject *sender, int signalId, + int nargs = 0, int *types = 0, void **args = 0, QSemaphore *semaphore = 0); + ~QMetaCallEvent(); + + inline int id() const { return method_offset_ + method_relative_; } + inline const QObject *sender() const { return sender_; } + inline int signalId() const { return signalId_; } + inline void **args() const { return args_; } + + virtual void placeMetaCall(QObject *object); + +private: + const QObject *sender_; + int signalId_; + int nargs_; + int *types_; + void **args_; + QSemaphore *semaphore_; + QObjectPrivate::StaticMetaCallFunction callFunction_; + ushort method_offset_; + ushort method_relative_; +}; + +class QBoolBlocker +{ +public: + inline QBoolBlocker(bool &b, bool value=true):block(b), reset(b){block = value;} + inline ~QBoolBlocker(){block = reset; } +private: + bool █ + bool reset; +}; + +void Q_CORE_EXPORT qDeleteInEventHandler(QObject *o); + + +struct Q_CORE_EXPORT QAbstractDynamicMetaObject : public QMetaObject +{ + virtual ~QAbstractDynamicMetaObject() {} + virtual int metaCall(QMetaObject::Call, int _id, void **) { return _id; } + virtual int createProperty(const char *, const char *) { return -1; } +}; + +QT_END_NAMESPACE + +#endif // QOBJECT_P_H diff --git a/src/corelib/kernel/qobjectcleanuphandler.cpp b/src/corelib/kernel/qobjectcleanuphandler.cpp new file mode 100644 index 0000000000..2355537a2a --- /dev/null +++ b/src/corelib/kernel/qobjectcleanuphandler.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qobjectcleanuphandler.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QObjectCleanupHandler + \brief The QObjectCleanupHandler class watches the lifetime of multiple QObjects. + + \ingroup objectmodel + + A QObjectCleanupHandler is useful whenever you need to know when a + number of \l{QObject}s that are owned by someone else have been + deleted. This is important, for example, when referencing memory + in an application that has been allocated in a shared library. + + To keep track of some \l{QObject}s, create a + QObjectCleanupHandler, and add() the objects you are interested + in. If you are no longer interested in tracking a particular + object, use remove() to remove it from the cleanup handler. If an + object being tracked by the cleanup handler gets deleted by + someone else it will automatically be removed from the cleanup + handler. You can delete all the objects in the cleanup handler + with clear(), or by destroying the cleanup handler. isEmpty() + returns true if the QObjectCleanupHandler has no objects to keep + track of. + + \sa QPointer +*/ + +/*! + Constructs an empty QObjectCleanupHandler. +*/ +QObjectCleanupHandler::QObjectCleanupHandler() +{ +} + +/*! + Destroys the cleanup handler. All objects in this cleanup handler + will be deleted. + + \sa clear() +*/ +QObjectCleanupHandler::~QObjectCleanupHandler() +{ + clear(); +} + +/*! + Adds \a object to this cleanup handler and returns the pointer to + the object. + + \sa remove() +*/ +QObject *QObjectCleanupHandler::add(QObject* object) +{ + if (!object) + return 0; + + connect(object, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*))); + cleanupObjects.insert(0, object); + return object; +} + +/*! + Removes the \a object from this cleanup handler. The object will + not be destroyed. + + \sa add() +*/ +void QObjectCleanupHandler::remove(QObject *object) +{ + int index; + if ((index = cleanupObjects.indexOf(object)) != -1) { + cleanupObjects.removeAt(index); + disconnect(object, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*))); + } +} + +/*! + Returns true if this cleanup handler is empty or if all objects in + this cleanup handler have been destroyed; otherwise return false. + + \sa add() remove() clear() +*/ +bool QObjectCleanupHandler::isEmpty() const +{ + return cleanupObjects.isEmpty(); +} + +/*! + Deletes all objects in this cleanup handler. The cleanup handler + becomes empty. + + \sa isEmpty() +*/ +void QObjectCleanupHandler::clear() +{ + while (!cleanupObjects.isEmpty()) + delete cleanupObjects.takeFirst(); +} + +void QObjectCleanupHandler::objectDestroyed(QObject *object) +{ + remove(object); +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qobjectcleanuphandler.h b/src/corelib/kernel/qobjectcleanuphandler.h new file mode 100644 index 0000000000..5004224e38 --- /dev/null +++ b/src/corelib/kernel/qobjectcleanuphandler.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QOBJECTCLEANUPHANDLER_H +#define QOBJECTCLEANUPHANDLER_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class Q_CORE_EXPORT QObjectCleanupHandler : public QObject +{ + Q_OBJECT + +public: + QObjectCleanupHandler(); + ~QObjectCleanupHandler(); + + QObject* add(QObject* object); + void remove(QObject *object); + bool isEmpty() const; + void clear(); + +private: + // ### move into d pointer + QObjectList cleanupObjects; + +private Q_SLOTS: + void objectDestroyed(QObject *); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QOBJECTCLEANUPHANDLER_H diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h new file mode 100644 index 0000000000..4384837f63 --- /dev/null +++ b/src/corelib/kernel/qobjectdefs.h @@ -0,0 +1,506 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QOBJECTDEFS_H +#define QOBJECTDEFS_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QByteArray; + +class QString; + +#ifndef Q_MOC_OUTPUT_REVISION +#define Q_MOC_OUTPUT_REVISION 63 +#endif + +// The following macros are our "extensions" to C++ +// They are used, strictly speaking, only by the moc. + +#ifndef Q_MOC_RUN +# if defined(QT_NO_KEYWORDS) +# define QT_NO_EMIT +# else +# define slots +# define signals protected +# endif +# define Q_SLOTS +# define Q_SIGNALS protected +# define Q_PRIVATE_SLOT(d, signature) +# define Q_EMIT +#ifndef QT_NO_EMIT +# define emit +#endif +#define Q_CLASSINFO(name, value) +#define Q_INTERFACES(x) +#define Q_PROPERTY(text) +#define Q_PRIVATE_PROPERTY(d, text) +#define Q_REVISION(v) +#define Q_OVERRIDE(text) +#define Q_ENUMS(x) +#define Q_FLAGS(x) +#ifdef QT3_SUPPORT +# define Q_SETS(x) +#endif +#define Q_SCRIPTABLE +#define Q_INVOKABLE +#define Q_SIGNAL +#define Q_SLOT + +#ifndef QT_NO_TRANSLATION +# ifndef QT_NO_TEXTCODEC +// full set of tr functions +// ### Qt 5: merge overloads +# define QT_TR_FUNCTIONS \ + static inline QString tr(const char *s, const char *c = 0) \ + { return staticMetaObject.tr(s, c); } \ + static inline QString trUtf8(const char *s, const char *c = 0) \ + { return staticMetaObject.trUtf8(s, c); } \ + static inline QString tr(const char *s, const char *c, int n) \ + { return staticMetaObject.tr(s, c, n); } \ + static inline QString trUtf8(const char *s, const char *c, int n) \ + { return staticMetaObject.trUtf8(s, c, n); } +# else +// no QTextCodec, no utf8 +// ### Qt 5: merge overloads +# define QT_TR_FUNCTIONS \ + static inline QString tr(const char *s, const char *c = 0) \ + { return staticMetaObject.tr(s, c); } \ + static inline QString tr(const char *s, const char *c, int n) \ + { return staticMetaObject.tr(s, c, n); } +# endif +#else +// inherit the ones from QObject +# define QT_TR_FUNCTIONS +#endif + +#if defined(QT_NO_QOBJECT_CHECK) +/* tmake ignore Q_OBJECT */ +#define Q_OBJECT_CHECK +#else + +/* This is a compile time check that ensures that any class cast with qobject_cast + actually contains a Q_OBJECT macro. Note: qobject_cast will fail if a QObject + subclass doesn't contain Q_OBJECT. + + In qt_check_for_QOBJECT_macro, we call a dummy templated function with two + parameters, the first being "this" and the other the target of the qobject + cast. If the types are not identical, we know that a Q_OBJECT macro is missing. + + If you get a compiler error here, make sure that the class you are casting + to contains a Q_OBJECT macro. +*/ + +/* tmake ignore Q_OBJECT */ +#define Q_OBJECT_CHECK \ + template inline void qt_check_for_QOBJECT_macro(const T &_q_argument) const \ + { int i = qYouForgotTheQ_OBJECT_Macro(this, &_q_argument); i = i; } + +template +inline int qYouForgotTheQ_OBJECT_Macro(T, T) { return 0; } + +template +inline void qYouForgotTheQ_OBJECT_Macro(T1, T2) {} +#endif // QT_NO_QOBJECT_CHECK + +#ifdef Q_NO_DATA_RELOCATION +#define Q_OBJECT_GETSTATICMETAOBJECT static const QMetaObject &getStaticMetaObject(); +#else +#define Q_OBJECT_GETSTATICMETAOBJECT +#endif + +/* tmake ignore Q_OBJECT */ +#define Q_OBJECT \ +public: \ + Q_OBJECT_CHECK \ + static const QMetaObject staticMetaObject; \ + Q_OBJECT_GETSTATICMETAOBJECT \ + virtual const QMetaObject *metaObject() const; \ + virtual void *qt_metacast(const char *); \ + QT_TR_FUNCTIONS \ + virtual int qt_metacall(QMetaObject::Call, int, void **); \ +private: \ + Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \ + Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); + +/* tmake ignore Q_OBJECT */ +#define Q_OBJECT_FAKE Q_OBJECT +/* tmake ignore Q_GADGET */ +#define Q_GADGET \ +public: \ + static const QMetaObject staticMetaObject; \ + Q_OBJECT_GETSTATICMETAOBJECT \ +private: +#else // Q_MOC_RUN +#define slots slots +#define signals signals +#define Q_SLOTS Q_SLOTS +#define Q_SIGNALS Q_SIGNALS +#define Q_CLASSINFO(name, value) Q_CLASSINFO(name, value) +#define Q_INTERFACES(x) Q_INTERFACES(x) +#define Q_PROPERTY(text) Q_PROPERTY(text) +#define Q_PRIVATE_PROPERTY(d, text) Q_PRIVATE_PROPERTY(d, text) +#define Q_REVISION(v) Q_REVISION(v) +#define Q_OVERRIDE(text) Q_OVERRIDE(text) +#define Q_ENUMS(x) Q_ENUMS(x) +#define Q_FLAGS(x) Q_FLAGS(x) +#ifdef QT3_SUPPORT +# define Q_SETS(x) Q_SETS(x) +#endif + /* tmake ignore Q_OBJECT */ +#define Q_OBJECT Q_OBJECT + /* tmake ignore Q_OBJECT */ +#define Q_OBJECT_FAKE Q_OBJECT_FAKE + /* tmake ignore Q_GADGET */ +#define Q_GADGET Q_GADGET +#define Q_SCRIPTABLE Q_SCRIPTABLE +#define Q_INVOKABLE Q_INVOKABLE +#define Q_SIGNAL Q_SIGNAL +#define Q_SLOT Q_SLOT +#endif //Q_MOC_RUN + +// macro for onaming members +#ifdef METHOD +#undef METHOD +#endif +#ifdef SLOT +#undef SLOT +#endif +#ifdef SIGNAL +#undef SIGNAL +#endif + +Q_CORE_EXPORT const char *qFlagLocation(const char *method); + +#define QTOSTRING_HELPER(s) #s +#define QTOSTRING(s) QTOSTRING_HELPER(s) +#ifndef QT_NO_DEBUG +# define QLOCATION "\0"__FILE__":"QTOSTRING(__LINE__) +# ifndef QT_NO_KEYWORDS +# define METHOD(a) qFlagLocation("0"#a QLOCATION) +# endif +# define SLOT(a) qFlagLocation("1"#a QLOCATION) +# define SIGNAL(a) qFlagLocation("2"#a QLOCATION) +#else +# ifndef QT_NO_KEYWORDS +# define METHOD(a) "0"#a +# endif +# define SLOT(a) "1"#a +# define SIGNAL(a) "2"#a +#endif + +#ifdef QT3_SUPPORT +#define METHOD_CODE 0 // member type codes +#define SLOT_CODE 1 +#define SIGNAL_CODE 2 +#endif + +#define QMETHOD_CODE 0 // member type codes +#define QSLOT_CODE 1 +#define QSIGNAL_CODE 2 + +#define Q_ARG(type, data) QArgument(#type, data) +#define Q_RETURN_ARG(type, data) QReturnArgument(#type, data) + +class QObject; +class QMetaMethod; +class QMetaEnum; +class QMetaProperty; +class QMetaClassInfo; + + +class Q_CORE_EXPORT QGenericArgument +{ +public: + inline QGenericArgument(const char *aName = 0, const void *aData = 0) + : _data(aData), _name(aName) {} + inline void *data() const { return const_cast(_data); } + inline const char *name() const { return _name; } + +private: + const void *_data; + const char *_name; +}; + +class Q_CORE_EXPORT QGenericReturnArgument: public QGenericArgument +{ +public: + inline QGenericReturnArgument(const char *aName = 0, void *aData = 0) + : QGenericArgument(aName, aData) + {} +}; + +template +class QArgument: public QGenericArgument +{ +public: + inline QArgument(const char *aName, const T &aData) + : QGenericArgument(aName, static_cast(&aData)) + {} +}; +template +class QArgument: public QGenericArgument +{ +public: + inline QArgument(const char *aName, T &aData) + : QGenericArgument(aName, static_cast(&aData)) + {} +}; + + +template +class QReturnArgument: public QGenericReturnArgument +{ +public: + inline QReturnArgument(const char *aName, T &aData) + : QGenericReturnArgument(aName, static_cast(&aData)) + {} +}; + +struct Q_CORE_EXPORT QMetaObject +{ + const char *className() const; + const QMetaObject *superClass() const; + + QObject *cast(QObject *obj) const; + const QObject *cast(const QObject *obj) const; + +#ifndef QT_NO_TRANSLATION + // ### Qt 4: Merge overloads + QString tr(const char *s, const char *c) const; + QString trUtf8(const char *s, const char *c) const; + QString tr(const char *s, const char *c, int n) const; + QString trUtf8(const char *s, const char *c, int n) const; +#endif // QT_NO_TRANSLATION + + int methodOffset() const; + int enumeratorOffset() const; + int propertyOffset() const; + int classInfoOffset() const; + + int constructorCount() const; + int methodCount() const; + int enumeratorCount() const; + int propertyCount() const; + int classInfoCount() const; + + int indexOfConstructor(const char *constructor) const; + int indexOfMethod(const char *method) const; + int indexOfSignal(const char *signal) const; + int indexOfSlot(const char *slot) const; + int indexOfEnumerator(const char *name) const; + int indexOfProperty(const char *name) const; + int indexOfClassInfo(const char *name) const; + + QMetaMethod constructor(int index) const; + QMetaMethod method(int index) const; + QMetaEnum enumerator(int index) const; + QMetaProperty property(int index) const; + QMetaClassInfo classInfo(int index) const; + QMetaProperty userProperty() const; + + static bool checkConnectArgs(const char *signal, const char *method); + static QByteArray normalizedSignature(const char *method); + static QByteArray normalizedType(const char *type); + + // internal index-based connect + static bool connect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index, + int type = 0, int *types = 0); + // internal index-based disconnect + static bool disconnect(const QObject *sender, int signal_index, + const QObject *receiver, int method_index); + static bool disconnectOne(const QObject *sender, int signal_index, + const QObject *receiver, int method_index); + // internal slot-name based connect + static void connectSlotsByName(QObject *o); + + // internal index-based signal activation + static void activate(QObject *sender, int signal_index, void **argv); //obsolete + static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsolete + static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv); + static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); //obsolete + + // internal guarded pointers + static void addGuard(QObject **ptr); + static void removeGuard(QObject **ptr); + static void changeGuard(QObject **ptr, QObject *o); + + static bool invokeMethod(QObject *obj, const char *member, + Qt::ConnectionType, + QGenericReturnArgument ret, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()); + + static inline bool invokeMethod(QObject *obj, const char *member, + QGenericReturnArgument ret, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) + { + return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3, + val4, val5, val6, val7, val8, val9); + } + + static inline bool invokeMethod(QObject *obj, const char *member, + Qt::ConnectionType type, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) + { + return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2, + val3, val4, val5, val6, val7, val8, val9); + } + + static inline bool invokeMethod(QObject *obj, const char *member, + QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) + { + return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0, + val1, val2, val3, val4, val5, val6, val7, val8, val9); + } + + QObject *newInstance(QGenericArgument val0 = QGenericArgument(0), + QGenericArgument val1 = QGenericArgument(), + QGenericArgument val2 = QGenericArgument(), + QGenericArgument val3 = QGenericArgument(), + QGenericArgument val4 = QGenericArgument(), + QGenericArgument val5 = QGenericArgument(), + QGenericArgument val6 = QGenericArgument(), + QGenericArgument val7 = QGenericArgument(), + QGenericArgument val8 = QGenericArgument(), + QGenericArgument val9 = QGenericArgument()) const; + + enum Call { + InvokeMetaMethod, + ReadProperty, + WriteProperty, + ResetProperty, + QueryPropertyDesignable, + QueryPropertyScriptable, + QueryPropertyStored, + QueryPropertyEditable, + QueryPropertyUser, + CreateInstance + }; + + int static_metacall(Call, int, void **) const; + static int metacall(QObject *, Call, int, void **); + +#ifdef QT3_SUPPORT + QT3_SUPPORT const char *superClassName() const; +#endif + + struct { // private data + const QMetaObject *superdata; + const char *stringdata; + const uint *data; + const void *extradata; + } d; +}; + +typedef const QMetaObject& (*QMetaObjectAccessor)(); + +struct QMetaObjectExtraData +{ +#ifdef Q_NO_DATA_RELOCATION + const QMetaObjectAccessor *objects; +#else + const QMetaObject **objects; +#endif + + typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **); //from revision 6 + //typedef int (*StaticMetaCall)(QMetaObject::Call, int, void **); //used from revison 2 until revison 5 + StaticMetacallFunction static_metacall; +}; + +inline const char *QMetaObject::className() const +{ return d.stringdata; } + +inline const QMetaObject *QMetaObject::superClass() const +{ return d.superdata; } + +#ifdef QT3_SUPPORT +inline const char *QMetaObject::superClassName() const +{ return d.superdata ? d.superdata->className() : 0; } +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QOBJECTDEFS_H diff --git a/src/corelib/kernel/qpointer.cpp b/src/corelib/kernel/qpointer.cpp new file mode 100644 index 0000000000..73f695b65a --- /dev/null +++ b/src/corelib/kernel/qpointer.cpp @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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$ +** +****************************************************************************/ + +/*! + \class QPointer + \brief The QPointer class is a template class that provides guarded pointers to QObject. + + \ingroup objectmodel + + + A guarded pointer, QPointer, behaves like a normal C++ + pointer \c{T *}, except that it is automatically set to 0 when the + referenced object is destroyed (unlike normal C++ pointers, which + become "dangling pointers" in such cases). \c T must be a + subclass of QObject. + + Guarded pointers are useful whenever you need to store a pointer + to a QObject that is owned by someone else, and therefore might be + destroyed while you still hold a reference to it. You can safely + test the pointer for validity. + + Qt also provides QSharedPointer, an implementation of a reference-counted + shared pointer object, which can be used to maintain a collection of + references to an individual pointer. + + Example: + + \snippet doc/src/snippets/pointer/pointer.cpp 0 + \dots + \snippet doc/src/snippets/pointer/pointer.cpp 1 + \snippet doc/src/snippets/pointer/pointer.cpp 2 + + If the QLabel is deleted in the meantime, the \c label variable + will hold 0 instead of an invalid address, and the last line will + never be executed. + + The functions and operators available with a QPointer are the + same as those available with a normal unguarded pointer, except + the pointer arithmetic operators (\c{+}, \c{-}, \c{++}, and + \c{--}), which are normally used only with arrays of objects. + + Use QPointers like normal pointers and you will not need to read + this class documentation. + + For creating guarded pointers, you can construct or assign to them + from a T* or from another guarded pointer of the same type. You + can compare them with each other using operator==() and + operator!=(), or test for 0 with isNull(). You can dereference + them using either the \c *x or the \c x->member notation. + + A guarded pointer will automatically cast to a \c T *, so you can + freely mix guarded and unguarded pointers. This means that if you + have a QPointer, you can pass it to a function that + requires a QWidget *. For this reason, it is of little value to + declare functions to take a QPointer as a parameter; just use + normal pointers. Use a QPointer when you are storing a pointer + over time. + + Note that class \c T must inherit QObject, or a compilation or + link error will result. + + \sa QSharedPointer, QObject, QObjectCleanupHandler +*/ + +/*! + \fn QPointer::QPointer() + + Constructs a 0 guarded pointer. + + \sa isNull() +*/ + +/*! + \fn QPointer::QPointer(T* p) + + Constructs a guarded pointer that points to same object that \a p + points to. +*/ + +/*! + \fn QPointer::QPointer(const QPointer &p) + + Copies one guarded pointer from another. The constructed guarded + pointer points to the same object that \a p points to (which may + be 0). +*/ + +/*! + \fn QPointer::~QPointer() + + Destroys the guarded pointer. Just like a normal pointer, + destroying a guarded pointer does \e not destroy the object being + pointed to. +*/ + +/*! + \fn QPointer& QPointer::operator=(const QPointer &p) + + Assignment operator. This guarded pointer will now point to the + same object that \a p points to. +*/ + +/*! + \fn QPointer & QPointer::operator=(T* p) + + Assignment operator. This guarded pointer will now point to the + same object that \a p points to. +*/ + +/*! + \fn T* QPointer::data() const + \since 4.4 + + Returns the pointer to the object being guarded. +*/ + +/*! + \fn bool QPointer::isNull() const + + Returns \c true if the referenced object has been destroyed or if + there is no referenced object; otherwise returns false. +*/ + +/*! + \fn T* QPointer::operator->() const + + Overloaded arrow operator; implements pointer semantics. Just use + this operator as you would with a normal C++ pointer. +*/ + +/*! + \fn T& QPointer::operator*() const + + Dereference operator; implements pointer semantics. Just use this + operator as you would with a normal C++ pointer. +*/ + +/*! + \fn QPointer::operator T*() const + + Cast operator; implements pointer semantics. Because of this + function you can pass a QPointer\ to a function where a T* + is required. +*/ + +/*! + \fn bool operator==(const T *o, const QPointer &p) + \relates QPointer + + Equality operator. Returns true if \a o and the guarded + pointer \a p are pointing to the same object, otherwise + returns false. + +*/ +/*! + \fn bool operator==(const QPointer &p, const T *o) + \relates QPointer + + Equality operator. Returns true if \a o and the guarded + pointer \a p are pointing to the same object, otherwise + returns false. + +*/ +/*! + \fn bool operator==(T *o, const QPointer &p) + \relates QPointer + + Equality operator. Returns true if \a o and the guarded + pointer \a p are pointing to the same object, otherwise + returns false. + +*/ +/*! + \fn bool operator==(const QPointer &p, T *o) + \relates QPointer + + Equality operator. Returns true if \a o and the guarded + pointer \a p are pointing to the same object, otherwise + returns false. + +*/ +/*! + \fn bool operator==(const QPointer &p1, const QPointer &p2) + \relates QPointer + + Equality operator. Returns true if the guarded pointers \a p1 and \a p2 + are pointing to the same object, otherwise + returns false. + +*/ + + +/*! + \fn bool operator!=(const T *o, const QPointer &p) + \relates QPointer + + Inequality operator. Returns true if \a o and the guarded + pointer \a p are not pointing to the same object, otherwise + returns false. +*/ +/*! + \fn bool operator!=(const QPointer &p, const T *o) + \relates QPointer + + Inequality operator. Returns true if \a o and the guarded + pointer \a p are not pointing to the same object, otherwise + returns false. +*/ +/*! + \fn bool operator!=(T *o, const QPointer &p) + \relates QPointer + + Inequality operator. Returns true if \a o and the guarded + pointer \a p are not pointing to the same object, otherwise + returns false. +*/ +/*! + \fn bool operator!=(const QPointer &p, T *o) + \relates QPointer + + Inequality operator. Returns true if \a o and the guarded + pointer \a p are not pointing to the same object, otherwise + returns false. +*/ +/*! + \fn bool operator!=(const QPointer &p1, const QPointer &p2) + \relates QPointer + + Inequality operator. Returns true if the guarded pointers \a p1 and + \a p2 are not pointing to the same object, otherwise + returns false. +*/ diff --git a/src/corelib/kernel/qpointer.h b/src/corelib/kernel/qpointer.h new file mode 100644 index 0000000000..c2621cb8bd --- /dev/null +++ b/src/corelib/kernel/qpointer.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QPOINTER_H +#define QPOINTER_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +template +class QPointer +{ + QObject *o; +public: + inline QPointer() : o(0) {} + inline QPointer(T *p) : o(p) + { QMetaObject::addGuard(&o); } + inline QPointer(const QPointer &p) : o(p.o) + { QMetaObject::addGuard(&o); } + inline ~QPointer() + { QMetaObject::removeGuard(&o); } + inline QPointer &operator=(const QPointer &p) + { if (this != &p) QMetaObject::changeGuard(&o, p.o); return *this; } + inline QPointer &operator=(T* p) + { if (o != p) QMetaObject::changeGuard(&o, p); return *this; } + + inline bool isNull() const + { return !o; } + + inline T* operator->() const + { return static_cast(const_cast(o)); } + inline T& operator*() const + { return *static_cast(const_cast(o)); } + inline operator T*() const + { return static_cast(const_cast(o)); } + inline T* data() const + { return static_cast(const_cast(o)); } +}; + + +#if (!defined(Q_CC_SUN) || (__SUNPRO_CC >= 0x580)) // ambiguity between const T * and T * + +template +inline bool operator==(const T *o, const QPointer &p) +{ return o == p.operator->(); } + +template +inline bool operator==(const QPointer &p, const T *o) +{ return p.operator->() == o; } + +#else + +template +inline bool operator==(const void *o, const QPointer &p) +{ return o == p.operator->(); } + +template +inline bool operator==(const QPointer &p, const void *o) +{ return p.operator->() == o; } + +#endif + +template +inline bool operator==(T *o, const QPointer &p) +{ return o == p.operator->(); } + +template +inline bool operator==(const QPointer &p, T *o) +{ return p.operator->() == o; } + +template +inline bool operator==(const QPointer &p1, const QPointer &p2) +{ return p1.operator->() == p2.operator->(); } + + +#if (!defined(Q_CC_SUN) || (__SUNPRO_CC >= 0x580)) // ambiguity between const T * and T * + +template +inline bool operator!=(const T *o, const QPointer &p) +{ return o != p.operator->(); } + +template +inline bool operator!= (const QPointer &p, const T *o) +{ return p.operator->() != o; } + +#else + +template +inline bool operator!= (const void *o, const QPointer &p) +{ return o != p.operator->(); } + +template +inline bool operator!= (const QPointer &p, const void *o) +{ return p.operator->() != o; } + +#endif + +template +inline bool operator!=(T *o, const QPointer &p) +{ return o != p.operator->(); } + +template +inline bool operator!= (const QPointer &p, T *o) +{ return p.operator->() != o; } + +template +inline bool operator!= (const QPointer &p1, const QPointer &p2) +{ return p1.operator->() != p2.operator->() ; } + +// Make MSVC < 1400 (2005) handle "if (NULL == p)" syntax +#if defined(Q_CC_MSVC) && (_MSC_VER < 1400) +template +inline bool operator== (int i, const QPointer &p) +{ Q_ASSERT(i == 0); return !i && p.isNull(); } + +template +inline bool operator!= (int i, const QPointer &p) +{ Q_ASSERT(i == 0); return !i && !p.isNull(); } +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QPOINTER_H diff --git a/src/corelib/kernel/qsharedmemory.cpp b/src/corelib/kernel/qsharedmemory.cpp new file mode 100644 index 0000000000..78715a1966 --- /dev/null +++ b/src/corelib/kernel/qsharedmemory.cpp @@ -0,0 +1,612 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qsharedmemory.h" +#include "qsharedmemory_p.h" +#include "qsystemsemaphore.h" +#include +#include +#ifdef Q_OS_SYMBIAN +#include +#endif +#include + +QT_BEGIN_NAMESPACE + +#if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE)) +/*! + \internal + + Generate a string from the key which can be any unicode string into + the subset that the win/unix kernel allows. + + On Unix this will be a file name + On Symbian key will be truncated to 80 characters + */ +QString +QSharedMemoryPrivate::makePlatformSafeKey(const QString &key, + const QString &prefix) +{ + if (key.isEmpty()) + return QString(); + + QString result = prefix; + + QString part1 = key; + part1.replace(QRegExp(QLatin1String("[^A-Za-z]")), QString()); + result.append(part1); + + QByteArray hex = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toHex(); + result.append(QLatin1String(hex)); +#ifdef Q_OS_WIN + return result; +#elif defined(Q_OS_SYMBIAN) + return result.left(KMaxKernelName); +#else + return QDir::tempPath() + QLatin1Char('/') + result; +#endif +} +#endif // QT_NO_SHAREDMEMORY && QT_NO_SHAREDMEMORY + +#ifndef QT_NO_SHAREDMEMORY + +/*! + \class QSharedMemory + \since 4.4 + + \brief The QSharedMemory class provides access to a shared memory segment. + + QSharedMemory provides access to a shared memory segment by multiple + threads and processes. It also provides a way for a single thread or + process to lock the memory for exclusive access. + + When using this class, be aware of the following platform + differences: + + \list + + \o Windows: QSharedMemory does not "own" the shared memory segment. + When all threads or processes that have an instance of QSharedMemory + attached to a particular shared memory segment have either destroyed + their instance of QSharedMemory or exited, the Windows kernel + releases the shared memory segment automatically. + + \o Unix: QSharedMemory "owns" the shared memory segment. When the + last thread or process that has an instance of QSharedMemory + attached to a particular shared memory segment detaches from the + segment by destroying its instance of QSharedMemory, the Unix kernel + release the shared memory segment. But if that last thread or + process crashes without running the QSharedMemory destructor, the + shared memory segment survives the crash. + + \o HP-UX: Only one attach to a shared memory segment is allowed per + process. This means that QSharedMemory should not be used across + multiple threads in the same process in HP-UX. + + \o Symbian: QSharedMemory does not "own" the shared memory segment. + When all threads or processes that have an instance of QSharedMemory + attached to a particular shared memory segment have either destroyed + their instance of QSharedMemory or exited, the Symbian kernel + releases the shared memory segment automatically. + Also, access to a shared memory segment cannot be limited to read-only + in Symbian. + + \endlist + + Remember to lock the shared memory with lock() before reading from + or writing to the shared memory, and remember to release the lock + with unlock() after you are done. + + Unlike QtSharedMemory, QSharedMemory automatically destroys the + shared memory segment when the last instance of QSharedMemory is + detached from the segment, and no references to the segment + remain. Do not mix using QtSharedMemory and QSharedMemory. Port + everything to QSharedMemory. + + \warning QSharedMemory changes the key in a Qt-specific way, unless otherwise + specified. Interoperation with non-Qt applications is achieved by first creating + a default shared memory with QSharedMemory() and then setting a native key with + setNativeKey(). When using native keys, shared memory is not protected against + multiple accesses on it (e.g. unable to lock()) and a user-defined mechanism + should be used to achieve a such protection. + */ + +/*! + \overload QSharedMemory() + + Constructs a shared memory object with the given \a parent. The + shared memory object's key is not set by the constructor, so the + shared memory object does not have an underlying shared memory + segment attached. The key must be set with setKey() or setNativeKey() + before create() or attach() can be used. + + \sa setKey() + */ +QSharedMemory::QSharedMemory(QObject *parent) + : QObject(*new QSharedMemoryPrivate, parent) +{ +} + +/*! + Constructs a shared memory object with the given \a parent and with + its key set to \a key. Because its key is set, its create() and + attach() functions can be called. + + \sa setKey(), create(), attach() + */ +QSharedMemory::QSharedMemory(const QString &key, QObject *parent) + : QObject(*new QSharedMemoryPrivate, parent) +{ + setKey(key); +} + +/*! + The destructor clears the key, which forces the shared memory object + to \l {detach()} {detach} from its underlying shared memory + segment. If this shared memory object is the last one connected to + the shared memory segment, the detach() operation destroys the + shared memory segment. + + \sa detach() isAttached() + */ +QSharedMemory::~QSharedMemory() +{ + setKey(QString()); +} + +/*! + Sets the platform independent \a key for this shared memory object. If \a key + is the same as the current key, the function returns without doing anything. + + You can call key() to retrieve the platform independent key. Internally, + QSharedMemory converts this key into a platform specific key. If you instead + call nativeKey(), you will get the platform specific, converted key. + + If the shared memory object is attached to an underlying shared memory + segment, it will \l {detach()} {detach} from it before setting the new key. + This function does not do an attach(). + + \sa key() nativeKey() isAttached() +*/ +void QSharedMemory::setKey(const QString &key) +{ + Q_D(QSharedMemory); + if (key == d->key && d->makePlatformSafeKey(key) == d->nativeKey) + return; + + if (isAttached()) + detach(); + d->cleanHandle(); + d->key = key; + d->nativeKey = d->makePlatformSafeKey(key); +} + +/*! + \since 4.8 + + Sets the native, platform specific, \a key for this shared memory object. If + \a key is the same as the current native key, the function returns without + doing anything. If all you want is to assign a key to a segment, you should + call setKey() instead. + + You can call nativeKey() to retrieve the native key. If a native key has been + assigned, calling key() will return a null string. + + If the shared memory object is attached to an underlying shared memory + segment, it will \l {detach()} {detach} from it before setting the new key. + This function does not do an attach(). + + The application will not be portable if you set a native key. + + \sa nativeKey() key() isAttached() +*/ +void QSharedMemory::setNativeKey(const QString &key) +{ + Q_D(QSharedMemory); + if (key == d->nativeKey && d->key.isNull()) + return; + + if (isAttached()) + detach(); + d->cleanHandle(); + d->key = QString(); + d->nativeKey = key; +} + +bool QSharedMemoryPrivate::initKey() +{ + if (!cleanHandle()) + return false; +#ifndef QT_NO_SYSTEMSEMAPHORE + systemSemaphore.setKey(QString(), 1); + systemSemaphore.setKey(key, 1); + if (systemSemaphore.error() != QSystemSemaphore::NoError) { + QString function = QLatin1String("QSharedMemoryPrivate::initKey"); + errorString = QSharedMemory::tr("%1: unable to set key on lock").arg(function); + switch(systemSemaphore.error()) { + case QSystemSemaphore::PermissionDenied: + error = QSharedMemory::PermissionDenied; + break; + case QSystemSemaphore::KeyError: + error = QSharedMemory::KeyError; + break; + case QSystemSemaphore::AlreadyExists: + error = QSharedMemory::AlreadyExists; + break; + case QSystemSemaphore::NotFound: + error = QSharedMemory::NotFound; + break; + case QSystemSemaphore::OutOfResources: + error = QSharedMemory::OutOfResources; + break; + case QSystemSemaphore::UnknownError: + default: + error = QSharedMemory::UnknownError; + break; + } + return false; + } +#endif + errorString = QString(); + error = QSharedMemory::NoError; + return true; +} + +/*! + Returns the key assigned with setKey() to this shared memory, or a null key + if no key has been assigned, or if the segment is using a nativeKey(). The + key is the identifier used by Qt applications to identify the shared memory + segment. + + You can find the native, platform specific, key used by the operating system + by calling nativeKey(). + + \sa setKey() setNativeKey() + */ +QString QSharedMemory::key() const +{ + Q_D(const QSharedMemory); + return d->key; +} + +/*! + \since 4.8 + + Returns the native, platform specific, key for this shared memory object. The + native key is the identifier used by the operating system to identify the + shared memory segment. + + You can use the native key to access shared memory segments that have not + been created by Qt, or to grant shared memory access to non-Qt applications. + + \sa setKey() setNativeKey() +*/ +QString QSharedMemory::nativeKey() const +{ + Q_D(const QSharedMemory); + return d->nativeKey; +} + +/*! + Creates a shared memory segment of \a size bytes with the key passed to the + constructor, set with setKey() or set with setNativeKey(), then attaches to + the new shared memory segment with the given access \a mode and returns + \tt true. If a shared memory segment identified by the key already exists, + the attach operation is not performed and \tt false is returned. When the + return value is \tt false, call error() to determine which error occurred. + + \sa error() + */ +bool QSharedMemory::create(int size, AccessMode mode) +{ + Q_D(QSharedMemory); + + if (!d->initKey()) + return false; + +#ifndef QT_NO_SYSTEMSEMAPHORE +#ifndef Q_OS_WIN + // Take ownership and force set initialValue because the semaphore + // might have already existed from a previous crash. + d->systemSemaphore.setKey(d->key, 1, QSystemSemaphore::Create); +#endif +#endif + + QString function = QLatin1String("QSharedMemory::create"); +#ifndef QT_NO_SYSTEMSEMAPHORE + QSharedMemoryLocker lock(this); + if (!d->key.isNull() && !d->tryLocker(&lock, function)) + return false; +#endif + + if (size <= 0) { + d->error = QSharedMemory::InvalidSize; + d->errorString = + QSharedMemory::tr("%1: create size is less then 0").arg(function); + return false; + } + + if (!d->create(size)) + return false; + + return d->attach(mode); +} + +/*! + Returns the size of the attached shared memory segment. If no shared + memory segment is attached, 0 is returned. + + \sa create() attach() + */ +int QSharedMemory::size() const +{ + Q_D(const QSharedMemory); + return d->size; +} + +/*! + \enum QSharedMemory::AccessMode + + \value ReadOnly The shared memory segment is read-only. Writing to + the shared memory segment is not allowed. An attempt to write to a + shared memory segment created with ReadOnly causes the program to + abort. + + \value ReadWrite Reading and writing the shared memory segment are + both allowed. +*/ + +/*! + Attempts to attach the process to the shared memory segment + identified by the key that was passed to the constructor or to a + call to setKey() or setNativeKey(). The access \a mode is \l {QSharedMemory::} + {ReadWrite} by default. It can also be \l {QSharedMemory::} + {ReadOnly}. Returns true if the attach operation is successful. If + false is returned, call error() to determine which error occurred. + After attaching the shared memory segment, a pointer to the shared + memory can be obtained by calling data(). + + \sa isAttached(), detach(), create() + */ +bool QSharedMemory::attach(AccessMode mode) +{ + Q_D(QSharedMemory); + + if (isAttached() || !d->initKey()) + return false; +#ifndef QT_NO_SYSTEMSEMAPHORE + QSharedMemoryLocker lock(this); + if (!d->key.isNull() && !d->tryLocker(&lock, QLatin1String("QSharedMemory::attach"))) + return false; +#endif + + if (isAttached() || !d->handle()) + return false; + + return d->attach(mode); +} + +/*! + Returns true if this process is attached to the shared memory + segment. + + \sa attach(), detach() + */ +bool QSharedMemory::isAttached() const +{ + Q_D(const QSharedMemory); + return (0 != d->memory); +} + +/*! + Detaches the process from the shared memory segment. If this was the + last process attached to the shared memory segment, then the shared + memory segment is released by the system, i.e., the contents are + destroyed. The function returns true if it detaches the shared + memory segment. If it returns false, it usually means the segment + either isn't attached, or it is locked by another process. + + \sa attach(), isAttached() + */ +bool QSharedMemory::detach() +{ + Q_D(QSharedMemory); + if (!isAttached()) + return false; + +#ifndef QT_NO_SYSTEMSEMAPHORE + QSharedMemoryLocker lock(this); + if (!d->key.isNull() && !d->tryLocker(&lock, QLatin1String("QSharedMemory::detach"))) + return false; +#endif + + return d->detach(); +} + +/*! + Returns a pointer to the contents of the shared memory segment, if + one is attached. Otherwise it returns null. Remember to lock the + shared memory with lock() before reading from or writing to the + shared memory, and remember to release the lock with unlock() after + you are done. + + \sa attach() + */ +void *QSharedMemory::data() +{ + Q_D(QSharedMemory); + return d->memory; +} + +/*! + Returns a const pointer to the contents of the shared memory + segment, if one is attached. Otherwise it returns null. Remember to + lock the shared memory with lock() before reading from or writing to + the shared memory, and remember to release the lock with unlock() + after you are done. + + \sa attach() create() + */ +const void* QSharedMemory::constData() const +{ + Q_D(const QSharedMemory); + return d->memory; +} + +/*! + \overload data() + */ +const void *QSharedMemory::data() const +{ + Q_D(const QSharedMemory); + return d->memory; +} + +#ifndef QT_NO_SYSTEMSEMAPHORE +/*! + This is a semaphore that locks the shared memory segment for access + by this process and returns true. If another process has locked the + segment, this function blocks until the lock is released. Then it + acquires the lock and returns true. If this function returns false, + it means that you have ignored a false return from create() or attach(), + that you have set the key with setNativeKey() or that + QSystemSemaphore::acquire() failed due to an unknown system error. + + \sa unlock(), data(), QSystemSemaphore::acquire() + */ +bool QSharedMemory::lock() +{ + Q_D(QSharedMemory); + if (d->lockedByMe) { + qWarning("QSharedMemory::lock: already locked"); + return true; + } + if (d->systemSemaphore.acquire()) { + d->lockedByMe = true; + return true; + } + QString function = QLatin1String("QSharedMemory::lock"); + d->errorString = QSharedMemory::tr("%1: unable to lock").arg(function); + d->error = QSharedMemory::LockError; + return false; +} + +/*! + Releases the lock on the shared memory segment and returns true, if + the lock is currently held by this process. If the segment is not + locked, or if the lock is held by another process, nothing happens + and false is returned. + + \sa lock() + */ +bool QSharedMemory::unlock() +{ + Q_D(QSharedMemory); + if (!d->lockedByMe) + return false; + d->lockedByMe = false; + if (d->systemSemaphore.release()) + return true; + QString function = QLatin1String("QSharedMemory::unlock"); + d->errorString = QSharedMemory::tr("%1: unable to unlock").arg(function); + d->error = QSharedMemory::LockError; + return false; +} +#endif // QT_NO_SYSTEMSEMAPHORE + +/*! + \enum QSharedMemory::SharedMemoryError + + \value NoError No error occurred. + + \value PermissionDenied The operation failed because the caller + didn't have the required permissions. + + \value InvalidSize A create operation failed because the requested + size was invalid. + + \value KeyError The operation failed because of an invalid key. + + \value AlreadyExists A create() operation failed because a shared + memory segment with the specified key already existed. + + \value NotFound An attach() failed because a shared memory segment + with the specified key could not be found. + + \value LockError The attempt to lock() the shared memory segment + failed because create() or attach() failed and returned false, or + because a system error occurred in QSystemSemaphore::acquire(). + + \value OutOfResources A create() operation failed because there was + not enough memory available to fill the request. + + \value UnknownError Something else happened and it was bad. +*/ + +/*! + Returns a value indicating whether an error occurred, and, if so, + which error it was. + + \sa errorString() + */ +QSharedMemory::SharedMemoryError QSharedMemory::error() const +{ + Q_D(const QSharedMemory); + return d->error; +} + +/*! + Returns a text description of the last error that occurred. If + error() returns an \l {QSharedMemory::SharedMemoryError} {error + value}, call this function to get a text string that describes the + error. + + \sa error() + */ +QString QSharedMemory::errorString() const +{ + Q_D(const QSharedMemory); + return d->errorString; +} + +#endif // QT_NO_SHAREDMEMORY + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qsharedmemory.h b/src/corelib/kernel/qsharedmemory.h new file mode 100644 index 0000000000..4a610033a1 --- /dev/null +++ b/src/corelib/kernel/qsharedmemory.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QSHAREDMEMORY_H +#define QSHAREDMEMORY_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_SHAREDMEMORY + +class QSharedMemoryPrivate; + +class Q_CORE_EXPORT QSharedMemory : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QSharedMemory) + +public: + enum AccessMode + { + ReadOnly, + ReadWrite + }; + + enum SharedMemoryError + { + NoError, + PermissionDenied, + InvalidSize, + KeyError, + AlreadyExists, + NotFound, + LockError, + OutOfResources, + UnknownError + }; + + QSharedMemory(QObject *parent = 0); + QSharedMemory(const QString &key, QObject *parent = 0); + ~QSharedMemory(); + + void setKey(const QString &key); + QString key() const; + void setNativeKey(const QString &key); + QString nativeKey() const; + + bool create(int size, AccessMode mode = ReadWrite); + int size() const; + + bool attach(AccessMode mode = ReadWrite); + bool isAttached() const; + bool detach(); + + void *data(); + const void* constData() const; + const void *data() const; + +#ifndef QT_NO_SYSTEMSEMAPHORE + bool lock(); + bool unlock(); +#endif + + SharedMemoryError error() const; + QString errorString() const; + +private: + Q_DISABLE_COPY(QSharedMemory) +}; + +#endif // QT_NO_SHAREDMEMORY + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSHAREDMEMORY_H + diff --git a/src/corelib/kernel/qsharedmemory_p.h b/src/corelib/kernel/qsharedmemory_p.h new file mode 100644 index 0000000000..72eda372ae --- /dev/null +++ b/src/corelib/kernel/qsharedmemory_p.h @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QSHAREDMEMORY_P_H +#define QSHAREDMEMORY_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 "qsharedmemory.h" + +#ifdef QT_NO_SHAREDMEMORY +# ifndef QT_NO_SYSTEMSEMAPHORE +namespace QSharedMemoryPrivate +{ + int createUnixKeyFile(const QString &fileName); + QString makePlatformSafeKey(const QString &key, + const QString &prefix = QLatin1String("qipc_sharedmemory_")); +} +#endif +#else + +#include "qsystemsemaphore.h" +#include "private/qobject_p.h" + +#ifdef Q_OS_WIN +#include +#elif defined(Q_OS_SYMBIAN) +#include +#include +#else +#include +#endif + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SYSTEMSEMAPHORE +/*! + Helper class + */ +class QSharedMemoryLocker +{ + +public: + inline QSharedMemoryLocker(QSharedMemory *sharedMemory) : q_sm(sharedMemory) + { + Q_ASSERT(q_sm); + } + + inline ~QSharedMemoryLocker() + { + if (q_sm) + q_sm->unlock(); + } + + inline bool lock() + { + if (q_sm && q_sm->lock()) + return true; + q_sm = 0; + return false; + } + +private: + QSharedMemory *q_sm; +}; +#endif // QT_NO_SYSTEMSEMAPHORE + +class Q_AUTOTEST_EXPORT QSharedMemoryPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QSharedMemory) + +public: + QSharedMemoryPrivate(); + + void *memory; + int size; + QString key; + QString nativeKey; + QSharedMemory::SharedMemoryError error; + QString errorString; +#ifndef QT_NO_SYSTEMSEMAPHORE + QSystemSemaphore systemSemaphore; + bool lockedByMe; +#endif + + static int createUnixKeyFile(const QString &fileName); + static QString makePlatformSafeKey(const QString &key, + const QString &prefix = QLatin1String("qipc_sharedmemory_")); +#ifdef Q_OS_WIN + HANDLE handle(); +#else + key_t handle(); +#endif + bool initKey(); + bool cleanHandle(); + bool create(int size); + bool attach(QSharedMemory::AccessMode mode); + bool detach(); + +#ifdef Q_OS_SYMBIAN + void setErrorString(const QString &function, TInt errorCode); +#else + void setErrorString(const QString &function); +#endif + +#ifndef QT_NO_SYSTEMSEMAPHORE + bool tryLocker(QSharedMemoryLocker *locker, const QString function) { + if (!locker->lock()) { + errorString = QSharedMemory::tr("%1: unable to lock").arg(function); + error = QSharedMemory::LockError; + return false; + } + return true; + } +#endif // QT_NO_SYSTEMSEMAPHORE + +private: +#ifdef Q_OS_WIN + HANDLE hand; +#elif defined(Q_OS_SYMBIAN) + RChunk chunk; +#else + key_t unix_key; +#endif +}; + +QT_END_NAMESPACE + +#endif // QT_NO_SHAREDMEMORY + +#endif // QSHAREDMEMORY_P_H + diff --git a/src/corelib/kernel/qsharedmemory_symbian.cpp b/src/corelib/kernel/qsharedmemory_symbian.cpp new file mode 100644 index 0000000000..3565e264db --- /dev/null +++ b/src/corelib/kernel/qsharedmemory_symbian.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qsharedmemory.h" +#include "qsharedmemory_p.h" +#include "qsystemsemaphore.h" +#include "qcore_symbian_p.h" +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SHAREDMEMORY + +#define QSHAREDMEMORY_DEBUG + +QSharedMemoryPrivate::QSharedMemoryPrivate() : QObjectPrivate(), + memory(0), size(0), error(QSharedMemory::NoError), + systemSemaphore(QString()), lockedByMe(false) +{ +} + +void QSharedMemoryPrivate::setErrorString(const QString &function, TInt errorCode) +{ + if (errorCode == KErrNone) + return; + switch (errorCode) { + case KErrAlreadyExists: + error = QSharedMemory::AlreadyExists; + errorString = QSharedMemory::tr("%1: already exists").arg(function); + break; + case KErrNotFound: + error = QSharedMemory::NotFound; + errorString = QSharedMemory::tr("%1: doesn't exists").arg(function); + break; + case KErrArgument: + error = QSharedMemory::InvalidSize; + errorString = QSharedMemory::tr("%1: invalid size").arg(function); + break; + case KErrNoMemory: + error = QSharedMemory::OutOfResources; + errorString = QSharedMemory::tr("%1: out of resources").arg(function); + break; + case KErrPermissionDenied: + error = QSharedMemory::PermissionDenied; + errorString = QSharedMemory::tr("%1: permission denied").arg(function); + break; + default: + errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(errorCode); + error = QSharedMemory::UnknownError; +#if defined QSHAREDMEMORY_DEBUG + qDebug() << errorString << "key" << key; +#endif + } +} + +key_t QSharedMemoryPrivate::handle() +{ + // Not really cost effective to check here if shared memory is attachable, as it requires + // exactly the same call as attaching, so always assume handle is valid and return failure + // from attach. + return 1; +} + +bool QSharedMemoryPrivate::cleanHandle() +{ + chunk.Close(); + return true; +} + +bool QSharedMemoryPrivate::create(int size) +{ + QString function = QLatin1String("QSharedMemory::create"); + if (nativeKey.isEmpty()) { + error = QSharedMemory::KeyError; + errorString = QSharedMemory::tr("%1: key error").arg(function); + return false; + } + + TPtrC ptr(qt_QString2TPtrC(nativeKey)); + + TInt err = chunk.CreateGlobal(ptr, size, size); + + setErrorString(function, err); + + if (err != KErrNone) + return false; + + // Zero out the created chunk + Mem::FillZ(chunk.Base(), chunk.Size()); + + return true; +} + +bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode /* mode */) +{ + // Grab a pointer to the memory block + if (!chunk.Handle()) { + QString function = QLatin1String("QSharedMemory::handle"); + if (nativeKey.isEmpty()) { + error = QSharedMemory::KeyError; + errorString = QSharedMemory::tr("%1: unable to make key").arg(function); + return false; + } + + TPtrC ptr(qt_QString2TPtrC(nativeKey)); + + TInt err = KErrNoMemory; + + err = chunk.OpenGlobal(ptr, false); + + if (err != KErrNone) { + setErrorString(function, err); + return false; + } + } + + size = chunk.Size(); + memory = chunk.Base(); + + return true; +} + +bool QSharedMemoryPrivate::detach() +{ + chunk.Close(); + + memory = 0; + size = 0; + + return true; +} + +#endif //QT_NO_SHAREDMEMORY + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qsharedmemory_unix.cpp b/src/corelib/kernel/qsharedmemory_unix.cpp new file mode 100644 index 0000000000..b30117f6c1 --- /dev/null +++ b/src/corelib/kernel/qsharedmemory_unix.cpp @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qsharedmemory.h" +#include "qsharedmemory_p.h" +#include "qsystemsemaphore.h" +#include +#include + +#include + +#ifndef QT_NO_SHAREDMEMORY +#include +#include +#include +#include +#include +#include +#include +#endif //QT_NO_SHAREDMEMORY + +#include "private/qcore_unix_p.h" + +#ifndef QT_NO_SHAREDMEMORY +QT_BEGIN_NAMESPACE + +QSharedMemoryPrivate::QSharedMemoryPrivate() + : QObjectPrivate(), memory(0), size(0), error(QSharedMemory::NoError), +#ifndef QT_NO_SYSTEMSEMAPHORE + systemSemaphore(QString()), lockedByMe(false), +#endif + unix_key(0) +{ +} + +void QSharedMemoryPrivate::setErrorString(const QString &function) +{ + // EINVAL is handled in functions so they can give better error strings + switch (errno) { + case EACCES: + errorString = QSharedMemory::tr("%1: permission denied").arg(function); + error = QSharedMemory::PermissionDenied; + break; + case EEXIST: + errorString = QSharedMemory::tr("%1: already exists").arg(function); + error = QSharedMemory::AlreadyExists; + break; + case ENOENT: + errorString = QSharedMemory::tr("%1: doesn't exist").arg(function); + error = QSharedMemory::NotFound; + break; + case EMFILE: + case ENOMEM: + case ENOSPC: + errorString = QSharedMemory::tr("%1: out of resources").arg(function); + error = QSharedMemory::OutOfResources; + break; + default: + errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(errno); + error = QSharedMemory::UnknownError; +#if defined QSHAREDMEMORY_DEBUG + qDebug() << errorString << "key" << key << "errno" << errno << EINVAL; +#endif + } +} + +/*! + \internal + + If not already made create the handle used for accessing the shared memory. +*/ +key_t QSharedMemoryPrivate::handle() +{ + // already made + if (unix_key) + return unix_key; + + // don't allow making handles on empty keys + if (nativeKey.isEmpty()) { + errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle:")); + error = QSharedMemory::KeyError; + return 0; + } + + // ftok requires that an actual file exists somewhere + if (!QFile::exists(nativeKey)) { + errorString = QSharedMemory::tr("%1: UNIX key file doesn't exist").arg(QLatin1String("QSharedMemory::handle:")); + error = QSharedMemory::NotFound; + return 0; + } + + unix_key = ftok(QFile::encodeName(nativeKey).constData(), 'Q'); + if (-1 == unix_key) { + errorString = QSharedMemory::tr("%1: ftok failed").arg(QLatin1String("QSharedMemory::handle:")); + error = QSharedMemory::KeyError; + unix_key = 0; + } + return unix_key; +} + +#endif // QT_NO_SHAREDMEMORY + +#if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE)) +/*! + \internal + Creates the unix file if needed. + returns true if the unix file was created. + + -1 error + 0 already existed + 1 created + */ +int QSharedMemoryPrivate::createUnixKeyFile(const QString &fileName) +{ + if (QFile::exists(fileName)) + return 0; + + int fd = qt_safe_open(QFile::encodeName(fileName).constData(), + O_EXCL | O_CREAT | O_RDWR, 0640); + if (-1 == fd) { + if (errno == EEXIST) + return 0; + return -1; + } else { + close(fd); + } + return 1; +} +#endif // QT_NO_SHAREDMEMORY && QT_NO_SYSTEMSEMAPHORE + +#ifndef QT_NO_SHAREDMEMORY + +bool QSharedMemoryPrivate::cleanHandle() +{ + unix_key = 0; + return true; +} + +bool QSharedMemoryPrivate::create(int size) +{ + // build file if needed + bool createdFile = false; + int built = createUnixKeyFile(nativeKey); + if (built == -1) { + errorString = QSharedMemory::tr("%1: unable to make key").arg(QLatin1String("QSharedMemory::handle:")); + error = QSharedMemory::KeyError; + return false; + } + if (built == 1) { + createdFile = true; + } + + // get handle + if (!handle()) { + if (createdFile) + QFile::remove(nativeKey); + return false; + } + + // create + if (-1 == shmget(unix_key, size, 0666 | IPC_CREAT | IPC_EXCL)) { + QString function = QLatin1String("QSharedMemory::create"); + switch (errno) { + case EINVAL: + errorString = QSharedMemory::tr("%1: system-imposed size restrictions").arg(QLatin1String("QSharedMemory::handle")); + error = QSharedMemory::InvalidSize; + break; + default: + setErrorString(function); + } + if (createdFile && error != QSharedMemory::AlreadyExists) + QFile::remove(nativeKey); + return false; + } + + return true; +} + +bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode) +{ + // grab the shared memory segment id + int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0444 : 0660)); + if (-1 == id) { + setErrorString(QLatin1String("QSharedMemory::attach (shmget)")); + return false; + } + + // grab the memory + memory = shmat(id, 0, (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0)); + if ((void*) - 1 == memory) { + memory = 0; + setErrorString(QLatin1String("QSharedMemory::attach (shmat)")); + return false; + } + + // grab the size + shmid_ds shmid_ds; + if (!shmctl(id, IPC_STAT, &shmid_ds)) { + size = (int)shmid_ds.shm_segsz; + } else { + setErrorString(QLatin1String("QSharedMemory::attach (shmctl)")); + return false; + } + + return true; +} + +bool QSharedMemoryPrivate::detach() +{ + // detach from the memory segment + if (-1 == shmdt(memory)) { + QString function = QLatin1String("QSharedMemory::detach"); + switch (errno) { + case EINVAL: + errorString = QSharedMemory::tr("%1: not attached").arg(function); + error = QSharedMemory::NotFound; + break; + default: + setErrorString(function); + } + return false; + } + memory = 0; + size = 0; + + // Get the number of current attachments + int id = shmget(unix_key, 0, 0444); + cleanHandle(); + + struct shmid_ds shmid_ds; + if (0 != shmctl(id, IPC_STAT, &shmid_ds)) { + switch (errno) { + case EINVAL: + return true; + default: + return false; + } + } + // If there are no attachments then remove it. + if (shmid_ds.shm_nattch == 0) { + // mark for removal + if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) { + setErrorString(QLatin1String("QSharedMemory::remove")); + switch (errno) { + case EINVAL: + return true; + default: + return false; + } + } + + // remove file + if (!QFile::remove(nativeKey)) + return false; + } + return true; +} + + +QT_END_NAMESPACE + +#endif // QT_NO_SHAREDMEMORY diff --git a/src/corelib/kernel/qsharedmemory_win.cpp b/src/corelib/kernel/qsharedmemory_win.cpp new file mode 100644 index 0000000000..bc13e338bd --- /dev/null +++ b/src/corelib/kernel/qsharedmemory_win.cpp @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qsharedmemory.h" +#include "qsharedmemory_p.h" +#include "qsystemsemaphore.h" +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SHAREDMEMORY + +QSharedMemoryPrivate::QSharedMemoryPrivate() : QObjectPrivate(), + memory(0), size(0), error(QSharedMemory::NoError), + systemSemaphore(QString()), lockedByMe(false), hand(0) +{ +} + +void QSharedMemoryPrivate::setErrorString(const QString &function) +{ + BOOL windowsError = GetLastError(); + if (windowsError == 0) + return; + switch (windowsError) { + case ERROR_ALREADY_EXISTS: + error = QSharedMemory::AlreadyExists; + errorString = QSharedMemory::tr("%1: already exists").arg(function); + break; + case ERROR_FILE_NOT_FOUND: +#ifdef Q_OS_WINCE + // This happens on CE only if no file is present as CreateFileMappingW + // bails out with this error code + case ERROR_INVALID_PARAMETER: +#endif + error = QSharedMemory::NotFound; + errorString = QSharedMemory::tr("%1: doesn't exist").arg(function); + break; + case ERROR_COMMITMENT_LIMIT: + error = QSharedMemory::InvalidSize; + errorString = QSharedMemory::tr("%1: invalid size").arg(function); + break; + case ERROR_NO_SYSTEM_RESOURCES: + case ERROR_NOT_ENOUGH_MEMORY: + error = QSharedMemory::OutOfResources; + errorString = QSharedMemory::tr("%1: out of resources").arg(function); + break; + case ERROR_ACCESS_DENIED: + error = QSharedMemory::PermissionDenied; + errorString = QSharedMemory::tr("%1: permission denied").arg(function); + break; + default: + errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(windowsError); + error = QSharedMemory::UnknownError; +#if defined QSHAREDMEMORY_DEBUG + qDebug() << errorString << "key" << key; +#endif + } +} + +HANDLE QSharedMemoryPrivate::handle() +{ + if (!hand) { + QString function = QLatin1String("QSharedMemory::handle"); + if (nativeKey.isEmpty()) { + error = QSharedMemory::KeyError; + errorString = QSharedMemory::tr("%1: unable to make key").arg(function); + return false; + } +#ifndef Q_OS_WINCE + hand = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, (wchar_t*)nativeKey.utf16()); +#else + // This works for opening a mapping too, but always opens it with read/write access in + // attach as it seems. + hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, 0, (wchar_t*)nativeKey.utf16()); +#endif + if (!hand) { + setErrorString(function); + return false; + } + } + return hand; +} + +bool QSharedMemoryPrivate::cleanHandle() +{ + if (hand != 0 && !CloseHandle(hand)) { + hand = 0; + setErrorString(QLatin1String("QSharedMemory::cleanHandle")); + return false; + } + hand = 0; + return true; +} + +bool QSharedMemoryPrivate::create(int size) +{ + QString function = QLatin1String("QSharedMemory::create"); + if (nativeKey.isEmpty()) { + error = QSharedMemory::KeyError; + errorString = QSharedMemory::tr("%1: key error").arg(function); + return false; + } + + // Create the file mapping. + hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, size, (wchar_t*)nativeKey.utf16()); + setErrorString(function); + + // hand is valid when it already exists unlike unix so explicitly check + if (error == QSharedMemory::AlreadyExists || !hand) + return false; + + return true; +} + +bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode) +{ + // Grab a pointer to the memory block + int permissions = (mode == QSharedMemory::ReadOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS); + memory = (void *)MapViewOfFile(handle(), permissions, 0, 0, 0); + if (0 == memory) { + setErrorString(QLatin1String("QSharedMemory::attach")); + cleanHandle(); + return false; + } + + // Grab the size of the memory we have been given (a multiple of 4K on windows) + MEMORY_BASIC_INFORMATION info; + if (!VirtualQuery(memory, &info, sizeof(info))) { + // Windows doesn't set an error code on this one, + // it should only be a kernel memory error. + error = QSharedMemory::UnknownError; + errorString = QSharedMemory::tr("%1: size query failed").arg(QLatin1String("QSharedMemory::attach: ")); + return false; + } + size = info.RegionSize; + + return true; +} + +bool QSharedMemoryPrivate::detach() +{ + // umap memory + if (!UnmapViewOfFile(memory)) { + setErrorString(QLatin1String("QSharedMemory::detach")); + return false; + } + memory = 0; + size = 0; + + // close handle + return cleanHandle(); +} + +#endif //QT_NO_SHAREDMEMORY + + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qsignalmapper.cpp b/src/corelib/kernel/qsignalmapper.cpp new file mode 100644 index 0000000000..017aa72376 --- /dev/null +++ b/src/corelib/kernel/qsignalmapper.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qsignalmapper.h" +#ifndef QT_NO_SIGNALMAPPER +#include "qhash.h" +#include "qobject_p.h" + +QT_BEGIN_NAMESPACE + +class QSignalMapperPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QSignalMapper) +public: + void _q_senderDestroyed() { + Q_Q(QSignalMapper); + q->removeMappings(q->sender()); + } + QHash intHash; + QHash stringHash; + QHash widgetHash; + QHash objectHash; + +}; + + +/*! + \class QSignalMapper + \brief The QSignalMapper class bundles signals from identifiable senders. + + \ingroup objectmodel + + + This class collects a set of parameterless signals, and re-emits + them with integer, string or widget parameters corresponding to + the object that sent the signal. + + The class supports the mapping of particular strings or integers + with particular objects using setMapping(). The objects' signals + can then be connected to the map() slot which will emit the + mapped() signal with the string or integer associated with the + original signalling object. Mappings can be removed later using + removeMappings(). + + Example: Suppose we want to create a custom widget that contains + a group of buttons (like a tool palette). One approach is to + connect each button's \c clicked() signal to its own custom slot; + but in this example we want to connect all the buttons to a + single slot and parameterize the slot by the button that was + clicked. + + Here's the definition of a simple custom widget that has a single + signal, \c clicked(), which is emitted with the text of the button + that was clicked: + + \snippet doc/src/snippets/qsignalmapper/buttonwidget.h 0 + \snippet doc/src/snippets/qsignalmapper/buttonwidget.h 1 + + The only function that we need to implement is the constructor: + + \snippet doc/src/snippets/qsignalmapper/buttonwidget.cpp 0 + \snippet doc/src/snippets/qsignalmapper/buttonwidget.cpp 1 + \snippet doc/src/snippets/qsignalmapper/buttonwidget.cpp 2 + + A list of texts is passed to the constructor. A signal mapper is + constructed and for each text in the list a QPushButton is + created. We connect each button's \c clicked() signal to the + signal mapper's map() slot, and create a mapping in the signal + mapper from each button to the button's text. Finally we connect + the signal mapper's mapped() signal to the custom widget's \c + clicked() signal. When the user clicks a button, the custom + widget will emit a single \c clicked() signal whose argument is + the text of the button the user clicked. + + \sa QObject, QButtonGroup, QActionGroup +*/ + +/*! + Constructs a QSignalMapper with parent \a parent. +*/ +QSignalMapper::QSignalMapper(QObject* parent) + : QObject(*new QSignalMapperPrivate, parent) +{ +} + +#ifdef QT3_SUPPORT +/*! + \overload QSignalMapper() + \obsolete + */ +QSignalMapper::QSignalMapper(QObject *parent, const char *name) + : QObject(*new QSignalMapperPrivate, parent) +{ + setObjectName(QString::fromAscii(name)); +} +#endif + +/*! + Destroys the QSignalMapper. +*/ +QSignalMapper::~QSignalMapper() +{ +} + +/*! + Adds a mapping so that when map() is signalled from the given \a + sender, the signal mapped(\a id) is emitted. + + There may be at most one integer ID for each sender. + + \sa mapping() +*/ +void QSignalMapper::setMapping(QObject *sender, int id) +{ + Q_D(QSignalMapper); + d->intHash.insert(sender, id); + connect(sender, SIGNAL(destroyed()), this, SLOT(_q_senderDestroyed())); +} + +/*! + Adds a mapping so that when map() is signalled from the \a sender, + the signal mapped(\a text ) is emitted. + + There may be at most one text for each sender. +*/ +void QSignalMapper::setMapping(QObject *sender, const QString &text) +{ + Q_D(QSignalMapper); + d->stringHash.insert(sender, text); + connect(sender, SIGNAL(destroyed()), this, SLOT(_q_senderDestroyed())); +} + +/*! + Adds a mapping so that when map() is signalled from the \a sender, + the signal mapped(\a widget ) is emitted. + + There may be at most one widget for each sender. +*/ +void QSignalMapper::setMapping(QObject *sender, QWidget *widget) +{ + Q_D(QSignalMapper); + d->widgetHash.insert(sender, widget); + connect(sender, SIGNAL(destroyed()), this, SLOT(_q_senderDestroyed())); +} + +/*! + Adds a mapping so that when map() is signalled from the \a sender, + the signal mapped(\a object ) is emitted. + + There may be at most one object for each sender. +*/ +void QSignalMapper::setMapping(QObject *sender, QObject *object) +{ + Q_D(QSignalMapper); + d->objectHash.insert(sender, object); + connect(sender, SIGNAL(destroyed()), this, SLOT(_q_senderDestroyed())); +} + +/*! + Returns the sender QObject that is associated with the \a id. + + \sa setMapping() +*/ +QObject *QSignalMapper::mapping(int id) const +{ + Q_D(const QSignalMapper); + return d->intHash.key(id); +} + +/*! + \overload mapping() +*/ +QObject *QSignalMapper::mapping(const QString &id) const +{ + Q_D(const QSignalMapper); + return d->stringHash.key(id); +} + +/*! + \overload mapping() + + Returns the sender QObject that is associated with the \a widget. +*/ +QObject *QSignalMapper::mapping(QWidget *widget) const +{ + Q_D(const QSignalMapper); + return d->widgetHash.key(widget); +} + +/*! + \overload mapping() + + Returns the sender QObject that is associated with the \a object. +*/ +QObject *QSignalMapper::mapping(QObject *object) const +{ + Q_D(const QSignalMapper); + return d->objectHash.key(object); +} + +/*! + Removes all mappings for \a sender. + + This is done automatically when mapped objects are destroyed. +*/ +void QSignalMapper::removeMappings(QObject *sender) +{ + Q_D(QSignalMapper); + + d->intHash.remove(sender); + d->stringHash.remove(sender); + d->widgetHash.remove(sender); + d->objectHash.remove(sender); +} + +/*! + This slot emits signals based on which object sends signals to it. +*/ +void QSignalMapper::map() { map(sender()); } + +/*! + This slot emits signals based on the \a sender object. +*/ +void QSignalMapper::map(QObject *sender) +{ + Q_D(QSignalMapper); + if (d->intHash.contains(sender)) + emit mapped(d->intHash.value(sender)); + if (d->stringHash.contains(sender)) + emit mapped(d->stringHash.value(sender)); + if (d->widgetHash.contains(sender)) + emit mapped(d->widgetHash.value(sender)); + if (d->objectHash.contains(sender)) + emit mapped(d->objectHash.value(sender)); +} + + +/*! + \fn void QSignalMapper::mapped(int i) + + This signal is emitted when map() is signalled from an object that + has an integer mapping set. The object's mapped integer is passed + in \a i. + + \sa setMapping() +*/ + +/*! + \fn void QSignalMapper::mapped(const QString &text) + + This signal is emitted when map() is signalled from an object that + has a string mapping set. The object's mapped string is passed in + \a text. + + \sa setMapping() +*/ + +/*! + \fn void QSignalMapper::mapped(QWidget *widget) + + This signal is emitted when map() is signalled from an object that + has a widget mapping set. The object's mapped widget is passed in + \a widget. + + \sa setMapping() +*/ + +/*! + \fn void QSignalMapper::mapped(QObject *object) + + This signal is emitted when map() is signalled from an object that + has an object mapping set. The object provided by the map is passed in + \a object. + + \sa setMapping() +*/ + +QT_END_NAMESPACE + +#include "moc_qsignalmapper.cpp" + +#endif // QT_NO_SIGNALMAPPER + diff --git a/src/corelib/kernel/qsignalmapper.h b/src/corelib/kernel/qsignalmapper.h new file mode 100644 index 0000000000..ddf5c34e47 --- /dev/null +++ b/src/corelib/kernel/qsignalmapper.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QSIGNALMAPPER_H +#define QSIGNALMAPPER_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_SIGNALMAPPER +class QSignalMapperPrivate; + +class Q_CORE_EXPORT QSignalMapper : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QSignalMapper) +public: + explicit QSignalMapper(QObject *parent = 0); + ~QSignalMapper(); + + void setMapping(QObject *sender, int id); + void setMapping(QObject *sender, const QString &text); + void setMapping(QObject *sender, QWidget *widget); + void setMapping(QObject *sender, QObject *object); + void removeMappings(QObject *sender); + + QObject *mapping(int id) const; + QObject *mapping(const QString &text) const; + QObject *mapping(QWidget *widget) const; + QObject *mapping(QObject *object) const; + +Q_SIGNALS: + void mapped(int); + void mapped(const QString &); + void mapped(QWidget *); + void mapped(QObject *); + +public Q_SLOTS: + void map(); + void map(QObject *sender); + +private: + Q_DISABLE_COPY(QSignalMapper) + Q_PRIVATE_SLOT(d_func(), void _q_senderDestroyed()) + +#ifdef QT3_SUPPORT +public: + QT3_SUPPORT_CONSTRUCTOR QSignalMapper(QObject *parent, const char *name); +#endif +}; +#endif // QT_NO_SIGNALMAPPER + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSIGNALMAPPER_H diff --git a/src/corelib/kernel/qsocketnotifier.cpp b/src/corelib/kernel/qsocketnotifier.cpp new file mode 100644 index 0000000000..360bc372f0 --- /dev/null +++ b/src/corelib/kernel/qsocketnotifier.cpp @@ -0,0 +1,323 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qsocketnotifier.h" + +#include "qplatformdefs.h" + +#include "qabstracteventdispatcher.h" +#include "qcoreapplication.h" + +#include "qobject_p.h" +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QSocketNotifier + \brief The QSocketNotifier class provides support for monitoring + activity on a file descriptor. + + \ingroup network + \ingroup io + + The QSocketNotifier makes it possible to integrate Qt's event + loop with other event loops based on file descriptors. For + example, the \l{CORBA Framework} uses it to process CORBA + events. File descriptor action is detected in Qt's main event + loop (QCoreApplication::exec()). + + \target write notifiers + + Once you have opened a device using a low-level (usually + platform-specific) API, you can create a socket notifier to + monitor the file descriptor. The socket notifier is enabled by + default, i.e. it emits the activated() signal whenever a socket + event corresponding to its type occurs. Connect the activated() + signal to the slot you want to be called when an event + corresponding to your socket notifier's type occurs. + + There are three types of socket notifiers: read, write, and + exception. The type is described by the \l Type enum, and must be + specified when constructing the socket notifier. After + construction it can be determined using the type() function. Note + that if you need to monitor both reads and writes for the same + file descriptor, you must create two socket notifiers. Note also + that it is not possible to install two socket notifiers of the + same type (\l Read, \l Write, \l Exception) on the same socket. + + The setEnabled() function allows you to disable as well as enable + the socket notifier. It is generally advisable to explicitly + enable or disable the socket notifier, especially for write + notifiers. A disabled notifier ignores socket events (the same + effect as not creating the socket notifier). Use the isEnabled() + function to determine the notifier's current status. + + Finally, you can use the socket() function to retrieve the + socket identifier. Although the class is called QSocketNotifier, + it is normally used for other types of devices than sockets. + QTcpSocket and QUdpSocket provide notification through signals, so + there is normally no need to use a QSocketNotifier on them. + + \section1 Notes for Windows Users + + The socket passed to QSocketNotifier will become non-blocking, even if + it was created as a blocking socket. + The activated() signal is sometimes triggered by high general activity + on the host, even if there is nothing to read. A subsequent read from + the socket can then fail, the error indicating that there is no data + available (e.g., \c{WSAEWOULDBLOCK}). This is an operating system + limitation, and not a bug in QSocketNotifier. + + To ensure that the socket notifier handles read notifications correctly, + follow these steps when you receive a notification: + + \list 1 + \o Disable the notifier. + \o Read data from the socket. + \o Re-enable the notifier if you are interested in more data (such as after + having written a new command to a remote server). + \endlist + + To ensure that the socket notifier handles write notifications correctly, + follow these steps when you receive a notification: + + \list 1 + \o Disable the notifier. + \o Write as much data as you can (before \c EWOULDBLOCK is returned). + \o Re-enable notifier if you have more data to write. + \endlist + + \bold{Further information:} + On Windows, Qt always disables the notifier after getting a notification, + and only re-enables it if more data is expected. For example, if data is + read from the socket and it can be used to read more, or if reading or + writing is not possible because the socket would block, in which case + it is necessary to wait before attempting to read or write again. + + \sa QFile, QProcess, QTcpSocket, QUdpSocket +*/ + +/*! + \enum QSocketNotifier::Type + + This enum describes the various types of events that a socket + notifier can recognize. The type must be specified when + constructing the socket notifier. + + Note that if you need to monitor both reads and writes for the + same file descriptor, you must create two socket notifiers. Note + also that it is not possible to install two socket notifiers of + the same type (Read, Write, Exception) on the same socket. + + \value Read There is data to be read. + \value Write Data can be written. + \value Exception An exception has occurred. We recommend against using this. + + \sa QSocketNotifier(), type() +*/ + +/*! + Constructs a socket notifier with the given \a parent. It enables + the \a socket, and watches for events of the given \a type. + + It is generally advisable to explicitly enable or disable the + socket notifier, especially for write notifiers. + + \bold{Note for Windows users:} The socket passed to QSocketNotifier + will become non-blocking, even if it was created as a blocking socket. + + \sa setEnabled(), isEnabled() +*/ + +QSocketNotifier::QSocketNotifier(int socket, Type type, QObject *parent) + : QObject(parent) +{ + if (socket < 0) + qWarning("QSocketNotifier: Invalid socket specified"); + sockfd = socket; + sntype = type; + snenabled = true; + + Q_D(QObject); + if (!d->threadData->eventDispatcher) { + qWarning("QSocketNotifier: Can only be used with threads started with QThread"); + } else { + d->threadData->eventDispatcher->registerSocketNotifier(this); + } +} + +#ifdef QT3_SUPPORT +/*! + \obsolete + + Use the QSocketNotifier() constructor combined with the + QObject::setObjectName() function instead. + + \oldcode + QSocketNotifier *notifier = new QSocketNotifier(socket, type, parent, name); + \newcode + QSocketNotifier *notifier = new QSocketNotifier(socket, type, parent); + notifier->setObjectName(name); + \endcode +*/ + +QSocketNotifier::QSocketNotifier(int socket, Type type, QObject *parent, + const char *name) + : QObject(parent) +{ + setObjectName(QString::fromAscii(name)); + if (socket < 0) + qWarning("QSocketNotifier: Invalid socket specified"); + sockfd = socket; + sntype = type; + snenabled = true; + + Q_D(QObject); + if (!d->threadData->eventDispatcher) { + qWarning("QSocketNotifier: Can only be used with threads started with QThread"); + } else { + d->threadData->eventDispatcher->registerSocketNotifier(this); + } +} +#endif +/*! + Destroys this socket notifier. +*/ + +QSocketNotifier::~QSocketNotifier() +{ + setEnabled(false); +} + + +/*! + \fn void QSocketNotifier::activated(int socket) + + This signal is emitted whenever the socket notifier is enabled and + a socket event corresponding to its \l {Type}{type} occurs. + + The socket identifier is passed in the \a socket parameter. + + \sa type(), socket() +*/ + + +/*! + \fn int QSocketNotifier::socket() const + + Returns the socket identifier specified to the constructor. + + \sa type() +*/ + +/*! + \fn Type QSocketNotifier::type() const + + Returns the socket event type specified to the constructor. + + \sa socket() +*/ + + +/*! + \fn bool QSocketNotifier::isEnabled() const + + Returns true if the notifier is enabled; otherwise returns false. + + \sa setEnabled() +*/ + +/*! + If \a enable is true, the notifier is enabled; otherwise the notifier + is disabled. + + The notifier is enabled by default, i.e. it emits the activated() + signal whenever a socket event corresponding to its + \l{type()}{type} occurs. If it is disabled, it ignores socket + events (the same effect as not creating the socket notifier). + + Write notifiers should normally be disabled immediately after the + activated() signal has been emitted + + \sa isEnabled(), activated() +*/ + +void QSocketNotifier::setEnabled(bool enable) +{ + if (sockfd < 0) + return; + if (snenabled == enable) // no change + return; + snenabled = enable; + + Q_D(QObject); + if (!d->threadData->eventDispatcher) // perhaps application/thread is shutting down + return; + if (snenabled) + d->threadData->eventDispatcher->registerSocketNotifier(this); + else + d->threadData->eventDispatcher->unregisterSocketNotifier(this); +} + + +/*!\reimp +*/ +bool QSocketNotifier::event(QEvent *e) +{ + // Emits the activated() signal when a QEvent::SockAct is + // received. + if (e->type() == QEvent::ThreadChange) { + if (snenabled) { + QMetaObject::invokeMethod(this, "setEnabled", Qt::QueuedConnection, + Q_ARG(bool, snenabled)); + setEnabled(false); + } + } + QObject::event(e); // will activate filters + if (e->type() == QEvent::SockAct) { + emit activated(sockfd); + return true; + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qsocketnotifier.h b/src/corelib/kernel/qsocketnotifier.h new file mode 100644 index 0000000000..13a5160e3c --- /dev/null +++ b/src/corelib/kernel/qsocketnotifier.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QSOCKETNOTIFIER_H +#define QSOCKETNOTIFIER_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class Q_CORE_EXPORT QSocketNotifier : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QObject) + +public: + enum Type { Read, Write, Exception }; + + QSocketNotifier(int socket, Type, QObject *parent = 0); +#ifdef QT3_SUPPORT + QT3_SUPPORT_CONSTRUCTOR QSocketNotifier(int socket, Type, QObject *parent, const char *name); +#endif + ~QSocketNotifier(); + + inline int socket() const { return sockfd; } + inline Type type() const { return sntype; } + + inline bool isEnabled() const { return snenabled; } + +public Q_SLOTS: + void setEnabled(bool); + +Q_SIGNALS: + void activated(int socket); + +protected: + bool event(QEvent *); + +private: + Q_DISABLE_COPY(QSocketNotifier) + + int sockfd; + Type sntype; + bool snenabled; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSOCKETNOTIFIER_H diff --git a/src/corelib/kernel/qsystemerror.cpp b/src/corelib/kernel/qsystemerror.cpp new file mode 100644 index 0000000000..18622c7a08 --- /dev/null +++ b/src/corelib/kernel/qsystemerror.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 +#include "qsystemerror_p.h" +#if !defined(Q_OS_WINCE) +# include +# if defined(Q_CC_MSVC) +# include +# endif +#endif +#ifdef Q_OS_WIN +#include +#endif + +QT_BEGIN_NAMESPACE + +#if !defined(Q_OS_WIN) && !defined(QT_NO_THREAD) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_QNX) && \ + defined(_POSIX_THREAD_SAFE_FUNCTIONS) && _POSIX_VERSION >= 200112L +namespace { + // There are two incompatible versions of strerror_r: + // a) the XSI/POSIX.1 version, which returns an int, + // indicating success or not + // b) the GNU version, which returns a char*, which may or may not + // be the beginning of the buffer we used + // The GNU libc manpage for strerror_r says you should use the the XSI + // version in portable code. However, it's impossible to do that if + // _GNU_SOURCE is defined so we use C++ overloading to decide what to do + // depending on the return type + static inline QString fromstrerror_helper(int, const QByteArray &buf) + { + return QString::fromLocal8Bit(buf); + } + static inline QString fromstrerror_helper(const char *str, const QByteArray &) + { + return QString::fromLocal8Bit(str); + } +} +#endif + +static QString standardLibraryErrorString(int errorCode) +{ + const char *s = 0; + QString ret; + switch (errorCode) { + case 0: + break; + case EACCES: + s = QT_TRANSLATE_NOOP("QIODevice", "Permission denied"); + break; + case EMFILE: + s = QT_TRANSLATE_NOOP("QIODevice", "Too many open files"); + break; + case ENOENT: + s = QT_TRANSLATE_NOOP("QIODevice", "No such file or directory"); + break; + case ENOSPC: + s = QT_TRANSLATE_NOOP("QIODevice", "No space left on device"); + break; + default: { + #if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) && _POSIX_VERSION >= 200112L && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_QNX) + QByteArray buf(1024, '\0'); + ret = fromstrerror_helper(strerror_r(errorCode, buf.data(), buf.size()), buf); + #else + ret = QString::fromLocal8Bit(strerror(errorCode)); + #endif + break; } + } + if (s) { + // ######## this breaks moc build currently + // ret = QCoreApplication::translate("QIODevice", s); + ret = QString::fromLatin1(s); + } + return ret.trimmed(); +} + +#ifdef Q_OS_WIN +static QString windowsErrorString(int errorCode) +{ + QString ret; + wchar_t *string = 0; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&string, + 0, + NULL); + ret = QString::fromWCharArray(string); + LocalFree((HLOCAL)string); + + if (ret.isEmpty() && errorCode == ERROR_MOD_NOT_FOUND) + ret = QString::fromLatin1("The specified module could not be found."); + return ret; +} +#endif + +#ifdef Q_OS_SYMBIAN +static QString symbianErrorString(int errorCode) +{ + switch (errorCode) { + case KErrNotFound: + return QLatin1String("not found"); + case KErrCancel: + return QLatin1String("cancelled"); + case KErrNoMemory: + return QLatin1String("out of memory"); + case KErrNotSupported: + return QLatin1String("not supported"); + case KErrBadHandle: + return QLatin1String("bad handle"); //KERN-EXEC 0 panic is more likely + case KErrAlreadyExists: + return QLatin1String("already exists"); + case KErrPathNotFound: + return QLatin1String("path not found"); + case KErrInUse: + return QLatin1String("in use"); + case KErrNotReady: + return QLatin1String("not ready (e.g. FS dismounted, network down)"); + case KErrCorrupt: + return QLatin1String("corrupt"); + case KErrAccessDenied: + return QLatin1String("access denied"); + case KErrLocked: + return QLatin1String("locked"); + case KErrWrite: + return QLatin1String("incomplete write error"); + case KErrDisMounted: + return QLatin1String("file system dismounted during operation"); //i.e. a forcible dismount was done while we had files open + case KErrEof: + return QLatin1String("end of file"); + case KErrDiskFull: + return QLatin1String("no space in file system"); + case KErrBadName: + return QLatin1String("invalid filename"); + case KErrTimedOut: + return QLatin1String("timed out"); + case KErrBadDescriptor: + return QLatin1String("bad descriptor (passed address on stack to async call?)"); + case KErrAbort: + return QLatin1String("aborted"); + case KErrTooBig: + return QLatin1String("too big"); //e.g. trying to open a >2GB file with 32 bit API + case KErrBadPower: + return QLatin1String("insufficient power"); + case KErrDirFull: + return QLatin1String("no space in directory table"); + case KErrHardwareNotAvailable: + return QLatin1String("hardware not available"); + case KErrSessionClosed: + return QLatin1String("session closed"); + case KErrPermissionDenied: + return QLatin1String("permission denied"); + default: + return QString(QLatin1String("symbian error %1")).arg(errorCode); + } +} +#endif + +QString QSystemError::toString() +{ + switch(errorScope) { + case NativeError: +#if defined (Q_OS_WIN) + return windowsErrorString(errorCode); +#elif defined (Q_OS_SYMBIAN) + return symbianErrorString(errorCode); +#else + //unix: fall through as native and standard library are the same +#endif + case StandardLibraryError: + return standardLibraryErrorString(errorCode); + default: + qWarning("invalid error scope"); + //fall through + case NoError: + return QLatin1String("No error"); + } +} + +QT_END_NAMESPACE + diff --git a/src/corelib/kernel/qsystemerror_p.h b/src/corelib/kernel/qsystemerror_p.h new file mode 100644 index 0000000000..e96e85a7ad --- /dev/null +++ b/src/corelib/kernel/qsystemerror_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QSYSTEMERROR_P_H +#define QSYSTEMERROR_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 + +QT_BEGIN_NAMESPACE + +class Q_CORE_EXPORT QSystemError +{ +public: + enum ErrorScope + { + NoError, + StandardLibraryError, + NativeError + }; + + inline QSystemError(int error, ErrorScope scope); + inline QSystemError(); + + QString toString(); + inline ErrorScope scope(); + inline int error(); + + //data members + int errorCode; + ErrorScope errorScope; +}; + +QSystemError::QSystemError(int error, QSystemError::ErrorScope scope) +: errorCode(error), errorScope(scope) +{ + +} + +QSystemError::QSystemError() +: errorCode(0), errorScope(NoError) +{ + +} + +QSystemError::ErrorScope QSystemError::scope() +{ + return errorScope; +} + +int QSystemError::error() +{ + return errorCode; +} + + +QT_END_NAMESPACE + +#endif // QSYSTEMERROR_P_H diff --git a/src/corelib/kernel/qsystemsemaphore.cpp b/src/corelib/kernel/qsystemsemaphore.cpp new file mode 100644 index 0000000000..80dccc2184 --- /dev/null +++ b/src/corelib/kernel/qsystemsemaphore.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qsystemsemaphore.h" +#include "qsystemsemaphore_p.h" +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SYSTEMSEMAPHORE + +/*! + \class QSystemSemaphore + \since 4.4 + + \brief The QSystemSemaphore class provides a general counting system semaphore. + + A semaphore is a generalization of a mutex. While a mutex can be + locked only once, a semaphore can be acquired multiple times. + Typically, a semaphore is used to protect a certain number of + identical resources. + + Like its lighter counterpart QSemaphore, a QSystemSemaphore can be + accessed from multiple \l {QThread} {threads}. Unlike QSemaphore, a + QSystemSemaphore can also be accessed from multiple \l {QProcess} + {processes}. This means QSystemSemaphore is a much heavier class, so + if your application doesn't need to access your semaphores across + multiple processes, you will probably want to use QSemaphore. + + Semaphores support two fundamental operations, acquire() and release(): + + acquire() tries to acquire one resource. If there isn't a resource + available, the call blocks until a resource becomes available. Then + the resource is acquired and the call returns. + + release() releases one resource so it can be acquired by another + process. The function can also be called with a parameter n > 1, + which releases n resources. + + A system semaphore is created with a string key that other processes + can use to use the same semaphore. + + Example: Create a system semaphore + \snippet doc/src/snippets/code/src_corelib_kernel_qsystemsemaphore.cpp 0 + + A typical application of system semaphores is for controlling access + to a circular buffer shared by a producer process and a consumer + processes. + + \section1 Platform-Specific Behavior + + When using this class, be aware of the following platform + differences: + + \bold{Windows:} QSystemSemaphore does not own its underlying system + semaphore. Windows owns it. This means that when all instances of + QSystemSemaphore for a particular key have been destroyed, either by + having their destructors called, or because one or more processes + crash, Windows removes the underlying system semaphore. + + \bold{Unix:} + + \list + \o QSystemSemaphore owns the underlying system semaphore + in Unix systems. This means that the last process having an instance of + QSystemSemaphore for a particular key must remove the underlying + system semaphore in its destructor. If the last process crashes + without running the QSystemSemaphore destructor, Unix does not + automatically remove the underlying system semaphore, and the + semaphore survives the crash. A subsequent process that constructs a + QSystemSemaphore with the same key will then be given the existing + system semaphore. In that case, if the QSystemSemaphore constructor + has specified its \l {QSystemSemaphore::AccessMode} {access mode} as + \l {QSystemSemaphore::} {Open}, its initial resource count will not + be reset to the one provided but remain set to the value it received + in the crashed process. To protect against this, the first process + to create a semaphore for a particular key (usually a server), must + pass its \l {QSystemSemaphore::AccessMode} {access mode} as \l + {QSystemSemaphore::} {Create}, which will force Unix to reset the + resource count in the underlying system semaphore. + + \o When a process using QSystemSemaphore terminates for + any reason, Unix automatically reverses the effect of all acquire + operations that were not released. Thus if the process acquires a + resource and then exits without releasing it, Unix will release that + resource. + + \o Symbian: QSystemSemaphore behaves the same as Windows semaphores. + In other words, the operating system owns the semaphore and ignores + QSystemSemaphore::AccessMode. + + \endlist + + \sa QSharedMemory, QSemaphore + */ + +/*! + Requests a system semaphore for the specified \a key. The parameters + \a initialValue and \a mode are used according to the following + rules, which are system dependent. + + In Unix, if the \a mode is \l {QSystemSemaphore::} {Open} and the + system already has a semaphore identified by \a key, that semaphore + is used, and the semaphore's resource count is not changed, i.e., \a + initialValue is ignored. But if the system does not already have a + semaphore identified by \a key, it creates a new semaphore for that + key and sets its resource count to \a initialValue. + + In Unix, if the \a mode is \l {QSystemSemaphore::} {Create} and the + system already has a semaphore identified by \a key, that semaphore + is used, and its resource count is set to \a initialValue. If the + system does not already have a semaphore identified by \a key, it + creates a new semaphore for that key and sets its resource count to + \a initialValue. + + In Windows and in Symbian, \a mode is ignored, and the system always tries to + create a semaphore for the specified \a key. If the system does not + already have a semaphore identified as \a key, it creates the + semaphore and sets its resource count to \a initialValue. But if the + system already has a semaphore identified as \a key it uses that + semaphore and ignores \a initialValue. + + The \l {QSystemSemaphore::AccessMode} {mode} parameter is only used + in Unix systems to handle the case where a semaphore survives a + process crash. In that case, the next process to allocate a + semaphore with the same \a key will get the semaphore that survived + the crash, and unless \a mode is \l {QSystemSemaphore::} {Create}, + the resource count will not be reset to \a initialValue but will + retain the initial value it had been given by the crashed process. + + \sa acquire(), key() + */ +QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode) + : d(new QSystemSemaphorePrivate) +{ + setKey(key, initialValue, mode); +} + +/*! + The destructor destroys the QSystemSemaphore object, but the + underlying system semaphore is not removed from the system unless + this instance of QSystemSemaphore is the last one existing for that + system semaphore. + + Two important side effects of the destructor depend on the system. + In Windows, if acquire() has been called for this semaphore but not + release(), release() will not be called by the destructor, nor will + the resource be released when the process exits normally. This would + be a program bug which could be the cause of a deadlock in another + process trying to acquire the same resource. In Unix, acquired + resources that are not released before the destructor is called are + automatically released when the process exits. +*/ +QSystemSemaphore::~QSystemSemaphore() +{ + d->cleanHandle(); +} + +/*! + \enum QSystemSemaphore::AccessMode + + This enum is used by the constructor and setKey(). Its purpose is to + enable handling the problem in Unix implementations of semaphores + that survive a crash. In Unix, when a semaphore survives a crash, we + need a way to force it to reset its resource count, when the system + reuses the semaphore. In Windows and in Symbian, where semaphores can't survive a + crash, this enum has no effect. + + \value Open If the semaphore already exists, its initial resource + count is not reset. If the semaphore does not already exist, it is + created and its initial resource count set. + + \value Create QSystemSemaphore takes ownership of the semaphore and + sets its resource count to the requested value, regardless of + whether the semaphore already exists by having survived a crash. + This value should be passed to the constructor, when the first + semaphore for a particular key is constructed and you know that if + the semaphore already exists it could only be because of a crash. In + Windows and in Symbian, where a semaphore can't survive a crash, Create and Open + have the same behavior. +*/ + +/*! + This function works the same as the constructor. It reconstructs + this QSystemSemaphore object. If the new \a key is different from + the old key, calling this function is like calling the destructor of + the semaphore with the old key, then calling the constructor to + create a new semaphore with the new \a key. The \a initialValue and + \a mode parameters are as defined for the constructor. + + \sa QSystemSemaphore(), key() + */ +void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode) +{ + if (key == d->key && mode == Open) + return; + d->error = NoError; + d->errorString = QString(); +#if !defined(Q_OS_WIN) && !defined(Q_OS_SYMBIAN) + // optimization to not destroy/create the file & semaphore + if (key == d->key && mode == Create && d->createdSemaphore && d->createdFile) { + d->initialValue = initialValue; + d->unix_key = -1; + d->handle(mode); + return; + } +#endif + d->cleanHandle(); + d->key = key; + d->initialValue = initialValue; + // cache the file name so it doesn't have to be generated all the time. + d->fileName = d->makeKeyFileName(); + d->handle(mode); +} + +/*! + Returns the key assigned to this system semaphore. The key is the + name by which the semaphore can be accessed from other processes. + + \sa setKey() + */ +QString QSystemSemaphore::key() const +{ + return d->key; +} + +/*! + Acquires one of the resources guarded by this semaphore, if there is + one available, and returns true. If all the resources guarded by this + semaphore have already been acquired, the call blocks until one of + them is released by another process or thread having a semaphore + with the same key. + + If false is returned, a system error has occurred. Call error() + to get a value of QSystemSemaphore::SystemSemaphoreError that + indicates which error occurred. + + \sa release() + */ +bool QSystemSemaphore::acquire() +{ + return d->modifySemaphore(-1); +} + +/*! + Releases \a n resources guarded by the semaphore. Returns true + unless there is a system error. + + Example: Create a system semaphore having five resources; acquire + them all and then release them all. + + \snippet doc/src/snippets/code/src_corelib_kernel_qsystemsemaphore.cpp 1 + + This function can also "create" resources. For example, immediately + following the sequence of statements above, suppose we add the + statement: + + \snippet doc/src/snippets/code/src_corelib_kernel_qsystemsemaphore.cpp 2 + + Ten new resources are now guarded by the semaphore, in addition to + the five that already existed. You would not normally use this + function to create more resources. + + \sa acquire() + */ +bool QSystemSemaphore::release(int n) +{ + if (n == 0) + return true; + if (n < 0) { + qWarning("QSystemSemaphore::release: n is negative."); + return false; + } + return d->modifySemaphore(n); +} + +/*! + Returns a value indicating whether an error occurred, and, if so, + which error it was. + + \sa errorString() + */ +QSystemSemaphore::SystemSemaphoreError QSystemSemaphore::error() const +{ + return d->error; +} + +/*! + \enum QSystemSemaphore::SystemSemaphoreError + + \value NoError No error occurred. + + \value PermissionDenied The operation failed because the caller + didn't have the required permissions. + + \value KeyError The operation failed because of an invalid key. + + \value AlreadyExists The operation failed because a system + semaphore with the specified key already existed. + + \value NotFound The operation failed because a system semaphore + with the specified key could not be found. + + \value OutOfResources The operation failed because there was + not enough memory available to fill the request. + + \value UnknownError Something else happened and it was bad. +*/ + +/*! + Returns a text description of the last error that occurred. If + error() returns an \l {QSystemSemaphore::SystemSemaphoreError} {error + value}, call this function to get a text string that describes the + error. + + \sa error() + */ +QString QSystemSemaphore::errorString() const +{ + return d->errorString; +} + +#endif // QT_NO_SYSTEMSEMAPHORE + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qsystemsemaphore.h b/src/corelib/kernel/qsystemsemaphore.h new file mode 100644 index 0000000000..305d311fda --- /dev/null +++ b/src/corelib/kernel/qsystemsemaphore.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QSYSTEMSEMAPHORE_H +#define QSYSTEMSEMAPHORE_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_SYSTEMSEMAPHORE + +class QSystemSemaphorePrivate; + +class Q_CORE_EXPORT QSystemSemaphore +{ + +public: + enum AccessMode + { + Open, + Create + }; + + enum SystemSemaphoreError + { + NoError, + PermissionDenied, + KeyError, + AlreadyExists, + NotFound, + OutOfResources, + UnknownError + }; + + QSystemSemaphore(const QString &key, int initialValue = 0, AccessMode mode = Open); + ~QSystemSemaphore(); + + void setKey(const QString &key, int initialValue = 0, AccessMode mode = Open); + QString key() const; + + bool acquire(); + bool release(int n = 1); + + SystemSemaphoreError error() const; + QString errorString() const; + +private: + Q_DISABLE_COPY(QSystemSemaphore) + QScopedPointer d; +}; + +#endif // QT_NO_SYSTEMSEMAPHORE + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSYSTEMSEMAPHORE_H + diff --git a/src/corelib/kernel/qsystemsemaphore_p.h b/src/corelib/kernel/qsystemsemaphore_p.h new file mode 100644 index 0000000000..f8f5043e23 --- /dev/null +++ b/src/corelib/kernel/qsystemsemaphore_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QSYSTEMSEMAPHORE_P_H +#define QSYSTEMSEMAPHORE_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 "qsystemsemaphore.h" + +#ifndef QT_NO_SYSTEMSEMAPHORE + +#include "qsharedmemory_p.h" +#ifndef Q_OS_WINCE +# include +#endif + +#ifdef Q_OS_SYMBIAN +class RSemaphore; +#endif + +QT_BEGIN_NAMESPACE + +class QSystemSemaphorePrivate +{ + +public: + QSystemSemaphorePrivate(); + + QString makeKeyFileName() + { + return QSharedMemoryPrivate::makePlatformSafeKey(key, QLatin1String("qipc_systemsem_")); + } + +#ifdef Q_OS_WIN + HANDLE handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open); + void setErrorString(const QString &function); +#elif defined(Q_OS_SYMBIAN) + int handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open); + void setErrorString(const QString &function,int err = 0); +#else + key_t handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open); + void setErrorString(const QString &function); +#endif + void cleanHandle(); + bool modifySemaphore(int count); + + QString key; + QString fileName; + int initialValue; +#ifdef Q_OS_WIN + HANDLE semaphore; + HANDLE semaphoreLock; +#elif defined(Q_OS_SYMBIAN) + RSemaphore semaphore; +#else + int semaphore; + bool createdFile; + bool createdSemaphore; + key_t unix_key; +#endif + QString errorString; + QSystemSemaphore::SystemSemaphoreError error; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_SYSTEMSEMAPHORE + +#endif // QSYSTEMSEMAPHORE_P_H + diff --git a/src/corelib/kernel/qsystemsemaphore_symbian.cpp b/src/corelib/kernel/qsystemsemaphore_symbian.cpp new file mode 100644 index 0000000000..0d257b8f5e --- /dev/null +++ b/src/corelib/kernel/qsystemsemaphore_symbian.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qsystemsemaphore.h" +#include "qsystemsemaphore_p.h" +#include "qcoreapplication.h" +#include + +#include "qcore_symbian_p.h" +#include +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SYSTEMSEMAPHORE + +QSystemSemaphorePrivate::QSystemSemaphorePrivate() : + error(QSystemSemaphore::NoError) +{ +} + +void QSystemSemaphorePrivate::setErrorString(const QString &function, int err) +{ + if (err == KErrNone){ + return; + } + switch(err){ + case KErrAlreadyExists: + errorString = QCoreApplication::tr("%1: already exists", "QSystemSemaphore").arg(function); + error = QSystemSemaphore::AlreadyExists; + break; + case KErrNotFound: + errorString = QCoreApplication::tr("%1: does not exist", "QSystemSemaphore").arg(function); + error = QSystemSemaphore::NotFound; + break; + case KErrNoMemory: + case KErrInUse: + errorString = QCoreApplication::tr("%1: out of resources", "QSystemSemaphore").arg(function); + error = QSystemSemaphore::OutOfResources; + break; + case KErrPermissionDenied: + errorString = QCoreApplication::tr("%1: permission denied", "QSystemSemaphore").arg(function); + error = QSystemSemaphore::PermissionDenied; + break; +default: + errorString = QCoreApplication::tr("%1: unknown error %2", "QSystemSemaphore").arg(function).arg(err); + error = QSystemSemaphore::UnknownError; + } + +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << errorString << "key" << key; +#endif +} + +int QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode) +{ + if (semaphore.Handle()) { + return semaphore.Handle(); + } + + // don't allow making handles on empty keys + if (key.isEmpty()) + return 0; + + TPtrC name(qt_QString2TPtrC(fileName)); + int err = KErrAlreadyExists; + int tryCount = 10; + // Sort out race conditions by retrying several times until existing handle is acquired. + // Sometimes opening can fail inexplicably with KErrPermissionDenied many times in a row. + while (err != KErrNoMemory && err != KErrNone && tryCount-- >= 0) { + err = semaphore.CreateGlobal(name, initialValue, EOwnerProcess); + if (err != KErrNoMemory && err != KErrNone) + err = semaphore.OpenGlobal(name,EOwnerProcess); + } + if (err){ + setErrorString(QLatin1String("QSystemSemaphore::handle"),err); + return 0; + } + return semaphore.Handle(); +} + +void QSystemSemaphorePrivate::cleanHandle() +{ + semaphore.Close(); +} + +bool QSystemSemaphorePrivate::modifySemaphore(int count) +{ + if (0 == handle()) + return false; + + if (count > 0) { + semaphore.Signal(count); + } else { + semaphore.Wait(); + } + return true; +} + +#endif //QT_NO_SYSTEMSEMAPHORE + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qsystemsemaphore_unix.cpp b/src/corelib/kernel/qsystemsemaphore_unix.cpp new file mode 100644 index 0000000000..55b65b75de --- /dev/null +++ b/src/corelib/kernel/qsystemsemaphore_unix.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qsystemsemaphore.h" +#include "qsystemsemaphore_p.h" + +#include +#include +#include + +#ifndef QT_NO_SYSTEMSEMAPHORE + +#include +#include +#include +#include +#include + +#include "private/qcore_unix_p.h" + +// OpenBSD 4.2 doesn't define EIDRM, see BUGS section: +// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2 +#if defined(Q_OS_OPENBSD) && !defined(EIDRM) +#define EIDRM EINVAL +#endif + +QT_BEGIN_NAMESPACE + +QSystemSemaphorePrivate::QSystemSemaphorePrivate() : + semaphore(-1), createdFile(false), + createdSemaphore(false), unix_key(-1), error(QSystemSemaphore::NoError) +{ +} + +void QSystemSemaphorePrivate::setErrorString(const QString &function) +{ + // EINVAL is handled in functions so they can give better error strings + switch (errno) { + case EPERM: + case EACCES: + errorString = QCoreApplication::translate("QSystemSemaphore", "%1: permission denied").arg(function); + error = QSystemSemaphore::PermissionDenied; + break; + case EEXIST: + errorString = QCoreApplication::translate("QSystemSemaphore", "%1: already exists").arg(function); + error = QSystemSemaphore::AlreadyExists; + break; + case ENOENT: + errorString = QCoreApplication::translate("QSystemSemaphore", "%1: does not exist").arg(function); + error = QSystemSemaphore::NotFound; + break; + case ERANGE: + case ENOSPC: + errorString = QCoreApplication::translate("QSystemSemaphore", "%1: out of resources").arg(function); + error = QSystemSemaphore::OutOfResources; + break; + default: + errorString = QCoreApplication::translate("QSystemSemaphore", "%1: unknown error %2").arg(function).arg(errno); + error = QSystemSemaphore::UnknownError; +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << errorString << "key" << key << "errno" << errno << EINVAL; +#endif + } +} + +/*! + \internal + + Setup unix_key + */ +key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode) +{ + if (key.isEmpty()){ + errorString = QCoreApplication::tr("%1: key is empty", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); + error = QSystemSemaphore::KeyError; + return -1; + } + + // ftok requires that an actual file exists somewhere + if (-1 != unix_key) + return unix_key; + + // Create the file needed for ftok + int built = QSharedMemoryPrivate::createUnixKeyFile(fileName); + if (-1 == built) { + errorString = QCoreApplication::tr("%1: unable to make key", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); + error = QSystemSemaphore::KeyError; + return -1; + } + createdFile = (1 == built); + + // Get the unix key for the created file + unix_key = ftok(QFile::encodeName(fileName).constData(), 'Q'); + if (-1 == unix_key) { + errorString = QCoreApplication::tr("%1: ftok failed", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle:")); + error = QSystemSemaphore::KeyError; + return -1; + } + + // Get semaphore + semaphore = semget(unix_key, 1, 0666 | IPC_CREAT | IPC_EXCL); + if (-1 == semaphore) { + if (errno == EEXIST) + semaphore = semget(unix_key, 1, 0666 | IPC_CREAT); + if (-1 == semaphore) { + setErrorString(QLatin1String("QSystemSemaphore::handle")); + cleanHandle(); + return -1; + } + } else { + createdSemaphore = true; + // Force cleanup of file, it is possible that it can be left over from a crash + createdFile = true; + } + + if (mode == QSystemSemaphore::Create) { + createdSemaphore = true; + createdFile = true; + } + + // Created semaphore so initialize its value. + if (createdSemaphore && initialValue >= 0) { + qt_semun init_op; + init_op.val = initialValue; + if (-1 == semctl(semaphore, 0, SETVAL, init_op)) { + setErrorString(QLatin1String("QSystemSemaphore::handle")); + cleanHandle(); + return -1; + } + } + + return unix_key; +} + +/*! + \internal + + Cleanup the unix_key + */ +void QSystemSemaphorePrivate::cleanHandle() +{ + unix_key = -1; + + // remove the file if we made it + if (createdFile) { + QFile::remove(fileName); + createdFile = false; + } + + if (createdSemaphore) { + if (-1 != semaphore) { + if (-1 == semctl(semaphore, 0, IPC_RMID, 0)) { + setErrorString(QLatin1String("QSystemSemaphore::cleanHandle")); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << QLatin1String("QSystemSemaphore::cleanHandle semctl failed."); +#endif + } + semaphore = -1; + } + createdSemaphore = false; + } +} + +/*! + \internal + */ +bool QSystemSemaphorePrivate::modifySemaphore(int count) +{ + if (-1 == handle()) + return false; + + struct sembuf operation; + operation.sem_num = 0; + operation.sem_op = count; + operation.sem_flg = SEM_UNDO; + + register int res; + EINTR_LOOP(res, semop(semaphore, &operation, 1)); + if (-1 == res) { + // If the semaphore was removed be nice and create it and then modifySemaphore again + if (errno == EINVAL || errno == EIDRM) { + semaphore = -1; + cleanHandle(); + handle(); + return modifySemaphore(count); + } + setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore")); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << QLatin1String("QSystemSemaphore::modify failed") << count << semctl(semaphore, 0, GETVAL) << errno << EIDRM << EINVAL; +#endif + return false; + } + + return true; +} + + +QT_END_NAMESPACE + +#endif // QT_NO_SYSTEMSEMAPHORE diff --git a/src/corelib/kernel/qsystemsemaphore_win.cpp b/src/corelib/kernel/qsystemsemaphore_win.cpp new file mode 100644 index 0000000000..fad50f2ffb --- /dev/null +++ b/src/corelib/kernel/qsystemsemaphore_win.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qsystemsemaphore.h" +#include "qsystemsemaphore_p.h" +#include "qcoreapplication.h" +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_SYSTEMSEMAPHORE + +QSystemSemaphorePrivate::QSystemSemaphorePrivate() : + semaphore(0), error(QSystemSemaphore::NoError) +{ +} + +void QSystemSemaphorePrivate::setErrorString(const QString &function) +{ + BOOL windowsError = GetLastError(); + if (windowsError == 0) + return; + + switch (windowsError) { + case ERROR_NO_SYSTEM_RESOURCES: + case ERROR_NOT_ENOUGH_MEMORY: + error = QSystemSemaphore::OutOfResources; + errorString = QCoreApplication::translate("QSystemSemaphore", "%1: out of resources").arg(function); + break; + case ERROR_ACCESS_DENIED: + error = QSystemSemaphore::PermissionDenied; + errorString = QCoreApplication::translate("QSystemSemaphore", "%1: permission denied").arg(function); + break; + default: + errorString = QCoreApplication::translate("QSystemSemaphore", "%1: unknown error %2").arg(function).arg(windowsError); + error = QSystemSemaphore::UnknownError; +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << errorString << "key" << key; +#endif + } +} + +HANDLE QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode) +{ + // don't allow making handles on empty keys + if (key.isEmpty()) + return 0; + + // Create it if it doesn't already exists. + if (semaphore == 0) { + semaphore = CreateSemaphore(0, initialValue, MAXLONG, (wchar_t*)fileName.utf16()); + if (semaphore == NULL) + setErrorString(QLatin1String("QSystemSemaphore::handle")); + } + + return semaphore; +} + +void QSystemSemaphorePrivate::cleanHandle() +{ + if (semaphore && !CloseHandle(semaphore)) { +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << QLatin1String("QSystemSemaphorePrivate::CloseHandle: sem failed"); +#endif + } + semaphore = 0; +} + +bool QSystemSemaphorePrivate::modifySemaphore(int count) +{ + if (0 == handle()) + return false; + + if (count > 0) { + if (0 == ReleaseSemaphore(semaphore, count, 0)) { + setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore")); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << QLatin1String("QSystemSemaphore::modifySemaphore ReleaseSemaphore failed"); +#endif + return false; + } + } else { + if (WAIT_OBJECT_0 != WaitForSingleObject(semaphore, INFINITE)) { + setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore")); +#if defined QSYSTEMSEMAPHORE_DEBUG + qDebug() << QLatin1String("QSystemSemaphore::modifySemaphore WaitForSingleObject failed"); +#endif + return false; + } + } + + return true; +} + +#endif //QT_NO_SYSTEMSEMAPHORE + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qtcore_eval.cpp b/src/corelib/kernel/qtcore_eval.cpp new file mode 100644 index 0000000000..79e4cca3e8 --- /dev/null +++ b/src/corelib/kernel/qtcore_eval.cpp @@ -0,0 +1,571 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 +#include +#include +#include +#include + +#include "stdio.h" +#include "stdlib.h" + +QT_BEGIN_NAMESPACE + +#include "qconfig_eval.cpp" + +static const char boilerplate_unsuported[] = + "\nQt %1 Evaluation License\n" + "Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).\n" + "All rights reserved.\n\n" + "This trial version may only be used for evaluation purposes\n" + "and will shut down after 120 minutes.\n" + "Registered to:\n" + " Licensee: %2\n\n" + "The evaluation expires in %4 days\n\n" + "Contact http://qt.nokia.com/about/contact-us for pricing and purchasing information.\n"; + +static const char boilerplate_supported[] = + "\nQt %1 Evaluation License\n" + "Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).\n" + "All rights reserved.\n\n" + "This trial version may only be used for evaluation purposes\n" + "Registered to:\n" + " Licensee: %2\n\n" + "The evaluation expires in %4 days\n\n" + "Contact http://qt.nokia.com/about/contact-us for pricing and purchasing information.\n"; + +static const char boilerplate_expired[] = + "This software is using the trial version of the Qt GUI toolkit.\n" + "The trial period has expired. If you need more time to\n" + "evaluate Qt, or if you have any questions about Qt, contact us\n" + "at: http://qt.nokia.com/about/contact-us.\n\n"; + +static const char will_shutdown_1min[] = + "\nThe evaluation of Qt will SHUT DOWN in 1 minute.\n" + "Contact http://qt.nokia.com/about/contact-us for pricing and purchasing information.\n"; + +static const char will_shutdown_now[] = + "\nThe evaluation of Qt has now reached its automatic\n" + "timeout and will shut down.\n" + "Contact http://qt.nokia.com/about/contact-us for pricing and purchasing information.\n"; + +static int qt_eval_is_supported() +{ + const volatile char *const license_key = qt_eval_key_data + 12; + + // fast fail + if (!qt_eval_key_data[0] || !*license_key) + return -1; + + // is this an unsupported evaluation? + const volatile char *typecode = license_key; + int field = 2; + for ( ; field && *typecode; ++typecode) + if (*typecode == '-') + --field; + + if (!field && typecode[1] == '4' && typecode[2] == 'M') { + if (typecode[0] == 'Q') + return 0; + else if (typecode[0] == 'R' || typecode[0] == 'Z') + return 1; + } + return -1; +} + +static int qt_eval_days_left() +{ + if (qt_eval_is_supported() < 0) + return -2; + + QDate today = QDate::currentDate(); + QDate build = QLibraryInfo::buildDate(); + return qMax(-1, today.daysTo(build) + 30); +} + +static QString qt_eval_string() +{ + const char *msg; + switch (qt_eval_is_supported()) { + case 0: + msg = boilerplate_unsuported; + break; + case 1: + msg = boilerplate_supported; + break; + default: + return QString(); + msg = 0; + } + + return QString::fromLatin1(msg) + .arg(QLatin1String(QT_VERSION_STR)) + .arg(QLibraryInfo::licensee()) + .arg(qt_eval_days_left()); +} + +#define WARN_TIMEOUT 60 * 1000 * 119 +#define KILL_DELAY 60 * 1000 * 1 + +class QCoreFuriCuri : public QObject +{ +public: + + int warn; + int kill; + + QCoreFuriCuri() : QObject(), warn(-1), kill(-1) + { + if (!qt_eval_is_supported()) { + warn = startTimer(WARN_TIMEOUT); + kill = 0; + } + } + + void timerEvent(QTimerEvent *e) { + if (e->timerId() == warn) { + killTimer(warn); + fprintf(stderr, "%s\n", will_shutdown_1min); + kill = startTimer(KILL_DELAY); + } else if (e->timerId() == kill) { + fprintf(stderr, "%s\n", will_shutdown_now); + QCoreApplication::instance()->quit(); + } + } +}; + +#if defined(QT_BUILD_CORE_LIB) || defined (QT_BOOTSTRAPPED) + +void qt_core_eval_init(uint type) +{ + if (!type) + return; // GUI app + + switch (qt_eval_days_left()) { + case -2: + return; + + case -1: + fprintf(stderr, "%s\n", boilerplate_expired); + if (type == 0) { + // if we're a console app only. + exit(0); + } + + default: + fprintf(stderr, "%s\n", qPrintable(qt_eval_string())); + if (type == 0) { + Q_UNUSED(new QCoreFuriCuri()); + } + } +} +#endif + +#ifdef QT_BUILD_GUI_LIB + +QT_BEGIN_INCLUDE_NAMESPACE +#include +#include +#include +#include +#include +#include +#include +QT_END_INCLUDE_NAMESPACE + + +static const char * const qtlogo_eval_xpm[] = { +/* columns rows colors chars-per-pixel */ +"46 55 174 2", +" c #002E02", +". c #00370D", +"X c #003A0E", +"o c #003710", +"O c #013C13", +"+ c #043E1A", +"@ c #084F0A", +"# c #0B520C", +"$ c #054413", +"% c #0C4C17", +"& c #07421D", +"* c #09451D", +"= c #0D491E", +"- c #125515", +"; c #13541A", +": c #17591B", +"> c #1B5C1D", +", c #1F611F", +"< c #20621E", +"1 c #337B1E", +"2 c #0B4521", +"3 c #0F4923", +"4 c #114B24", +"5 c #154D2A", +"6 c #175323", +"7 c #1C5924", +"8 c #1C532F", +"9 c #1E5432", +"0 c #245936", +"q c #265938", +"w c #295C3B", +"e c #246324", +"r c #266823", +"t c #2A6C24", +"y c #276628", +"u c #2D7026", +"i c #327427", +"p c #367927", +"a c #37782A", +"s c #397C2A", +"d c #2E613E", +"f c #336C37", +"g c #2F6040", +"h c #356545", +"j c #3C6B4E", +"k c #3F6C51", +"l c #406E4F", +"z c #406D52", +"x c #477457", +"c c #497557", +"v c #4B7857", +"b c #517B5E", +"n c #3C8423", +"m c #3E812C", +"M c #53A61D", +"N c #41862C", +"B c #458A2D", +"V c #498F2D", +"C c #479324", +"Z c #489226", +"A c #4D952C", +"S c #478B30", +"D c #488C30", +"F c #4D9232", +"G c #509632", +"H c #549A33", +"J c #589F35", +"K c #56A526", +"L c #57A821", +"P c #5BAA27", +"I c #57A32A", +"U c #5CA72E", +"Y c #5DAB2A", +"T c #5CA336", +"R c #60AD2E", +"E c #63B12D", +"W c #65AF35", +"Q c #62A53F", +"! c #65AE39", +"~ c #66B036", +"^ c #6AB437", +"/ c #67B138", +"( c #6AB339", +") c #6DB838", +"_ c #70BA3C", +"` c #4D8545", +"' c #4E8942", +"] c #548851", +"[ c #6FAF4A", +"{ c #6DB243", +"} c #71B546", +"| c #70B840", +" . c #73B648", +".. c #79BA4E", +"X. c #7CBB53", +"o. c #598266", +"O. c #62886D", +"+. c #6A8F75", +"@. c #6B9173", +"#. c #70937A", +"$. c #799F79", +"%. c #7BAF66", +"&. c #81BD5B", +"*. c #85BF60", +"=. c #85AC7F", +"-. c #8DBA7B", +";. c #87C061", +":. c #8AC364", +">. c #8DC46A", +",. c #90C56E", +"<. c #93C771", +"1. c #96CA73", +"2. c #9ACB7C", +"3. c #9FD07D", +"4. c #779981", +"5. c #7F9F89", +"6. c #809F88", +"7. c #82A18B", +"8. c #86A192", +"9. c #8DA994", +"0. c #8FA998", +"q. c #94AF9B", +"w. c #97B991", +"e. c #97B19E", +"r. c #9DB6A3", +"t. c #A3BCA7", +"y. c #A6BCAB", +"u. c #A9BEB1", +"i. c #9ECD81", +"p. c #A2CF85", +"a. c #A5D284", +"s. c #A6D189", +"d. c #A9D28E", +"f. c #ABD491", +"g. c #B1D797", +"h. c #B1D699", +"j. c #B5D89E", +"k. c #ADC5AC", +"l. c #B1CAAE", +"z. c #B9DAA3", +"x. c #BDDDA8", +"c. c #ADC1B4", +"v. c #B2C6B6", +"b. c #B5C6BC", +"n. c #B6C9BA", +"m. c #BCD1BA", +"M. c #C6E1B4", +"N. c #CDE5BD", +"B. c #C2D2C6", +"V. c #CADEC2", +"C. c #C6D3CC", +"Z. c #C8D7CB", +"A. c #CEDAD2", +"S. c #D2DDD4", +"D. c #D3E9C6", +"F. c #D7EBC9", +"G. c #D9EBCD", +"H. c #DEEED4", +"J. c #D6E0D9", +"K. c #DAE4DC", +"L. c #E0EFD7", +"P. c #E5F2DD", +"I. c #DFE8E0", +"U. c #E4EBE5", +"Y. c #E9EFEA", +"T. c #EDF4EB", +"R. c #F0FAE6", +"E. c #F1F8EC", +"W. c #EDF0F0", +"Q. c #F4F7F3", +"!. c #F6F9F4", +"~. c #F8FAF7", +"^. c #FEFEFE", +"/. c None", +/* pixels */ +"/././././.c h ' Q / W _ &.p././././././././././././././././././././././././././././././././.", +"/././.4 O % Z ~ ~ W ~ W R U R R ( X.>.p././././././././././././././././././././././././././.", +"/./.. * = J _ ~ ~ ~ ~ ~ / / / / W W U P P U W .;.2././././././././././././././././././././.", +"/.= = & a ) W ~ ~ ~ ~ ~ / W / ~ ~ ~ ^ ( ( ^ ~ R R U P Y ~ .;.2././././././././././././././.", +"O.O = = T ^ W ~ ~ ~ ~ ~ ~ W W / W ~ ~ ~ ~ ~ ~ ~ ( ( ( ( ~ W Y Y Y Y W { &.1././././././././.", +"0 = * 7 ~ ~ ~ ~ ~ ~ ~ ~ ~ / / W ~ ~ ~ ~ ~ ~ ~ ~ W W W ~ ~ ~ ~ ( ( ( W W R U P U W { X.1.f./.", +"= = & e ^ W ~ ~ ~ ~ ~ ~ ~ ~ / / ~ ~ ~ ~ ~ ~ ~ ~ W ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ ( ( / ~ W R U U Y ", +"= = & e ^ W ~ ~ ~ ~ ~ ~ ~ ~ W W ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W ( W ~ ~ ~ ^ ^ ( ", +"= = * e ^ W ~ ~ ~ ~ ~ ~ / W / W ! ( / ~ W ^ ( ( ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W ~ W W ~ ~ ~ ~ ~ ~ ", +"= = & e ^ ! ~ ~ ~ ~ ~ ~ W W ^ _ ~ K Y W W R P Y W ( ~ ~ ~ ~ ~ ~ ~ W / ~ ~ ~ ^ W ~ ~ ~ ~ ~ ~ ", +"= = & e ^ W ~ ~ ~ ~ ~ ~ W ) W 1 ` w.V.L.H.D.z.,.~ Y ^ ~ ~ ~ ~ ~ W ~ ~ ~ ( ~ W W ~ ~ ~ ~ ~ ~ ", +"= = & e ^ W ~ ~ ~ ~ ~ W ) V = 8.~.^.^.^.^.^.^.^.U.<.Y ~ ~ ~ ~ ~ W W ! ~ Y W ^ W ~ ~ ~ ~ ~ ~ ", +"= = & e ^ W ~ ~ ~ ~ W ^ B O u.^.~.^.^.^.^.~.~.^.^.^.h.Y ^ ~ ~ ^ F $ k.R.G.1.Y / ~ ~ ~ ~ ~ ~ ", +"= = & e ^ ~ ~ ~ / W ( J X 7.^.~.^.^.^.^.^.^.^.^.^.^.^.s.Y / W ) a 2 U.^.^.d.U ( ~ ~ ~ ~ ~ ~ ", +"= = & e ^ W / ~ ~ ~ ^ > w ~.^.^.^.^.^.F.%.v c.^.^.^.^.~.X.W ~ ^ > h ^.^.^.d.P ( ~ ~ ~ ~ ~ ~ ", +"= = & e ^ W ~ ~ W ^ H o e.^.^.^.^.^.G.Y E n . y.^.^.^.^.M.Y ( ! $ @.^.~.^.f.U ( / ~ ~ W ~ ~ ", +"= = & e ^ W ~ W ! ) t 4 U.^.^.^.^.^.>.U ( _ , 9 ~.^.^.^.~...^ A y.^.~.^.s.M W Y ~ ~ ~ ~ ~ ", +"= 3 & e ^ W ~ ( ^ ( $ c ^.^.^.^.^.E.) ~ ~ ^ S o n.^.^.^.^.=.- l.v.Y.^.^.^.M.:.:.X.~ ~ ~ ~ ~ ", +"= = & e ^ ! W W ( J X 7.^.^.^.^.^.F.Y ( W ^ T X 6.^.^.~.^.c.. J.^.^.^.^.^.^.^.^.P.~ ~ ~ ~ ~ ", +"= = & r ^ W / W ) B o v.^.~.^.^.^.M.U / ~ ~ ! $ o.^.^.^.^.K.* S.^.^.^.^.^.^.^.^.P.~ ~ ~ ~ ~ ", +"= = & e ^ ! ~ W ) a + S.^.^.^.^.^.z.P ( W ~ ( % z ^.^.^.^.~.f t.U.^.^.^.^.~.^.^.P.~ ~ ~ ~ ~ ", +"* = & e ^ W ~ W ) t 3 Y.^.^.^.^.^.f.P ( ~ ~ ^ ; h ^.^.^.^.^.:.@ j ^.^.^.^.h.{ X.&.~ ~ ~ ~ ~ ", +"3 = & e ^ W ~ ~ ^ e 8 Q.^.^.^.^.^.s.P ~ ~ W ^ > 0 ~.^.^.^.^.1.# z ^.^.^.^.d.L W R ~ ~ ~ ~ ~ ", +"= = & e ^ W ~ ~ ^ > q ~.^.^.^.^.^.p.U ^ ~ W ) e 9 ~.^.^.^.^.3.# k ^.^.^.^.f.Y ( / ~ ~ ~ ~ ~ ", +"= = & e ^ W / W ^ > w ~.^.^.^.^.^.i.Y / ~ W ^ e 8 Q.^.^.^.^.a.# z ^.^.^.^.f.Y / ~ ~ ~ ~ ~ ~ ", +"= = & e ^ W / W ^ > w ^.^.^.^.^.^.2.Y / ~ ~ ) e 8 Q.^.^.^.^.s.# z ^.^.^.^.d.P ( ~ ~ ~ ~ ~ ~ ", +"= = & e ^ W W W ^ > q ^.^.^.^.^.^.p.Y / ~ ~ ^ e 9 Q.^.^.^.^.a.@ z ^.^.^.^.f.U / ~ ~ ~ ~ ~ ~ ", +"= = & e ^ W / W ) 7 9 Q.^.^.^.^.^.a.P / ~ W ) , 9 Q.^.^.^.^.3.# z ^.^.~.^.f.P ^ ~ ~ ~ ~ ~ ~ ", +"= = & e ^ W / W ) r 5 T.^.^.^.^.^.d.Y / ~ W ) > q ~.^.^.^.^.1.# k ^.^.^.^.f.Y ( ~ ~ ~ ~ ~ ~ ", +"= = & e ^ / / W ) i 2 I.^.^.^.^.^.h.P ( ~ W ( > g ^.^.^.^.^.:.# z ^.^.^.^.f.P / ~ ~ ~ ~ ~ ~ ", +"= = & e ( W / W ) m O Z.^.^.^.^.^.x.P / ~ ~ ( ; j ^.^.^.^.~.&.- k ^.^.~.^.f.P / ~ ~ ~ ~ ~ ~ ", +"= = & e ( W / W ) F o y.^.~.^.^.^.N.U ( ~ ~ W $ b ^.^.^.^.R._ - k ^.^.^.^.f.Y ( ~ ~ ~ ~ ~ ~ ", +"= = & e ^ W ~ ~ ^ J X 4.^.^.^.^.^.L.~ ~ W ^ T X #.^.^.^.^.F.~ ; j ^.^.^.^.f.U ( ~ ~ ~ ~ ~ ~ ", +"= = & e ^ ~ ~ ~ / ^ % l ^.^.^.^.^.!. .R ^ ^ G . r.^.~.^.^.j.E : j ^.^.^.^.f.P ) ( ~ ~ ~ ~ ~ ", +"= = & e ^ W ~ ~ W ) u = U.^.^.^.^.^.1.Y ! ) a & K.^.^.^.^.;.~ : j ^.^.~.^.z.M I I / ~ ~ W ~ ", +"= = & e ( W ~ ~ W ( G . q.^.^.^.^.^.D.U ^ ! X o.^.^.^.^.P.~ ^ > g ^.^.^.^.E.-.$.m.X.W ~ ~ ~ ", +"= = & e ^ / ~ ~ ^ ! ( > w ~.^.^.^.^.^.h.T > j T.^.^.~.^.a.Y _ i 3 U.^.^.^.^.^.^.^.X.R ~ ~ ~ ", +"= = & e ^ / ~ ~ W W ^ H . 9.^.~.^.^.^.^.K.C.~.^.^.^.^.H.W W ^ T . q.^.~.^.^.^.^.^.X.R ~ ~ ~ ", +"= = + e ^ W / ~ W W W ) m + B.^.~.^.^.^.^.^.^.^.^.^.E.X.Y ( W ^ B 6 y.^.^.^.E.D.2.( ~ ~ ~ ~ ", +"= = * e ^ ! / ! W ^ W W ) a 4 b.^.^.^.^.^.^.^.^.^.P...Y ( ! W ! ^ W Z [ *.X.{ Y U ~ ~ ~ ~ ~ ", +"= = & e ( W ~ ~ W / W / W ) A < +.A.~.^.^.^.^.!.p.W R ~ ~ ~ ~ ~ W / ) E U W W / ^ ~ ~ ~ ~ ~ ", +"= = & e ^ W ~ ~ / W / / / W ( _ Z X 6.^.^.^.^.E.W ~ ^ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ / ~ ~ ~ ~ ~ ~ ~ ~ ", +"= = & e ^ ~ ~ ~ W W / W ~ ~ ~ ~ ) ; h ^.^.^.^.^.d.M U ~ / ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W ", +"= = & e ^ W ~ ~ ^ W W / ~ ~ ~ W ) p + S.^.^.^.^.~.M.f. .W ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ .", +"= = & e ^ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W ( T O +.^.~.^.^.^.^.^.&.Y ( ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W ( Y 2.", +"= = & e ( W ~ ~ ~ ~ ~ ~ ~ ~ ~ / W ) N + b.^.^.^.^.^.^.&.R ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W /.", +"= = & e ^ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W ^ N 7 r.W.^.^.^.!.X.W ~ ~ W ~ W ~ ~ ~ ~ ~ ~ / ( ( K p./.", +"= = & e ( W ~ ~ W ~ ~ ~ ~ ~ ~ ~ ~ ~ W ( W C Q &.:.X.| ~ ~ ~ ~ W ~ / ~ ( / ( ~ W E U P 1././.", +"= = + e ^ / / / ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W / ) ^ R Y W W ~ ~ ( / ( / W R Y Y U R ( X.,././././.", +"= = * e ( / ~ / ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ W W W ! ( ( ( W W E U P Y W ( X.,.d./././././././././.", +"= = * e ( W ~ ~ ~ ~ W ! ~ W ~ W ~ ( ( / ^ W W U Y P W ( X.,.d./././././././././././././././.", +"8 $ * e ( W ~ ~ ~ ! ( ( ( / ( W R Y Y Y R ( X.>.d./././././././././././././././././././././.", +"/.d . y ^ / / / ( W Y Y P P W ( X.>.d./././././././././././././././././././././././././././.", +"/./.h : ^ R R R W ( X.<.f./././././././././././././././././././././././././././././././././.", +"/././.] _ *.3./././././././././././././././././././././././././././././././././././././././." +}; + +class EvalMessageBox : public QDialog +{ +public: + EvalMessageBox(bool expired) + { + setWindowTitle(QLatin1String(" ")); + + QString str = qt_eval_string(); + if (expired) { + str = QLatin1String(boilerplate_expired); + } else { + str = qt_eval_string(); + } + str = str.trimmed(); + + QFrame *border = new QFrame(this); + + QLabel *pixmap_label = new QLabel(border); + pixmap_label->setPixmap(qtlogo_eval_xpm); + pixmap_label->setAlignment(Qt::AlignTop); + + QLabel *text_label = new QLabel(str, border); + + QHBoxLayout *pm_and_text_layout = new QHBoxLayout(); + pm_and_text_layout->addWidget(pixmap_label); + pm_and_text_layout->addWidget(text_label); + + QVBoxLayout *master_layout = new QVBoxLayout(border); + master_layout->addLayout(pm_and_text_layout); + + QVBoxLayout *border_layout = new QVBoxLayout(this); + border_layout->setMargin(0); + border_layout->addWidget(border); + + if (expired) { + QPushButton *cmd = new QPushButton(QLatin1String("OK"), border); + cmd->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + cmd->setDefault(true); + + QHBoxLayout *button_layout = new QHBoxLayout(); + master_layout->addLayout(button_layout); + button_layout->addWidget(cmd); + + connect(cmd, SIGNAL(clicked()), this, SLOT(close())); + } else { + border->setFrameShape(QFrame::WinPanel); + border->setFrameShadow(QFrame::Raised); + setParent(parentWidget(), Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); + QTimer::singleShot(7000, this, SLOT(close())); + setAttribute(Qt::WA_DeleteOnClose); + } + + setFixedSize(sizeHint()); + } +}; + +class QGuiFuriCuri : public QCoreFuriCuri +{ +public: + void timerEvent(QTimerEvent *e) { + if (e->timerId() == warn) { + killTimer(warn); + QMessageBox::information(0, QLatin1String("Automatic Timeout"), QLatin1String(will_shutdown_1min)); + kill = startTimer(KILL_DELAY); + } else if (e->timerId() == kill) { + killTimer(kill); + QMessageBox::information(0, QLatin1String("Automatic Timeout"), QLatin1String(will_shutdown_now)); + qApp->quit(); + } + } +}; + + +void qt_gui_eval_init(uint) +{ + switch (qt_eval_days_left()) { + case -2: + return; + + case -1: { + EvalMessageBox box(true); + box.exec(); + ::exit(0); + } + + default: { + EvalMessageBox *box = new EvalMessageBox(false); + box->show(); + Q_UNUSED(new QGuiFuriCuri()); + } + } +} + +static QString qt_eval_title_prefix() +{ + return QLatin1String("[Qt Evaluation] "); +} + +QString qt_eval_adapt_window_title(const QString &title) +{ + if (qt_eval_days_left() == -2) + return title; + return qt_eval_title_prefix() + title; +} + +void qt_eval_init_widget(QWidget *w) +{ + if (qt_eval_days_left() == -2) + return; + if (w->isTopLevel()) { + QString windowTitle = w->windowTitle(); + if (windowTitle.isEmpty()) { + w->setWindowTitle(QLatin1String(" ")); + } else if (!windowTitle.startsWith(qt_eval_title_prefix())) { + qt_eval_adapt_window_title(windowTitle); + } + } +} +#endif + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp new file mode 100644 index 0000000000..3efeda229d --- /dev/null +++ b/src/corelib/kernel/qtimer.cpp @@ -0,0 +1,394 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qtimer.h" +#include "qabstracteventdispatcher.h" +#include "qcoreapplication.h" +#include "qobject_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QTimer + \brief The QTimer class provides repetitive and single-shot timers. + + \ingroup events + + + The QTimer class provides a high-level programming interface for + timers. To use it, create a QTimer, connect its timeout() signal + to the appropriate slots, and call start(). From then on it will + emit the timeout() signal at constant intervals. + + Example for a one second (1000 millisecond) timer (from the + \l{widgets/analogclock}{Analog Clock} example): + + \snippet examples/widgets/analogclock/analogclock.cpp 4 + \snippet examples/widgets/analogclock/analogclock.cpp 5 + \snippet examples/widgets/analogclock/analogclock.cpp 6 + + From then on, the \c update() slot is called every second. + + You can set a timer to time out only once by calling + setSingleShot(true). You can also use the static + QTimer::singleShot() function to call a slot after a specified + interval: + + \snippet doc/src/snippets/timers/timers.cpp 3 + + In multithreaded applications, you can use QTimer in any thread + that has an event loop. To start an event loop from a non-GUI + thread, use QThread::exec(). Qt uses the timer's + \l{QObject::thread()}{thread affinity} to determine which thread + will emit the \l{QTimer::}{timeout()} signal. Because of this, you + must start and stop the timer in its thread; it is not possible to + start a timer from another thread. + + As a special case, a QTimer with a timeout of 0 will time out as + soon as all the events in the window system's event queue have + been processed. This can be used to do heavy work while providing + a snappy user interface: + + \snippet doc/src/snippets/timers/timers.cpp 4 + \snippet doc/src/snippets/timers/timers.cpp 5 + \snippet doc/src/snippets/timers/timers.cpp 6 + + \c processOneThing() will from then on be called repeatedly. It + should be written in such a way that it always returns quickly + (typically after processing one data item) so that Qt can deliver + events to widgets and stop the timer as soon as it has done all + its work. This is the traditional way of implementing heavy work + in GUI applications; multithreading is now becoming available on + more and more platforms, and we expect that zero-millisecond + QTimers will gradually be replaced by \l{QThread}s. + + \section1 Accuracy and Timer Resolution + + Timers will never time out earlier than the specified timeout value + and they are not guaranteed to time out at the exact value specified. + In many situations, they may time out late by a period of time that + depends on the accuracy of the system timers. + + The accuracy of timers depends on the underlying operating system + and hardware. Most platforms support a resolution of 1 millisecond, + though the accuracy of the timer will not equal this resolution + in many real-world situations. + + If Qt is unable to deliver the requested number of timer clicks, + it will silently discard some. + + \section1 Alternatives to QTimer + + An alternative to using QTimer is to call QObject::startTimer() + for your object and reimplement the QObject::timerEvent() event + handler in your class (which must inherit QObject). The + disadvantage is that timerEvent() does not support such + high-level features as single-shot timers or signals. + + Another alternative to using QTimer is to use QBasicTimer. It is + typically less cumbersome than using QObject::startTimer() + directly. See \l{Timers} for an overview of all three approaches. + + Some operating systems limit the number of timers that may be + used; Qt tries to work around these limitations. + + \sa QBasicTimer, QTimerEvent, QObject::timerEvent(), Timers, + {Analog Clock Example}, {Wiggly Example} +*/ + + +static const int INV_TIMER = -1; // invalid timer id + +/*! + Constructs a timer with the given \a parent. +*/ + +QTimer::QTimer(QObject *parent) + : QObject(parent), id(INV_TIMER), inter(0), del(0), single(0), nulltimer(0) +{ +} + + +#ifdef QT3_SUPPORT +/*! + Constructs a timer called \a name, with a \a parent. +*/ + +QTimer::QTimer(QObject *parent, const char *name) + : QObject(parent), id(INV_TIMER), single(0), nulltimer(0) +{ + setObjectName(QString::fromAscii(name)); +} +#endif + +/*! + Destroys the timer. +*/ + +QTimer::~QTimer() +{ + if (id != INV_TIMER) // stop running timer + stop(); +} + + +/*! + \fn void QTimer::timeout() + + This signal is emitted when the timer times out. + + \sa interval, start(), stop() +*/ + +/*! + \property QTimer::active + \since 4.3 + + This boolean property is true if the timer is running; otherwise + false. +*/ + +/*! + \fn bool QTimer::isActive() const + + Returns true if the timer is running (pending); otherwise returns + false. +*/ + +/*! + \fn int QTimer::timerId() const + + Returns the ID of the timer if the timer is running; otherwise returns + -1. +*/ + + +/*! \overload start() + + Starts or restarts the timer with the timeout specified in \l interval. + + If \l singleShot is true, the timer will be activated only once. +*/ +void QTimer::start() +{ + if (id != INV_TIMER) // stop running timer + stop(); + nulltimer = (!inter && single); + id = QObject::startTimer(inter); +} + +/*! + Starts or restarts the timer with a timeout interval of \a msec + milliseconds. +*/ +void QTimer::start(int msec) +{ + inter = msec; + start(); +} + + +#ifdef QT3_SUPPORT +/*! \overload start() + + Call setSingleShot(\a sshot) and start(\a msec) instead. +*/ + +int QTimer::start(int msec, bool sshot) +{ + if (id >=0 && nulltimer && !msec && sshot) + return id; + stop(); + setInterval(msec); + setSingleShot(sshot); + start(); + return timerId(); +} +#endif + + +/*! + Stops the timer. + + \sa start() +*/ + +void QTimer::stop() +{ + if (id != INV_TIMER) { + QObject::killTimer(id); + id = INV_TIMER; + } +} + + +/*! + \reimp +*/ +void QTimer::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == id) { + if (single) + stop(); + emit timeout(); + } +} + +class QSingleShotTimer : public QObject +{ + Q_OBJECT + int timerId; +public: + ~QSingleShotTimer(); + QSingleShotTimer(int msec, QObject *r, const char * m); +Q_SIGNALS: + void timeout(); +protected: + void timerEvent(QTimerEvent *); +}; + +QSingleShotTimer::QSingleShotTimer(int msec, QObject *receiver, const char *member) + : QObject(QAbstractEventDispatcher::instance()) +{ + connect(this, SIGNAL(timeout()), receiver, member); + timerId = startTimer(msec); +} + +QSingleShotTimer::~QSingleShotTimer() +{ + if (timerId > 0) + killTimer(timerId); +} + +void QSingleShotTimer::timerEvent(QTimerEvent *) +{ + // need to kill the timer _before_ we emit timeout() in case the + // slot connected to timeout calls processEvents() + if (timerId > 0) + killTimer(timerId); + timerId = -1; + emit timeout(); + + // we would like to use delete later here, but it feels like a + // waste to post a new event to handle this event, so we just unset the flag + // and explicitly delete... + qDeleteInEventHandler(this); +} + +QT_BEGIN_INCLUDE_NAMESPACE +#include "qtimer.moc" +QT_END_INCLUDE_NAMESPACE + +/*! + \reentrant + This static function calls a slot after a given time interval. + + It is very convenient to use this function because you do not need + to bother with a \link QObject::timerEvent() timerEvent\endlink or + create a local QTimer object. + + Example: + \snippet doc/src/snippets/code/src_corelib_kernel_qtimer.cpp 0 + + This sample program automatically terminates after 10 minutes + (600,000 milliseconds). + + The \a receiver is the receiving object and the \a member is the + slot. The time interval is \a msec milliseconds. + + \sa start() +*/ + +void QTimer::singleShot(int msec, QObject *receiver, const char *member) +{ + if (receiver && member) { + if (msec == 0) { + // special code shortpath for 0-timers + const char* bracketPosition = strchr(member, '('); + if (!bracketPosition || !(member[0] >= '0' && member[0] <= '3')) { + qWarning("QTimer::singleShot: Invalid slot specification"); + return; + } + QByteArray methodName(member+1, bracketPosition - 1 - member); // extract method name + QMetaObject::invokeMethod(receiver, methodName.constData(), Qt::QueuedConnection); + return; + } + (void) new QSingleShotTimer(msec, receiver, member); + } +} + +/*! + \property QTimer::singleShot + \brief whether the timer is a single-shot timer + + A single-shot timer fires only once, non-single-shot timers fire + every \l interval milliseconds. + + \sa interval, singleShot() +*/ + +/*! + \property QTimer::interval + \brief the timeout interval in milliseconds + + The default value for this property is 0. A QTimer with a timeout + interval of 0 will time out as soon as all the events in the window + system's event queue have been processed. + + Setting the interval of an active timer changes its timerId(). + + \sa singleShot +*/ +void QTimer::setInterval(int msec) +{ + inter = msec; + if (id != INV_TIMER) { // create new timer + QObject::killTimer(id); // restart timer + id = QObject::startTimer(msec); + } +} + +/*! \fn void QTimer::changeInterval(int msec) + + Use setInterval(msec) or start(msec) instead. +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qtimer.h b/src/corelib/kernel/qtimer.h new file mode 100644 index 0000000000..a7470848ee --- /dev/null +++ b/src/corelib/kernel/qtimer.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QTIMER_H +#define QTIMER_H + +#ifndef QT_NO_QOBJECT + +#include // conceptual inheritance +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class Q_CORE_EXPORT QTimer : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool singleShot READ isSingleShot WRITE setSingleShot) + Q_PROPERTY(int interval READ interval WRITE setInterval) + Q_PROPERTY(bool active READ isActive) +public: + explicit QTimer(QObject *parent = 0); +#ifdef QT3_SUPPORT + QT3_SUPPORT_CONSTRUCTOR QTimer(QObject *parent, const char *name); +#endif + ~QTimer(); + + inline bool isActive() const { return id >= 0; } + int timerId() const { return id; } + + void setInterval(int msec); + int interval() const { return inter; } + + inline void setSingleShot(bool singleShot); + inline bool isSingleShot() const { return single; } + + static void singleShot(int msec, QObject *receiver, const char *member); + +public Q_SLOTS: + void start(int msec); + + void start(); + void stop(); + +#ifdef QT3_SUPPORT + inline QT_MOC_COMPAT void changeInterval(int msec) { start(msec); } + QT_MOC_COMPAT int start(int msec, bool sshot); +#endif + +Q_SIGNALS: + void timeout(); + +protected: + void timerEvent(QTimerEvent *); + +private: + Q_DISABLE_COPY(QTimer) + + inline int startTimer(int){ return -1;} + inline void killTimer(int){} + + int id, inter, del; + uint single : 1; + uint nulltimer : 1; +}; + +inline void QTimer::setSingleShot(bool asingleShot) { single = asingleShot; } + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_QOBJECT + +#endif // QTIMER_H diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp new file mode 100644 index 0000000000..22b06a56a6 --- /dev/null +++ b/src/corelib/kernel/qtranslator.cpp @@ -0,0 +1,979 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qtranslator.h" + +#ifndef QT_NO_TRANSLATION + +#include "qfileinfo.h" +#include "qstring.h" +#include "qstringlist.h" +#include "qcoreapplication.h" +#include "qcoreapplication_p.h" +#include "qdatastream.h" +#include "qfile.h" +#include "qmap.h" +#include "qalgorithms.h" +#include "qhash.h" +#include "qtranslator_p.h" +#include "qlocale.h" + +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) && !defined(Q_OS_INTEGRITY) +#define QT_USE_MMAP +#include "private/qcore_unix_p.h" +#endif + +// most of the headers below are already included in qplatformdefs.h +// also this lacks Large File support but that's probably irrelevant +#if defined(QT_USE_MMAP) +// for mmap +#include +#include +#endif + +#include + +#include "qobject_p.h" + +QT_BEGIN_NAMESPACE + +enum Tag { Tag_End = 1, Tag_SourceText16, Tag_Translation, Tag_Context16, Tag_Obsolete1, + Tag_SourceText, Tag_Context, Tag_Comment, Tag_Obsolete2 }; +/* +$ mcookie +3cb86418caef9c95cd211cbf60a1bddd +$ +*/ + +// magic number for the file +static const int MagicLength = 16; +static const uchar magic[MagicLength] = { + 0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95, + 0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd +}; + +static bool match(const uchar* found, const char* target, uint len) +{ + // catch the case if \a found has a zero-terminating symbol and \a len includes it. + // (normalize it to be without the zero-terminating symbol) + if (len > 0 && found[len-1] == '\0') + --len; + return (memcmp(found, target, len) == 0 && target[len] == '\0'); +} + +static uint elfHash(const char *name) +{ + const uchar *k; + uint h = 0; + uint g; + + if (name) { + k = (const uchar *) name; + while (*k) { + h = (h << 4) + *k++; + if ((g = (h & 0xf0000000)) != 0) + h ^= g >> 24; + h &= ~g; + } + } + if (!h) + h = 1; + return h; +} + +static int numerusHelper(int n, const uchar *rules, int rulesSize) +{ +#define CHECK_RANGE \ + do { \ + if (i >= rulesSize) \ + return -1; \ + } while (0) + + int result = 0; + int i = 0; + + if (rulesSize == 0) + return 0; + + for (;;) { + bool orExprTruthValue = false; + + for (;;) { + bool andExprTruthValue = true; + + for (;;) { + bool truthValue = true; + + CHECK_RANGE; + int opcode = rules[i++]; + + int leftOperand = n; + if (opcode & Q_MOD_10) { + leftOperand %= 10; + } else if (opcode & Q_MOD_100) { + leftOperand %= 100; + } else if (opcode & Q_LEAD_1000) { + while (leftOperand >= 1000) + leftOperand /= 1000; + } + + int op = opcode & Q_OP_MASK; + + CHECK_RANGE; + int rightOperand = rules[i++]; + + switch (op) { + default: + return -1; + case Q_EQ: + truthValue = (leftOperand == rightOperand); + break; + case Q_LT: + truthValue = (leftOperand < rightOperand); + break; + case Q_LEQ: + truthValue = (leftOperand <= rightOperand); + break; + case Q_BETWEEN: + int bottom = rightOperand; + CHECK_RANGE; + int top = rules[i++]; + truthValue = (leftOperand >= bottom && leftOperand <= top); + } + + if (opcode & Q_NOT) + truthValue = !truthValue; + + andExprTruthValue = andExprTruthValue && truthValue; + + if (i == rulesSize || rules[i] != Q_AND) + break; + ++i; + } + + orExprTruthValue = orExprTruthValue || andExprTruthValue; + + if (i == rulesSize || rules[i] != Q_OR) + break; + ++i; + } + + if (orExprTruthValue) + return result; + + ++result; + + if (i == rulesSize) + return result; + + if (rules[i++] != Q_NEWRULE) + break; + } + return -1; +} + +class QTranslatorPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QTranslator) +public: + enum { Contexts = 0x2f, Hashes = 0x42, Messages = 0x69, NumerusRules = 0x88 }; + + QTranslatorPrivate() + : used_mmap(0), unmapPointer(0), unmapLength(0), + messageArray(0), offsetArray(0), contextArray(0), numerusRulesArray(0), + messageLength(0), offsetLength(0), contextLength(0), numerusRulesLength(0) {} + + // for mmap'ed files, this is what needs to be unmapped. + bool used_mmap : 1; + char *unmapPointer; + unsigned int unmapLength; + + // for squeezed but non-file data, this is what needs to be deleted + const uchar *messageArray; + const uchar *offsetArray; + const uchar *contextArray; + const uchar *numerusRulesArray; + uint messageLength; + uint offsetLength; + uint contextLength; + uint numerusRulesLength; + + bool do_load(const QString &filename); + bool do_load(const uchar *data, int len); + QString do_translate(const char *context, const char *sourceText, const char *comment, + int n) const; + void clear(); +}; + +/*! + \class QTranslator + + \brief The QTranslator class provides internationalization support for text + output. + + \ingroup i18n + + An object of this class contains a set of translations from a + source language to a target language. QTranslator provides + functions to look up translations in a translation file. + Translation files are created using \l{Qt Linguist}. + + The most common use of QTranslator is to: load a translation + file, install it using QApplication::installTranslator(), and use + it via QObject::tr(). Here's the \c main() function from the + \l{linguist/hellotr}{Hello tr()} example: + + \snippet examples/linguist/hellotr/main.cpp 2 + + Note that the translator must be created \e before the + application's widgets. + + Most applications will never need to do anything else with this + class. The other functions provided by this class are useful for + applications that work on translator files. + + \section1 Looking up Translations + + It is possible to look up a translation using translate() (as tr() + and QApplication::translate() do). The translate() function takes + up to three parameters: + + \list + \o The \e context - usually the class name for the tr() caller. + \o The \e {source text} - usually the argument to tr(). + \o The \e disambiguation - an optional string that helps disambiguate + different uses of the same text in the same context. + \endlist + + For example, the "Cancel" in a dialog might have "Anuluj" when the + program runs in Polish (in this case the source text would be + "Cancel"). The context would (normally) be the dialog's class + name; there would normally be no comment, and the translated text + would be "Anuluj". + + But it's not always so simple. The Spanish version of a printer + dialog with settings for two-sided printing and binding would + probably require both "Activado" and "Activada" as translations + for "Enabled". In this case the source text would be "Enabled" in + both cases, and the context would be the dialog's class name, but + the two items would have disambiguations such as "two-sided printing" + for one and "binding" for the other. The disambiguation enables the + translator to choose the appropriate gender for the Spanish version, + and enables Qt to distinguish between translations. + + \section1 Using Multiple Translations + + Multiple translation files can be installed in an application. + Translations are searched for in the reverse order in which they were + installed, so the most recently installed translation file is searched + for translations first and the earliest translation file is searched + last. The search stops as soon as a translation containing a matching + string is found. + + This mechanism makes it possible for a specific translation to be + "selected" or given priority over the others; simply uninstall the + translator from the application by passing it to the + QApplication::removeTranslator() function and reinstall it with + QApplication::installTranslator(). It will then be the first + translation to be searched for matching strings. + + \sa QApplication::installTranslator(), QApplication::removeTranslator(), + QObject::tr(), QApplication::translate(), {I18N Example}, + {Hello tr() Example}, {Arrow Pad Example}, {Troll Print Example} +*/ + +/*! + Constructs an empty message file object with parent \a parent that + is not connected to any file. +*/ + +QTranslator::QTranslator(QObject * parent) + : QObject(*new QTranslatorPrivate, parent) +{ +} + +#ifdef QT3_SUPPORT +/*! + \overload QTranslator() + \obsolete + */ +QTranslator::QTranslator(QObject * parent, const char * name) + : QObject(*new QTranslatorPrivate, parent) +{ + setObjectName(QString::fromAscii(name)); +} +#endif + +/*! + Destroys the object and frees any allocated resources. +*/ + +QTranslator::~QTranslator() +{ + if (QCoreApplication::instance()) + QCoreApplication::removeTranslator(this); + Q_D(QTranslator); + d->clear(); +} + +/*! + + Loads \a filename + \a suffix (".qm" if the \a suffix is not + specified), which may be an absolute file name or relative to \a + directory. Returns true if the translation is successfully loaded; + otherwise returns false. + + If \a directory is not specified, the directory of the + application's executable is used (i.e., as + \l{QCoreApplication::}{applicationDirPath()}). + + The previous contents of this translator object are discarded. + + If the file name does not exist, other file names are tried + in the following order: + + \list 1 + \o File name without \a suffix appended. + \o File name with text after a character in \a search_delimiters + stripped ("_." is the default for \a search_delimiters if it is + an empty string) and \a suffix. + \o File name stripped without \a suffix appended. + \o File name stripped further, etc. + \endlist + + For example, an application running in the fr_CA locale + (French-speaking Canada) might call load("foo.fr_ca", + "/opt/foolib"). load() would then try to open the first existing + readable file from this list: + + \list 1 + \o \c /opt/foolib/foo.fr_ca.qm + \o \c /opt/foolib/foo.fr_ca + \o \c /opt/foolib/foo.fr.qm + \o \c /opt/foolib/foo.fr + \o \c /opt/foolib/foo.qm + \o \c /opt/foolib/foo + \endlist +*/ + +bool QTranslator::load(const QString & filename, const QString & directory, + const QString & search_delimiters, + const QString & suffix) +{ + Q_D(QTranslator); + d->clear(); + + QString prefix; + if (QFileInfo(filename).isRelative()) { + prefix = directory; + if (prefix.length() && !prefix.endsWith(QLatin1Char('/'))) + prefix += QLatin1Char('/'); + } + + QString fname = filename; + QString realname; + QString delims; + delims = search_delimiters.isNull() ? QString::fromLatin1("_.") : search_delimiters; + + for (;;) { + QFileInfo fi; + + realname = prefix + fname + (suffix.isNull() ? QString::fromLatin1(".qm") : suffix); + fi.setFile(realname); + if (fi.isReadable() && fi.isFile()) + break; + + realname = prefix + fname; + fi.setFile(realname); + if (fi.isReadable() && fi.isFile()) + break; + + int rightmost = 0; + for (int i = 0; i < (int)delims.length(); i++) { + int k = fname.lastIndexOf(delims[i]); + if (k > rightmost) + rightmost = k; + } + + // no truncations? fail + if (rightmost == 0) + return false; + + fname.truncate(rightmost); + } + + // realname is now the fully qualified name of a readable file. + return d->do_load(realname); +} + +bool QTranslatorPrivate::do_load(const QString &realname) +{ + QTranslatorPrivate *d = this; + bool ok = false; + +#ifdef QT_USE_MMAP + +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif +#ifndef MAP_FAILED +#define MAP_FAILED -1 +#endif + + int fd = -1; + if (!realname.startsWith(QLatin1Char(':'))) + fd = QT_OPEN(QFile::encodeName(realname), O_RDONLY, +#if defined(Q_OS_WIN) + _S_IREAD | _S_IWRITE +#else + 0666 +#endif + ); + if (fd >= 0) { + QT_STATBUF st; + if (!QT_FSTAT(fd, &st)) { + char *ptr; + ptr = reinterpret_cast( + mmap(0, st.st_size, // any address, whole file + PROT_READ, // read-only memory + MAP_FILE | MAP_PRIVATE, // swap-backed map from file + fd, 0)); // from offset 0 of fd + if (ptr && ptr != reinterpret_cast(MAP_FAILED)) { + d->used_mmap = true; + d->unmapPointer = ptr; + d->unmapLength = st.st_size; + ok = true; + } + } + ::close(fd); + } +#endif // QT_USE_MMAP + + if (!ok) { + QFile file(realname); + d->unmapLength = file.size(); + if (!d->unmapLength) + return false; + d->unmapPointer = new char[d->unmapLength]; + + if (file.open(QIODevice::ReadOnly)) + ok = (d->unmapLength == (uint)file.read(d->unmapPointer, d->unmapLength)); + + if (!ok) { + delete [] d->unmapPointer; + d->unmapPointer = 0; + d->unmapLength = 0; + return false; + } + } + + return d->do_load(reinterpret_cast(d->unmapPointer), d->unmapLength); +} + +static QString find_translation(const QLocale & locale, + const QString & filename, + const QString & prefix, + const QString & directory, + const QString & suffix) +{ + QString path; + if (QFileInfo(filename).isRelative()) { + path = directory; + if (!path.isEmpty() && !path.endsWith(QLatin1Char('/'))) + path += QLatin1Char('/'); + } + + QFileInfo fi; + QString realname; + QStringList fuzzyLocales; + + // see http://www.unicode.org/reports/tr35/#LanguageMatching for inspiration + + QStringList languages = locale.uiLanguages(); +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) + for (int i = languages.size()-1; i >= 0; --i) { + QString lang = languages.at(i); + QString lowerLang = lang.toLower(); + if (lang != lowerLang) + languages.insert(i+1, lowerLang); + } +#endif + + // try explicit locales names first + foreach (QString localeName, languages) { + localeName.replace(QLatin1Char('-'), QLatin1Char('_')); + + realname = path + filename + prefix + localeName + (suffix.isNull() ? QLatin1String(".qm") : suffix); + fi.setFile(realname); + if (fi.isReadable() && fi.isFile()) + return realname; + + realname = path + filename + prefix + localeName; + fi.setFile(realname); + if (fi.isReadable() && fi.isFile()) + return realname; + + fuzzyLocales.append(localeName); + } + + // start guessing + foreach (QString localeName, fuzzyLocales) { + for (;;) { + int rightmost = localeName.lastIndexOf(QLatin1Char('_')); + // no truncations? fail + if (rightmost <= 0) + break; + localeName.truncate(rightmost); + + realname = path + filename + prefix + localeName + (suffix.isNull() ? QLatin1String(".qm") : suffix); + fi.setFile(realname); + if (fi.isReadable() && fi.isFile()) + return realname; + + realname = path + filename + prefix + localeName; + fi.setFile(realname); + if (fi.isReadable() && fi.isFile()) + return realname; + } + } + + if (!suffix.isNull()) { + realname = path + filename + suffix; + fi.setFile(realname); + if (fi.isReadable() && fi.isFile()) + return realname; + } + + realname = path + filename + prefix; + fi.setFile(realname); + if (fi.isReadable() && fi.isFile()) + return realname; + + realname = path + filename; + fi.setFile(realname); + if (fi.isReadable() && fi.isFile()) + return realname; + + return QString(); +} + +/*! + \since 4.8 + + Loads \a filename + \a prefix + \a \l{QLocale::uiLanguages()}{ui language + name} + \a suffix (".qm" if the \a suffix is not specified), which may be + an absolute file name or relative to \a directory. Returns true if the + translation is successfully loaded; otherwise returns false. + + The previous contents of this translator object are discarded. + + If the file name does not exist, other file names are tried + in the following order: + + \list 1 + \o File name without \a suffix appended. + \o File name with ui language part after a "_" character stripped and \a suffix. + \o File name with ui language part stripped without \a suffix appended. + \o File name with ui language part stripped further, etc. + \endlist + + For example, an application running in the locale with the following + l{QLocale::uiLanguages()}{ui languages} - "es", "fr-CA", "de" might call + load(QLocale::system(), "foo", ".", "/opt/foolib", ".qm"). load() would + replace '-' (dash) with '_' (underscore) in the ui language and then try to + open the first existing readable file from this list: + + \list 1 + \o \c /opt/foolib/foo.es.qm + \o \c /opt/foolib/foo.es + \o \c /opt/foolib/foo.fr_CA.qm + \o \c /opt/foolib/foo.fr_CA + \o \c /opt/foolib/foo.de.qm + \o \c /opt/foolib/foo.de + \o \c /opt/foolib/foo.fr.qm + \o \c /opt/foolib/foo.fr + \o \c /opt/foolib/foo.qm + \o \c /opt/foolib/foo. + \o \c /opt/foolib/foo + \endlist + + For OSs where file system is case sensitive, QTranslator also tries to load + a lower-cased version of the locale name. +*/ +bool QTranslator::load(const QLocale & locale, + const QString & filename, + const QString & prefix, + const QString & directory, + const QString & suffix) +{ + Q_D(QTranslator); + d->clear(); + QString fname = find_translation(locale, filename, prefix, directory, suffix); + return !fname.isEmpty() && d->do_load(fname); +} + +/*! + \overload load() + \fn bool QTranslator::load(const uchar *data, int len) + + Loads the QM file data \a data of length \a len into the + translator. + + The data is not copied. The caller must be able to guarantee that \a data + will not be deleted or modified. +*/ +bool QTranslator::load(const uchar *data, int len) +{ + Q_D(QTranslator); + d->clear(); + return d->do_load(data, len); +} + +static quint8 read8(const uchar *data) +{ + return *data; +} + +static quint16 read16(const uchar *data) +{ + return (data[0] << 8) | (data[1]); +} + +static quint32 read32(const uchar *data) +{ + return (data[0] << 24) + | (data[1] << 16) + | (data[2] << 8) + | (data[3]); +} + +bool QTranslatorPrivate::do_load(const uchar *data, int len) +{ + if (!data || len < MagicLength || memcmp(data, magic, MagicLength)) + return false; + + bool ok = true; + const uchar *end = data + len; + + data += MagicLength; + + while (data < end - 4) { + quint8 tag = read8(data++); + quint32 blockLen = read32(data); + data += 4; + if (!tag || !blockLen) + break; + if (data + blockLen > end) { + ok = false; + break; + } + + if (tag == QTranslatorPrivate::Contexts) { + contextArray = data; + contextLength = blockLen; + } else if (tag == QTranslatorPrivate::Hashes) { + offsetArray = data; + offsetLength = blockLen; + } else if (tag == QTranslatorPrivate::Messages) { + messageArray = data; + messageLength = blockLen; + } else if (tag == QTranslatorPrivate::NumerusRules) { + numerusRulesArray = data; + numerusRulesLength = blockLen; + } + + data += blockLen; + } + + return ok; +} + +static QString getMessage(const uchar *m, const uchar *end, const char *context, + const char *sourceText, const char *comment, int numerus) +{ + const uchar *tn = 0; + uint tn_length = 0; + int currentNumerus = -1; + + for (;;) { + uchar tag = 0; + if (m < end) + tag = read8(m++); + switch((Tag)tag) { + case Tag_End: + goto end; + case Tag_Translation: { + int len = read32(m); + if (len % 1) + return QString(); + m += 4; + if (++currentNumerus == numerus) { + tn_length = len; + tn = m; + } + m += len; + break; + } + case Tag_Obsolete1: + m += 4; + break; + case Tag_SourceText: { + quint32 len = read32(m); + m += 4; + if (!match(m, sourceText, len)) + return QString(); + m += len; + } + break; + case Tag_Context: { + quint32 len = read32(m); + m += 4; + if (!match(m, context, len)) + return QString(); + m += len; + } + break; + case Tag_Comment: { + quint32 len = read32(m); + m += 4; + if (*m && !match(m, comment, len)) + return QString(); + m += len; + } + break; + default: + return QString(); + } + } +end: + if (!tn) + return QString(); + QString str = QString((const QChar *)tn, tn_length/2); + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + for (int i = 0; i < str.length(); ++i) + str[i] = QChar((str.at(i).unicode() >> 8) + ((str.at(i).unicode() << 8) & 0xff00)); + } + return str; +} + +QString QTranslatorPrivate::do_translate(const char *context, const char *sourceText, + const char *comment, int n) const +{ + if (context == 0) + context = ""; + if (sourceText == 0) + sourceText = ""; + if (comment == 0) + comment = ""; + + if (!offsetLength) + return QString(); + + /* + Check if the context belongs to this QTranslator. If many + translators are installed, this step is necessary. + */ + if (contextLength) { + quint16 hTableSize = read16(contextArray); + uint g = elfHash(context) % hTableSize; + const uchar *c = contextArray + 2 + (g << 1); + quint16 off = read16(c); + c += 2; + if (off == 0) + return QString(); + c = contextArray + (2 + (hTableSize << 1) + (off << 1)); + + for (;;) { + quint8 len = read8(c++); + if (len == 0) + return QString(); + if (match(c, context, len)) + break; + c += len; + } + } + + size_t numItems = offsetLength / (2 * sizeof(quint32)); + if (!numItems) + return QString(); + + int numerus = 0; + if (n >= 0) + numerus = numerusHelper(n, numerusRulesArray, numerusRulesLength); + + for (;;) { + quint32 h = elfHash(QByteArray(QByteArray(sourceText) + comment).constData()); + + const uchar *start = offsetArray; + const uchar *end = start + ((numItems-1) << 3); + while (start <= end) { + const uchar *middle = start + (((end - start) >> 4) << 3); + uint hash = read32(middle); + if (h == hash) { + start = middle; + break; + } else if (hash < h) { + start = middle + 8; + } else { + end = middle - 8; + } + } + + if (start <= end) { + // go back on equal key + while (start != offsetArray && read32(start) == read32(start-8)) + start -= 8; + + while (start < offsetArray + offsetLength) { + quint32 rh = read32(start); + start += 4; + if (rh != h) + break; + quint32 ro = read32(start); + start += 4; + QString tn = getMessage(messageArray + ro, messageArray + messageLength, context, + sourceText, comment, numerus); + if (!tn.isNull()) + return tn; + } + } + if (!comment[0]) + break; + comment = ""; + } + return QString(); +} + +/*! + Empties this translator of all contents. + + This function works with stripped translator files. +*/ + +void QTranslatorPrivate::clear() +{ + Q_Q(QTranslator); + if (unmapPointer && unmapLength) { +#if defined(QT_USE_MMAP) + if (used_mmap) + munmap(unmapPointer, unmapLength); + else +#endif + delete [] unmapPointer; + } + + unmapPointer = 0; + unmapLength = 0; + messageArray = 0; + contextArray = 0; + offsetArray = 0; + numerusRulesArray = 0; + messageLength = 0; + contextLength = 0; + offsetLength = 0; + numerusRulesLength = 0; + + if (QCoreApplicationPrivate::isTranslatorInstalled(q)) + QCoreApplication::postEvent(QCoreApplication::instance(), + new QEvent(QEvent::LanguageChange)); +} + +/*! + Returns the translation for the key (\a context, \a sourceText, + \a disambiguation). If none is found, also tries (\a context, \a + sourceText, ""). If that still fails, returns an empty string. + + If you need to programatically insert translations in to a + QTranslator, this function can be reimplemented. + + \sa load() +*/ +QString QTranslator::translate(const char *context, const char *sourceText, const char *disambiguation) const +{ + Q_D(const QTranslator); + return d->do_translate(context, sourceText, disambiguation, -1); +} + + +/*! + \overload translate() + + Returns the translation for the key (\a context, \a sourceText, + \a disambiguation). If none is found, also tries (\a context, \a + sourceText, ""). If that still fails, returns an empty string. + + If \a n is not -1, it is used to choose an appropriate form for + the translation (e.g. "%n file found" vs. "%n files found"). + + \sa load() +*/ +QString QTranslator::translate(const char *context, const char *sourceText, const char *disambiguation, + int n) const +{ + Q_D(const QTranslator); + // this step is necessary because the 3-parameter translate() overload is virtual + if (n == -1) + return translate(context, sourceText, disambiguation); + return d->do_translate(context, sourceText, disambiguation, n); +} + +/*! + Returns true if this translator is empty, otherwise returns false. + This function works with stripped and unstripped translation files. +*/ +bool QTranslator::isEmpty() const +{ + Q_D(const QTranslator); + return !d->unmapPointer && !d->unmapLength && !d->messageArray && + !d->offsetArray && !d->contextArray; +} + +/*! + \fn QString QTranslator::find(const char *context, const char *sourceText, const char * comment = 0) const + + Use translate(\a context, \a sourceText, \a comment) instead. +*/ + +QT_END_NAMESPACE + +#endif // QT_NO_TRANSLATION diff --git a/src/corelib/kernel/qtranslator.h b/src/corelib/kernel/qtranslator.h new file mode 100644 index 0000000000..7a4fa00369 --- /dev/null +++ b/src/corelib/kernel/qtranslator.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QTRANSLATOR_H +#define QTRANSLATOR_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_TRANSLATION + +class QLocale; +class QTranslatorPrivate; + +class Q_CORE_EXPORT QTranslator : public QObject +{ + Q_OBJECT +public: + explicit QTranslator(QObject *parent = 0); +#ifdef QT3_SUPPORT + QT3_SUPPORT_CONSTRUCTOR QTranslator(QObject * parent, const char * name); +#endif + ~QTranslator(); + + // ### Qt 5: Merge (with "int n = -1") + virtual QString translate(const char *context, const char *sourceText, + const char *disambiguation = 0) const; + QString translate(const char *context, const char *sourceText, const char *disambiguation, + int n) const; + + virtual bool isEmpty() const; + + bool load(const QString & filename, + const QString & directory = QString(), + const QString & search_delimiters = QString(), + const QString & suffix = QString()); + bool load(const QLocale & locale, + const QString & filename, + const QString & prefix = QString(), + const QString & directory = QString(), + const QString & suffix = QString()); + bool load(const uchar *data, int len); + +#ifdef QT3_SUPPORT + QT3_SUPPORT QString find(const char *context, const char *sourceText, const char * comment = 0) const + { return translate(context, sourceText, comment); } +#endif + +private: + Q_DISABLE_COPY(QTranslator) + Q_DECLARE_PRIVATE(QTranslator) +}; + +#endif // QT_NO_TRANSLATION + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QTRANSLATOR_H diff --git a/src/corelib/kernel/qtranslator_p.h b/src/corelib/kernel/qtranslator_p.h new file mode 100644 index 0000000000..fd29592ded --- /dev/null +++ b/src/corelib/kernel/qtranslator_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QTRANSLATOR_P_H +#define QTRANSLATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qfontencodings_x11.cpp and qfont_x11.cpp. This header file may +// change from version to version without notice, or even be removed. +// +// We mean it. +// + +enum { + Q_EQ = 0x01, + Q_LT = 0x02, + Q_LEQ = 0x03, + Q_BETWEEN = 0x04, + + Q_NOT = 0x08, + Q_MOD_10 = 0x10, + Q_MOD_100 = 0x20, + Q_LEAD_1000 = 0x40, + + Q_AND = 0xFD, + Q_OR = 0xFE, + Q_NEWRULE = 0xFF, + + Q_OP_MASK = 0x07, + + Q_NEQ = Q_NOT | Q_EQ, + Q_GT = Q_NOT | Q_LEQ, + Q_GEQ = Q_NOT | Q_LT, + Q_NOT_BETWEEN = Q_NOT | Q_BETWEEN +}; + +#endif diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp new file mode 100644 index 0000000000..f0652386c2 --- /dev/null +++ b/src/corelib/kernel/qvariant.cpp @@ -0,0 +1,3237 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qvariant.h" +#include "qbitarray.h" +#include "qbytearray.h" +#include "qdatastream.h" +#include "qdebug.h" +#include "qmap.h" +#include "qdatetime.h" +#include "qeasingcurve.h" +#include "qlist.h" +#include "qstring.h" +#include "qstringlist.h" +#include "qurl.h" +#include "qlocale.h" +#include "private/qvariant_p.h" + +#ifndef QT_NO_GEOM_VARIANT +#include "qsize.h" +#include "qpoint.h" +#include "qrect.h" +#include "qline.h" +#endif + +#include + +QT_BEGIN_NAMESPACE + +#ifndef DBL_DIG +# define DBL_DIG 10 +#endif +#ifndef FLT_DIG +# define FLT_DIG 6 +#endif + +static void construct(QVariant::Private *x, const void *copy) +{ + x->is_shared = false; + + switch (x->type) { + case QVariant::String: + v_construct(x, copy); + break; + case QVariant::Char: + v_construct(x, copy); + break; + case QVariant::StringList: + v_construct(x, copy); + break; + case QVariant::Map: + v_construct(x, copy); + break; + case QVariant::Hash: + v_construct(x, copy); + break; + case QVariant::List: + v_construct(x, copy); + break; + case QVariant::Date: + v_construct(x, copy); + break; + case QVariant::Time: + v_construct(x, copy); + break; + case QVariant::DateTime: + v_construct(x, copy); + break; + case QVariant::ByteArray: + v_construct(x, copy); + break; + case QVariant::BitArray: + v_construct(x, copy); + break; +#ifndef QT_NO_GEOM_VARIANT + case QVariant::Size: + v_construct(x, copy); + break; + case QVariant::SizeF: + v_construct(x, copy); + break; + case QVariant::Rect: + v_construct(x, copy); + break; + case QVariant::LineF: + v_construct(x, copy); + break; + case QVariant::Line: + v_construct(x, copy); + break; + case QVariant::RectF: + v_construct(x, copy); + break; + case QVariant::Point: + v_construct(x, copy); + break; + case QVariant::PointF: + v_construct(x, copy); + break; +#endif + case QVariant::Url: + v_construct(x, copy); + break; + case QVariant::Locale: + v_construct(x, copy); + break; +#ifndef QT_NO_REGEXP + case QVariant::RegExp: + v_construct(x, copy); + break; +#endif +#ifndef QT_BOOTSTRAPPED + case QVariant::EasingCurve: + v_construct(x, copy); + break; +#endif + case QVariant::Int: + x->data.i = copy ? *static_cast(copy) : 0; + break; + case QVariant::UInt: + x->data.u = copy ? *static_cast(copy) : 0u; + break; + case QVariant::Bool: + x->data.b = copy ? *static_cast(copy) : false; + break; + case QVariant::Double: + x->data.d = copy ? *static_cast(copy) : 0.0; + break; + case QMetaType::Float: + x->data.f = copy ? *static_cast(copy) : 0.0f; + break; + case QMetaType::QObjectStar: + x->data.o = copy ? *static_cast(copy) : 0; + break; + case QVariant::LongLong: + x->data.ll = copy ? *static_cast(copy) : Q_INT64_C(0); + break; + case QVariant::ULongLong: + x->data.ull = copy ? *static_cast(copy) : Q_UINT64_C(0); + break; + case QVariant::Invalid: + case QVariant::UserType: + break; + default: + void *ptr = QMetaType::construct(x->type, copy); + if (!ptr) { + x->type = QVariant::Invalid; + } else { + x->is_shared = true; + x->data.shared = new QVariant::PrivateShared(ptr); + } + break; + } + x->is_null = !copy; +} + +static void clear(QVariant::Private *d) +{ + switch (d->type) { + case QVariant::String: + v_clear(d); + break; + case QVariant::Char: + v_clear(d); + break; + case QVariant::StringList: + v_clear(d); + break; + case QVariant::Map: + v_clear(d); + break; + case QVariant::Hash: + v_clear(d); + break; + case QVariant::List: + v_clear(d); + break; + case QVariant::Date: + v_clear(d); + break; + case QVariant::Time: + v_clear(d); + break; + case QVariant::DateTime: + v_clear(d); + break; + case QVariant::ByteArray: + v_clear(d); + break; + case QVariant::BitArray: + v_clear(d); + break; +#ifndef QT_NO_GEOM_VARIANT + case QVariant::Point: + v_clear(d); + break; + case QVariant::PointF: + v_clear(d); + break; + case QVariant::Size: + v_clear(d); + break; + case QVariant::SizeF: + v_clear(d); + break; + case QVariant::Rect: + v_clear(d); + break; + case QVariant::LineF: + v_clear(d); + break; + case QVariant::Line: + v_clear(d); + break; + case QVariant::RectF: + v_clear(d); + break; +#endif + case QVariant::Url: + v_clear(d); + break; + case QVariant::Locale: + v_clear(d); + break; +#ifndef QT_NO_REGEXP + case QVariant::RegExp: + v_clear(d); + break; +#endif +#ifndef QT_BOOTSTRAPPED + case QVariant::EasingCurve: + v_clear(d); + break; +#endif + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Double: + case QMetaType::Float: + case QMetaType::QObjectStar: + break; + case QVariant::Invalid: + case QVariant::UserType: + case QVariant::Int: + case QVariant::UInt: + case QVariant::Bool: + break; + default: + QMetaType::destroy(d->type, d->data.shared->ptr); + delete d->data.shared; + break; + } + + d->type = QVariant::Invalid; + d->is_null = true; + d->is_shared = false; +} + +static bool isNull(const QVariant::Private *d) +{ + switch(d->type) { + case QVariant::String: + return v_cast(d)->isNull(); + case QVariant::Char: + return v_cast(d)->isNull(); + case QVariant::Date: + return v_cast(d)->isNull(); + case QVariant::Time: + return v_cast(d)->isNull(); + case QVariant::DateTime: + return v_cast(d)->isNull(); + case QVariant::ByteArray: + return v_cast(d)->isNull(); + case QVariant::BitArray: + return v_cast(d)->isNull(); +#ifndef QT_NO_GEOM_VARIANT + case QVariant::Size: + return v_cast(d)->isNull(); + case QVariant::SizeF: + return v_cast(d)->isNull(); + case QVariant::Rect: + return v_cast(d)->isNull(); + case QVariant::Line: + return v_cast(d)->isNull(); + case QVariant::LineF: + return v_cast(d)->isNull(); + case QVariant::RectF: + return v_cast(d)->isNull(); + case QVariant::Point: + return v_cast(d)->isNull(); + case QVariant::PointF: + return v_cast(d)->isNull(); +#endif +#ifndef QT_BOOTSTRAPPED + case QVariant::EasingCurve: +#endif + case QVariant::Url: + case QVariant::Locale: + case QVariant::RegExp: + case QVariant::StringList: + case QVariant::Map: + case QVariant::Hash: + case QVariant::List: + case QVariant::Invalid: + case QVariant::UserType: + case QVariant::Int: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Bool: + case QVariant::Double: + case QMetaType::Float: + case QMetaType::QObjectStar: + break; + } + return d->is_null; +} + +/* + \internal + \since 4.4 + + We cannot use v_cast() for QMetaType's numeric types because they're smaller than QVariant::Private::Data, + which in turns makes v_cast() believe the value is stored in d->data.c. But + it's not, since we're a QMetaType type. + */ +template +inline bool compareNumericMetaType(const QVariant::Private *const a, const QVariant::Private *const b) +{ + return *static_cast(a->data.shared->ptr) == *static_cast(b->data.shared->ptr); +} + +/*! + \internal + + Compares \a a to \a b. The caller guarantees that \a a and \a b + are of the same type. + */ +static bool compare(const QVariant::Private *a, const QVariant::Private *b) +{ + switch(a->type) { + case QVariant::List: + return *v_cast(a) == *v_cast(b); + case QVariant::Map: { + const QVariantMap *m1 = v_cast(a); + const QVariantMap *m2 = v_cast(b); + if (m1->count() != m2->count()) + return false; + QVariantMap::ConstIterator it = m1->constBegin(); + QVariantMap::ConstIterator it2 = m2->constBegin(); + while (it != m1->constEnd()) { + if (*it != *it2 || it.key() != it2.key()) + return false; + ++it; + ++it2; + } + return true; + } + case QVariant::Hash: + return *v_cast(a) == *v_cast(b); + case QVariant::String: + return *v_cast(a) == *v_cast(b); + case QVariant::Char: + return *v_cast(a) == *v_cast(b); + case QVariant::StringList: + return *v_cast(a) == *v_cast(b); +#ifndef QT_NO_GEOM_VARIANT + case QVariant::Size: + return *v_cast(a) == *v_cast(b); + case QVariant::SizeF: + return *v_cast(a) == *v_cast(b); + case QVariant::Rect: + return *v_cast(a) == *v_cast(b); + case QVariant::Line: + return *v_cast(a) == *v_cast(b); + case QVariant::LineF: + return *v_cast(a) == *v_cast(b); + case QVariant::RectF: + return *v_cast(a) == *v_cast(b); + case QVariant::Point: + return *v_cast(a) == *v_cast(b); + case QVariant::PointF: + return *v_cast(a) == *v_cast(b); +#endif + case QVariant::Url: + return *v_cast(a) == *v_cast(b); + case QVariant::Locale: + return *v_cast(a) == *v_cast(b); +#ifndef QT_NO_REGEXP + case QVariant::RegExp: + return *v_cast(a) == *v_cast(b); +#endif + case QVariant::Int: + return a->data.i == b->data.i; + case QVariant::UInt: + return a->data.u == b->data.u; + case QVariant::LongLong: + return a->data.ll == b->data.ll; + case QVariant::ULongLong: + return a->data.ull == b->data.ull; + case QVariant::Bool: + return a->data.b == b->data.b; + case QVariant::Double: + return a->data.d == b->data.d; + case QMetaType::Float: + return a->data.f == b->data.f; + case QMetaType::QObjectStar: + return a->data.o == b->data.o; + case QVariant::Date: + return *v_cast(a) == *v_cast(b); + case QVariant::Time: + return *v_cast(a) == *v_cast(b); + case QVariant::DateTime: + return *v_cast(a) == *v_cast(b); +#ifndef QT_BOOTSTRAPPED + case QVariant::EasingCurve: + return *v_cast(a) == *v_cast(b); +#endif + case QVariant::ByteArray: + return *v_cast(a) == *v_cast(b); + case QVariant::BitArray: + return *v_cast(a) == *v_cast(b); + case QVariant::Invalid: + return true; + case QMetaType::Long: + return compareNumericMetaType(a, b); + case QMetaType::ULong: + return compareNumericMetaType(a, b); + case QMetaType::Short: + return compareNumericMetaType(a, b); + case QMetaType::UShort: + return compareNumericMetaType(a, b); + case QMetaType::UChar: + return compareNumericMetaType(a, b); + case QMetaType::Char: + return compareNumericMetaType(a, b); + default: + break; + } + if (!QMetaType::isRegistered(a->type)) + qFatal("QVariant::compare: type %d unknown to QVariant.", a->type); + + const void *a_ptr = a->is_shared ? a->data.shared->ptr : &(a->data.ptr); + const void *b_ptr = b->is_shared ? b->data.shared->ptr : &(b->data.ptr); + + /* The reason we cannot place this test in a case branch above for the types + * QMetaType::VoidStar, QMetaType::QObjectStar and so forth, is that it wouldn't include + * user defined pointer types. */ + const char *const typeName = QMetaType::typeName(a->type); + uint typeNameLen = qstrlen(typeName); + if (typeNameLen > 0 && typeName[typeNameLen - 1] == '*') + return *static_cast(a_ptr) == *static_cast(b_ptr); + + if (a->is_null && b->is_null) + return true; + + return a_ptr == b_ptr; +} + +/*! + \internal + */ +static qlonglong qMetaTypeNumber(const QVariant::Private *d) +{ + switch (d->type) { + case QMetaType::Int: + return d->data.i; + case QMetaType::LongLong: + return d->data.ll; + case QMetaType::Char: + return qlonglong(*static_cast(d->data.shared->ptr)); + case QMetaType::Short: + return qlonglong(*static_cast(d->data.shared->ptr)); + case QMetaType::Long: + return qlonglong(*static_cast(d->data.shared->ptr)); + case QMetaType::Float: + return qRound64(d->data.f); + case QVariant::Double: + return qRound64(d->data.d); + } + Q_ASSERT(false); + return 0; +} + +static qulonglong qMetaTypeUNumber(const QVariant::Private *d) +{ + switch (d->type) { + case QVariant::UInt: + return d->data.u; + case QVariant::ULongLong: + return d->data.ull; + case QMetaType::UChar: + return qulonglong(*static_cast(d->data.shared->ptr)); + case QMetaType::UShort: + return qulonglong(*static_cast(d->data.shared->ptr)); + case QMetaType::ULong: + return qulonglong(*static_cast(d->data.shared->ptr)); + } + Q_ASSERT(false); + return 0; +} + +static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok) +{ + *ok = true; + + switch (uint(d->type)) { + case QVariant::String: + return v_cast(d)->toLongLong(ok); + case QVariant::Char: + return v_cast(d)->unicode(); + case QVariant::ByteArray: + return v_cast(d)->toLongLong(ok); + case QVariant::Bool: + return qlonglong(d->data.b); + case QVariant::Double: + case QVariant::Int: + case QMetaType::Char: + case QMetaType::Short: + case QMetaType::Long: + case QMetaType::Float: + case QMetaType::LongLong: + return qMetaTypeNumber(d); + case QVariant::ULongLong: + case QVariant::UInt: + case QMetaType::UChar: + case QMetaType::UShort: + case QMetaType::ULong: + return qlonglong(qMetaTypeUNumber(d)); + } + + *ok = false; + return Q_INT64_C(0); +} + +static qulonglong qConvertToUnsignedNumber(const QVariant::Private *d, bool *ok) +{ + *ok = true; + + switch (uint(d->type)) { + case QVariant::String: + return v_cast(d)->toULongLong(ok); + case QVariant::Char: + return v_cast(d)->unicode(); + case QVariant::ByteArray: + return v_cast(d)->toULongLong(ok); + case QVariant::Bool: + return qulonglong(d->data.b); + case QVariant::Double: + case QVariant::Int: + case QMetaType::Char: + case QMetaType::Short: + case QMetaType::Long: + case QMetaType::Float: + case QMetaType::LongLong: + return qulonglong(qMetaTypeNumber(d)); + case QVariant::ULongLong: + case QVariant::UInt: + case QMetaType::UChar: + case QMetaType::UShort: + case QMetaType::ULong: + return qMetaTypeUNumber(d); + } + + *ok = false; + return Q_UINT64_C(0); +} + +template +inline bool qt_convertToBool(const QVariant::Private *const d) +{ + TInput str = v_cast(d)->toLower(); + return !(str == LiteralWrapper("0") || str == LiteralWrapper("false") || str.isEmpty()); +} + +/*! + \internal + + Converts \a d to type \a t, which is placed in \a result. + */ +static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, bool *ok) +{ + Q_ASSERT(d->type != uint(t)); + Q_ASSERT(result); + + bool dummy; + if (!ok) + ok = &dummy; + + switch (uint(t)) { + case QVariant::Url: + switch (d->type) { + case QVariant::String: + *static_cast(result) = QUrl(*v_cast(d)); + break; + default: + return false; + } + break; + case QVariant::String: { + QString *str = static_cast(result); + switch (d->type) { + case QVariant::Char: + *str = QString(*v_cast(d)); + break; + case QMetaType::Char: + case QMetaType::UChar: + *str = QChar::fromAscii(*static_cast(d->data.shared->ptr)); + break; + case QMetaType::Short: + case QMetaType::Long: + case QVariant::Int: + case QVariant::LongLong: + *str = QString::number(qMetaTypeNumber(d)); + break; + case QVariant::UInt: + case QVariant::ULongLong: + case QMetaType::UShort: + case QMetaType::ULong: + *str = QString::number(qMetaTypeUNumber(d)); + break; + case QMetaType::Float: + *str = QString::number(d->data.f, 'g', FLT_DIG); + break; + case QVariant::Double: + *str = QString::number(d->data.d, 'g', DBL_DIG); + break; +#if !defined(QT_NO_DATESTRING) + case QVariant::Date: + *str = v_cast(d)->toString(Qt::ISODate); + break; + case QVariant::Time: + *str = v_cast(d)->toString(Qt::ISODate); + break; + case QVariant::DateTime: + *str = v_cast(d)->toString(Qt::ISODate); + break; +#endif + case QVariant::Bool: + *str = QLatin1String(d->data.b ? "true" : "false"); + break; + case QVariant::ByteArray: + *str = QString::fromAscii(v_cast(d)->constData()); + break; + case QVariant::StringList: + if (v_cast(d)->count() == 1) + *str = v_cast(d)->at(0); + break; + case QVariant::Url: + *str = v_cast(d)->toString(); + break; + default: + return false; + } + break; + } + case QVariant::Char: { + QChar *c = static_cast(result); + switch (d->type) { + case QVariant::Int: + case QVariant::LongLong: + case QMetaType::Char: + case QMetaType::Short: + case QMetaType::Long: + case QMetaType::Float: + *c = QChar(ushort(qMetaTypeNumber(d))); + break; + case QVariant::UInt: + case QVariant::ULongLong: + case QMetaType::UChar: + case QMetaType::UShort: + case QMetaType::ULong: + *c = QChar(ushort(qMetaTypeUNumber(d))); + break; + default: + return false; + } + break; + } +#ifndef QT_NO_GEOM_VARIANT + case QVariant::Size: { + QSize *s = static_cast(result); + switch (d->type) { + case QVariant::SizeF: + *s = v_cast(d)->toSize(); + break; + default: + return false; + } + break; + } + + case QVariant::SizeF: { + QSizeF *s = static_cast(result); + switch (d->type) { + case QVariant::Size: + *s = QSizeF(*(v_cast(d))); + break; + default: + return false; + } + break; + } + + case QVariant::Line: { + QLine *s = static_cast(result); + switch (d->type) { + case QVariant::LineF: + *s = v_cast(d)->toLine(); + break; + default: + return false; + } + break; + } + + case QVariant::LineF: { + QLineF *s = static_cast(result); + switch (d->type) { + case QVariant::Line: + *s = QLineF(*(v_cast(d))); + break; + default: + return false; + } + break; + } +#endif + case QVariant::StringList: + if (d->type == QVariant::List) { + QStringList *slst = static_cast(result); + const QVariantList *list = v_cast(d); + for (int i = 0; i < list->size(); ++i) + slst->append(list->at(i).toString()); + } else if (d->type == QVariant::String) { + QStringList *slst = static_cast(result); + *slst = QStringList(*v_cast(d)); + } else { + return false; + } + break; + case QVariant::Date: { + QDate *dt = static_cast(result); + if (d->type == QVariant::DateTime) + *dt = v_cast(d)->date(); +#ifndef QT_NO_DATESTRING + else if (d->type == QVariant::String) + *dt = QDate::fromString(*v_cast(d), Qt::ISODate); +#endif + else + return false; + + return dt->isValid(); + } + case QVariant::Time: { + QTime *t = static_cast(result); + switch (d->type) { + case QVariant::DateTime: + *t = v_cast(d)->time(); + break; +#ifndef QT_NO_DATESTRING + case QVariant::String: + *t = QTime::fromString(*v_cast(d), Qt::ISODate); + break; +#endif + default: + return false; + } + return t->isValid(); + } + case QVariant::DateTime: { + QDateTime *dt = static_cast(result); + switch (d->type) { +#ifndef QT_NO_DATESTRING + case QVariant::String: + *dt = QDateTime::fromString(*v_cast(d), Qt::ISODate); + break; +#endif + case QVariant::Date: + *dt = QDateTime(*v_cast(d)); + break; + default: + return false; + } + return dt->isValid(); + } + case QVariant::ByteArray: { + QByteArray *ba = static_cast(result); + switch (d->type) { + case QVariant::String: + *ba = v_cast(d)->toAscii(); + break; + case QVariant::Double: + *ba = QByteArray::number(d->data.d, 'g', DBL_DIG); + break; + case QMetaType::Float: + *ba = QByteArray::number(d->data.f, 'g', FLT_DIG); + break; + case QMetaType::Char: + case QMetaType::UChar: + *ba = QByteArray(1, *static_cast(d->data.shared->ptr)); + break; + case QVariant::Int: + case QVariant::LongLong: + case QMetaType::Short: + case QMetaType::Long: + *ba = QByteArray::number(qMetaTypeNumber(d)); + break; + case QVariant::UInt: + case QVariant::ULongLong: + case QMetaType::UShort: + case QMetaType::ULong: + *ba = QByteArray::number(qMetaTypeUNumber(d)); + break; + case QVariant::Bool: + *ba = QByteArray(d->data.b ? "true" : "false"); + break; + default: + return false; + } + } + break; + case QMetaType::Short: + *static_cast(result) = short(qConvertToNumber(d, ok)); + return *ok; + case QMetaType::Long: + *static_cast(result) = long(qConvertToNumber(d, ok)); + return *ok; + case QMetaType::UShort: + *static_cast(result) = ushort(qConvertToUnsignedNumber(d, ok)); + return *ok; + case QMetaType::ULong: + *static_cast(result) = ulong(qConvertToUnsignedNumber(d, ok)); + return *ok; + case QVariant::Int: + *static_cast(result) = int(qConvertToNumber(d, ok)); + return *ok; + case QVariant::UInt: + *static_cast(result) = uint(qConvertToUnsignedNumber(d, ok)); + return *ok; + case QVariant::LongLong: + *static_cast(result) = qConvertToNumber(d, ok); + return *ok; + case QVariant::ULongLong: { + *static_cast(result) = qConvertToUnsignedNumber(d, ok); + return *ok; + } + case QMetaType::UChar: { + *static_cast(result) = qConvertToUnsignedNumber(d, ok); + return *ok; + } + case QVariant::Bool: { + bool *b = static_cast(result); + switch(d->type) { + case QVariant::ByteArray: + *b = qt_convertToBool(d); + break; + case QVariant::String: + *b = qt_convertToBool(d); + break; + case QVariant::Char: + *b = !v_cast(d)->isNull(); + break; + case QVariant::Double: + case QVariant::Int: + case QVariant::LongLong: + case QMetaType::Char: + case QMetaType::Short: + case QMetaType::Long: + case QMetaType::Float: + *b = qMetaTypeNumber(d) != Q_INT64_C(0); + break; + case QVariant::UInt: + case QVariant::ULongLong: + case QMetaType::UChar: + case QMetaType::UShort: + case QMetaType::ULong: + *b = qMetaTypeUNumber(d) != Q_UINT64_C(0); + break; + default: + *b = false; + return false; + } + break; + } + case QVariant::Double: { + double *f = static_cast(result); + switch (d->type) { + case QVariant::String: + *f = v_cast(d)->toDouble(ok); + break; + case QVariant::ByteArray: + *f = v_cast(d)->toDouble(ok); + break; + case QVariant::Bool: + *f = double(d->data.b); + break; + case QMetaType::Float: + *f = double(d->data.f); + break; + case QVariant::LongLong: + case QVariant::Int: + case QMetaType::Char: + case QMetaType::Short: + case QMetaType::Long: + *f = double(qMetaTypeNumber(d)); + break; + case QVariant::UInt: + case QVariant::ULongLong: + case QMetaType::UChar: + case QMetaType::UShort: + case QMetaType::ULong: + *f = double(qMetaTypeUNumber(d)); + break; + default: + *f = 0.0; + return false; + } + break; + } + case QMetaType::Float: { + float *f = static_cast(result); + switch (d->type) { + case QVariant::String: + *f = v_cast(d)->toFloat(ok); + break; + case QVariant::ByteArray: + *f = v_cast(d)->toFloat(ok); + break; + case QVariant::Bool: + *f = float(d->data.b); + break; + case QVariant::Double: + *f = float(d->data.d); + break; + case QVariant::LongLong: + case QVariant::Int: + case QMetaType::Char: + case QMetaType::Short: + case QMetaType::Long: + *f = float(qMetaTypeNumber(d)); + break; + case QVariant::UInt: + case QVariant::ULongLong: + case QMetaType::UChar: + case QMetaType::UShort: + case QMetaType::ULong: + *f = float(qMetaTypeUNumber(d)); + break; + default: + *f = 0.0f; + return false; + } + break; + } + case QVariant::List: + if (d->type == QVariant::StringList) { + QVariantList *lst = static_cast(result); + const QStringList *slist = v_cast(d); + for (int i = 0; i < slist->size(); ++i) + lst->append(QVariant(slist->at(i))); + } else if (qstrcmp(QMetaType::typeName(d->type), "QList") == 0) { + *static_cast(result) = + *static_cast *>(d->data.shared->ptr); + } else { + return false; + } + break; + case QVariant::Map: + if (qstrcmp(QMetaType::typeName(d->type), "QMap") == 0) { + *static_cast(result) = + *static_cast *>(d->data.shared->ptr); + } else { + return false; + } + break; + case QVariant::Hash: + if (qstrcmp(QMetaType::typeName(d->type), "QHash") == 0) { + *static_cast(result) = + *static_cast *>(d->data.shared->ptr); + } else { + return false; + } + break; +#ifndef QT_NO_GEOM_VARIANT + case QVariant::Rect: + if (d->type == QVariant::RectF) + *static_cast(result) = (v_cast(d))->toRect(); + else + return false; + break; + case QVariant::RectF: + if (d->type == QVariant::Rect) + *static_cast(result) = *v_cast(d); + else + return false; + break; + case QVariant::PointF: + if (d->type == QVariant::Point) + *static_cast(result) = *v_cast(d); + else + return false; + break; + case QVariant::Point: + if (d->type == QVariant::PointF) + *static_cast(result) = (v_cast(d))->toPoint(); + else + return false; + break; + case QMetaType::Char: + { + *static_cast(result) = qint8(qConvertToNumber(d, ok)); + return *ok; + } +#endif + default: + return false; + } + return true; +} + +#if !defined(QT_NO_DEBUG_STREAM) && !defined(Q_BROKEN_DEBUG_STREAM) +static void streamDebug(QDebug dbg, const QVariant &v) +{ + switch (v.userType()) { + case QVariant::Int: + dbg.nospace() << v.toInt(); + break; + case QVariant::UInt: + dbg.nospace() << v.toUInt(); + break; + case QVariant::LongLong: + dbg.nospace() << v.toLongLong(); + break; + case QVariant::ULongLong: + dbg.nospace() << v.toULongLong(); + break; + case QMetaType::Float: + dbg.nospace() << v.toFloat(); + break; + case QMetaType::QObjectStar: + dbg.nospace() << qvariant_cast(v); + break; + case QVariant::Double: + dbg.nospace() << v.toDouble(); + break; + case QVariant::Bool: + dbg.nospace() << v.toBool(); + break; + case QVariant::String: + dbg.nospace() << v.toString(); + break; + case QVariant::Char: + dbg.nospace() << v.toChar(); + break; + case QVariant::StringList: + dbg.nospace() << v.toStringList(); + break; + case QVariant::Map: + dbg.nospace() << v.toMap(); + break; + case QVariant::Hash: + dbg.nospace() << v.toHash(); + break; + case QVariant::List: + dbg.nospace() << v.toList(); + break; + case QVariant::Date: + dbg.nospace() << v.toDate(); + break; + case QVariant::Time: + dbg.nospace() << v.toTime(); + break; + case QVariant::DateTime: + dbg.nospace() << v.toDateTime(); + break; +#ifndef QT_BOOTSTRAPPED + case QVariant::EasingCurve: + dbg.nospace() << v.toEasingCurve(); + break; +#endif + case QVariant::ByteArray: + dbg.nospace() << v.toByteArray(); + break; + case QVariant::Url: + dbg.nospace() << v.toUrl(); + break; +#ifndef QT_NO_GEOM_VARIANT + case QVariant::Point: + dbg.nospace() << v.toPoint(); + break; + case QVariant::PointF: + dbg.nospace() << v.toPointF(); + break; + case QVariant::Rect: + dbg.nospace() << v.toRect(); + break; + case QVariant::Size: + dbg.nospace() << v.toSize(); + break; + case QVariant::SizeF: + dbg.nospace() << v.toSizeF(); + break; + case QVariant::Line: + dbg.nospace() << v.toLine(); + break; + case QVariant::LineF: + dbg.nospace() << v.toLineF(); + break; + case QVariant::RectF: + dbg.nospace() << v.toRectF(); + break; +#endif + case QVariant::BitArray: + //dbg.nospace() << v.toBitArray(); + break; + default: + break; + } +} +#endif + +const QVariant::Handler qt_kernel_variant_handler = { + construct, + clear, + isNull, +#ifndef QT_NO_DATASTREAM + 0, + 0, +#endif + compare, + convert, + 0, +#if !defined(QT_NO_DEBUG_STREAM) && !defined(Q_BROKEN_DEBUG_STREAM) + streamDebug +#else + 0 +#endif +}; + +Q_CORE_EXPORT const QVariant::Handler *qcoreVariantHandler() +{ + return &qt_kernel_variant_handler; +} + + +const QVariant::Handler *QVariant::handler = &qt_kernel_variant_handler; + +/*! + \class QVariant + \brief The QVariant class acts like a union for the most common Qt data types. + + \ingroup objectmodel + \ingroup shared + + + Because C++ forbids unions from including types that have + non-default constructors or destructors, most interesting Qt + classes cannot be used in unions. Without QVariant, this would be + a problem for QObject::property() and for database work, etc. + + A QVariant object holds a single value of a single type() at a + time. (Some type()s are multi-valued, for example a string list.) + You can find out what type, T, the variant holds, convert it to a + different type using convert(), get its value using one of the + toT() functions (e.g., toSize()) and check whether the type can + be converted to a particular type using canConvert(). + + The methods named toT() (e.g., toInt(), toString()) are const. If + you ask for the stored type, they return a copy of the stored + object. If you ask for a type that can be generated from the + stored type, toT() copies and converts and leaves the object + itself unchanged. If you ask for a type that cannot be generated + from the stored type, the result depends on the type; see the + function documentation for details. + + Here is some example code to demonstrate the use of QVariant: + + \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 0 + + You can even store QList and QMap + values in a variant, so you can easily construct arbitrarily + complex data structures of arbitrary types. This is very powerful + and versatile, but may prove less memory and speed efficient than + storing specific types in standard data structures. + + QVariant also supports the notion of null values, where you can + have a defined type with no value set. However, note that QVariant + types can only be cast when they have had a value set. + + \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 1 + + QVariant can be extended to support other types than those + mentioned in the \l Type enum. See the \l QMetaType documentation + for details. + + \section1 A Note on GUI Types + + Because QVariant is part of the QtCore library, it cannot provide + conversion functions to data types defined in QtGui, such as + QColor, QImage, and QPixmap. In other words, there is no \c + toColor() function. Instead, you can use the QVariant::value() or + the qvariant_cast() template function. For example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 2 + + The inverse conversion (e.g., from QColor to QVariant) is + automatic for all data types supported by QVariant, including + GUI-related types: + + \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 3 + + \section1 Using canConvert() and convert() Consecutively + + When using canConvert() and convert() consecutively, it is possible for + canConvert() to return true, but convert() to return false. This + is typically because canConvert() only reports the general ability of + QVariant to convert between types given suitable data; it is still + possible to supply data which cannot actually be converted. + + For example, canConvert() would return true when called on a variant + containing a string because, in principle, QVariant is able to convert + strings of numbers to integers. + However, if the string contains non-numeric characters, it cannot be + converted to an integer, and any attempt to convert it will fail. + Hence, it is important to have both functions return true for a + successful conversion. + + \sa QMetaType +*/ + +/*! + \enum QVariant::Type + + This enum type defines the types of variable that a QVariant can + contain. + + \value Invalid no type + \value BitArray a QBitArray + \value Bitmap a QBitmap + \value Bool a bool + \value Brush a QBrush + \value ByteArray a QByteArray + \value Char a QChar + \value Color a QColor + \value Cursor a QCursor + \value Date a QDate + \value DateTime a QDateTime + \value Double a double + \value EasingCurve a QEasingCurve + \value Font a QFont + \value Hash a QVariantHash + \value Icon a QIcon + \value Image a QImage + \value Int an int + \value KeySequence a QKeySequence + \value Line a QLine + \value LineF a QLineF + \value List a QVariantList + \value Locale a QLocale + \value LongLong a \l qlonglong + \value Map a QVariantMap + \value Matrix a QMatrix + \value Transform a QTransform + \value Matrix4x4 a QMatrix4x4 + \value Palette a QPalette + \value Pen a QPen + \value Pixmap a QPixmap + \value Point a QPoint + \value PointArray a QPointArray + \value PointF a QPointF + \value Polygon a QPolygon + \value Quaternion a QQuaternion + \value Rect a QRect + \value RectF a QRectF + \value RegExp a QRegExp + \value Region a QRegion + \value Size a QSize + \value SizeF a QSizeF + \value SizePolicy a QSizePolicy + \value String a QString + \value StringList a QStringList + \value TextFormat a QTextFormat + \value TextLength a QTextLength + \value Time a QTime + \value UInt a \l uint + \value ULongLong a \l qulonglong + \value Url a QUrl + \value Vector2D a QVector2D + \value Vector3D a QVector3D + \value Vector4D a QVector4D + + \value UserType Base value for user-defined types. + + \omitvalue CString + \omitvalue ColorGroup + \omitvalue IconSet + \omitvalue LastGuiType + \omitvalue LastCoreType + \omitvalue LastType +*/ + +/*! + \fn QVariant::QVariant() + + Constructs an invalid variant. +*/ + + +/*! + \fn QVariant::QVariant(int typeOrUserType, const void *copy) + + Constructs variant of type \a typeOrUserType, and initializes with + \a copy if \a copy is not 0. + + Note that you have to pass the address of the variable you want stored. + + Usually, you never have to use this constructor, use QVariant::fromValue() + instead to construct variants from the pointer types represented by + \c QMetaType::VoidStar, \c QMetaType::QObjectStar and + \c QMetaType::QWidgetStar. + + \sa QVariant::fromValue(), Type +*/ + +/*! + \fn QVariant::QVariant(Type type) + + Constructs a null variant of type \a type. +*/ + + + +/*! + \fn QVariant::create(int type, const void *copy) + + \internal + + Constructs a variant private of type \a type, and initializes with \a copy if + \a copy is not 0. +*/ + +void QVariant::create(int type, const void *copy) +{ + d.type = type; + handler->construct(&d, copy); +} + +/*! + \fn QVariant::~QVariant() + + Destroys the QVariant and the contained object. + + Note that subclasses that reimplement clear() should reimplement + the destructor to call clear(). This destructor calls clear(), but + because it is the destructor, QVariant::clear() is called rather + than a subclass's clear(). +*/ + +QVariant::~QVariant() +{ + if ((d.is_shared && !d.data.shared->ref.deref()) || (!d.is_shared && d.type > Char && d.type < UserType)) + handler->clear(&d); +} + +/*! + \fn QVariant::QVariant(const QVariant &p) + + Constructs a copy of the variant, \a p, passed as the argument to + this constructor. +*/ + +QVariant::QVariant(const QVariant &p) + : d(p.d) +{ + if (d.is_shared) { + d.data.shared->ref.ref(); + } else if (p.d.type > Char && p.d.type < QVariant::UserType) { + handler->construct(&d, p.constData()); + d.is_null = p.d.is_null; + } +} + +#ifndef QT_NO_DATASTREAM +/*! + Reads the variant from the data stream, \a s. +*/ +QVariant::QVariant(QDataStream &s) +{ + d.is_null = true; + s >> *this; +} +#endif //QT_NO_DATASTREAM + +/*! + \fn QVariant::QVariant(const QString &val) + + Constructs a new variant with a string value, \a val. +*/ + +/*! + \fn QVariant::QVariant(const QLatin1String &val) + + Constructs a new variant with a string value, \a val. +*/ + +/*! + \fn QVariant::QVariant(const char *val) + + Constructs a new variant with a string value of \a val. + The variant creates a deep copy of \a val, using the encoding + set by QTextCodec::setCodecForCStrings(). + + Note that \a val is converted to a QString for storing in the + variant and QVariant::type() will return QMetaType::QString for + the variant. + + You can disable this operator by defining \c + QT_NO_CAST_FROM_ASCII when you compile your applications. + + \sa QTextCodec::setCodecForCStrings() +*/ + +#ifndef QT_NO_CAST_FROM_ASCII +QVariant::QVariant(const char *val) +{ + QString s = QString::fromAscii(val); + create(String, &s); +} +#endif + +/*! + \fn QVariant::QVariant(const QStringList &val) + + Constructs a new variant with a string list value, \a val. +*/ + +/*! + \fn QVariant::QVariant(const QMap &val) + + Constructs a new variant with a map of QVariants, \a val. +*/ + +/*! + \fn QVariant::QVariant(const QHash &val) + + Constructs a new variant with a hash of QVariants, \a val. +*/ + +/*! + \fn QVariant::QVariant(const QDate &val) + + Constructs a new variant with a date value, \a val. +*/ + +/*! + \fn QVariant::QVariant(const QTime &val) + + Constructs a new variant with a time value, \a val. +*/ + +/*! + \fn QVariant::QVariant(const QDateTime &val) + + Constructs a new variant with a date/time value, \a val. +*/ + +/*! + \since 4.7 + \fn QVariant::QVariant(const QEasingCurve &val) + + Constructs a new variant with an easing curve value, \a val. +*/ + +/*! + \fn QVariant::QVariant(const QByteArray &val) + + Constructs a new variant with a bytearray value, \a val. +*/ + +/*! + \fn QVariant::QVariant(const QBitArray &val) + + Constructs a new variant with a bitarray value, \a val. +*/ + +/*! + \fn QVariant::QVariant(const QPoint &val) + + Constructs a new variant with a point value of \a val. + */ + +/*! + \fn QVariant::QVariant(const QPointF &val) + + Constructs a new variant with a point value of \a val. + */ + +/*! + \fn QVariant::QVariant(const QRectF &val) + + Constructs a new variant with a rect value of \a val. + */ + +/*! + \fn QVariant::QVariant(const QLineF &val) + + Constructs a new variant with a line value of \a val. + */ + +/*! + \fn QVariant::QVariant(const QLine &val) + + Constructs a new variant with a line value of \a val. + */ + +/*! + \fn QVariant::QVariant(const QRect &val) + + Constructs a new variant with a rect value of \a val. + */ + +/*! + \fn QVariant::QVariant(const QSize &val) + + Constructs a new variant with a size value of \a val. + */ + +/*! + \fn QVariant::QVariant(const QSizeF &val) + + Constructs a new variant with a size value of \a val. + */ + +/*! + \fn QVariant::QVariant(const QUrl &val) + + Constructs a new variant with a url value of \a val. + */ + +/*! + \fn QVariant::QVariant(int val) + + Constructs a new variant with an integer value, \a val. +*/ + +/*! + \fn QVariant::QVariant(uint val) + + Constructs a new variant with an unsigned integer value, \a val. +*/ + +/*! + \fn QVariant::QVariant(qlonglong val) + + Constructs a new variant with a long long integer value, \a val. +*/ + +/*! + \fn QVariant::QVariant(qulonglong val) + + Constructs a new variant with an unsigned long long integer value, \a val. +*/ + + +/*! + \fn QVariant::QVariant(bool val) + + Constructs a new variant with a boolean value, \a val. +*/ + +/*! + \fn QVariant::QVariant(double val) + + Constructs a new variant with a floating point value, \a val. +*/ + +/*! + \fn QVariant::QVariant(float val) + + Constructs a new variant with a floating point value, \a val. + \since 4.6 +*/ + +/*! + \fn QVariant::QVariant(const QList &val) + + Constructs a new variant with a list value, \a val. +*/ + +/*! + \fn QVariant::QVariant(const QChar &c) + + Constructs a new variant with a char value, \a c. +*/ + +/*! + \fn QVariant::QVariant(const QLocale &l) + + Constructs a new variant with a locale value, \a l. +*/ + +/*! + \fn QVariant::QVariant(const QRegExp ®Exp) + + Constructs a new variant with the regexp value \a regExp. +*/ + +/*! \since 4.2 + \fn QVariant::QVariant(Qt::GlobalColor color) + + Constructs a new variant of type QVariant::Color and initializes + it with \a color. + + This is a convenience constructor that allows \c{QVariant(Qt::blue);} + to create a valid QVariant storing a QColor. + + Note: This constructor will assert if the application does not link + to the Qt GUI library. + */ + +QVariant::QVariant(Type type) +{ create(type, 0); } +QVariant::QVariant(int typeOrUserType, const void *copy) +{ create(typeOrUserType, copy); d.is_null = false; } + +/*! \internal + flags is true if it is a pointer type + */ +QVariant::QVariant(int typeOrUserType, const void *copy, uint flags) +{ + if (flags) { //type is a pointer type + d.type = typeOrUserType; + d.data.ptr = *reinterpret_cast(copy); + d.is_null = false; + } else { + create(typeOrUserType, copy); + d.is_null = false; + } +} + +QVariant::QVariant(int val) +{ d.is_null = false; d.type = Int; d.data.i = val; } +QVariant::QVariant(uint val) +{ d.is_null = false; d.type = UInt; d.data.u = val; } +QVariant::QVariant(qlonglong val) +{ d.is_null = false; d.type = LongLong; d.data.ll = val; } +QVariant::QVariant(qulonglong val) +{ d.is_null = false; d.type = ULongLong; d.data.ull = val; } +QVariant::QVariant(bool val) +{ d.is_null = false; d.type = Bool; d.data.b = val; } +QVariant::QVariant(double val) +{ d.is_null = false; d.type = Double; d.data.d = val; } + +QVariant::QVariant(const QByteArray &val) +{ d.is_null = false; d.type = ByteArray; v_construct(&d, val); } +QVariant::QVariant(const QBitArray &val) +{ d.is_null = false; d.type = BitArray; v_construct(&d, val); } +QVariant::QVariant(const QString &val) +{ d.is_null = false; d.type = String; v_construct(&d, val); } +QVariant::QVariant(const QChar &val) +{ d.is_null = false; d.type = Char; v_construct(&d, val); } +QVariant::QVariant(const QLatin1String &val) +{ QString str(val); d.is_null = false; d.type = String; v_construct(&d, str); } +QVariant::QVariant(const QStringList &val) +{ d.is_null = false; d.type = StringList; v_construct(&d, val); } + +QVariant::QVariant(const QDate &val) +{ d.is_null = false; d.type = Date; v_construct(&d, val); } +QVariant::QVariant(const QTime &val) +{ d.is_null = false; d.type = Time; v_construct(&d, val); } +QVariant::QVariant(const QDateTime &val) +{ d.is_null = false; d.type = DateTime; v_construct(&d, val); } +#ifndef QT_BOOTSTRAPPED +QVariant::QVariant(const QEasingCurve &val) +{ d.is_null = false; d.type = EasingCurve; v_construct(&d, val); } +#endif +QVariant::QVariant(const QList &list) +{ d.is_null = false; d.type = List; v_construct(&d, list); } +QVariant::QVariant(const QMap &map) +{ d.is_null = false; d.type = Map; v_construct(&d, map); } +QVariant::QVariant(const QHash &hash) +{ d.is_null = false; d.type = Hash; v_construct(&d, hash); } +#ifndef QT_NO_GEOM_VARIANT +QVariant::QVariant(const QPoint &pt) { d.is_null = false; d.type = Point; v_construct(&d, pt); } +QVariant::QVariant(const QPointF &pt) { d.is_null = false; d.type = PointF; v_construct(&d, pt); } +QVariant::QVariant(const QRectF &r) { d.is_null = false; d.type = RectF; v_construct(&d, r); } +QVariant::QVariant(const QLineF &l) { d.is_null = false; d.type = LineF; v_construct(&d, l); } +QVariant::QVariant(const QLine &l) { d.is_null = false; d.type = Line; v_construct(&d, l); } +QVariant::QVariant(const QRect &r) { d.is_null = false; d.type = Rect; v_construct(&d, r); } +QVariant::QVariant(const QSize &s) { d.is_null = false; d.type = Size; v_construct(&d, s); } +QVariant::QVariant(const QSizeF &s) { d.is_null = false; d.type = SizeF; v_construct(&d, s); } +#endif +QVariant::QVariant(const QUrl &u) { d.is_null = false; d.type = Url; v_construct(&d, u); } +QVariant::QVariant(const QLocale &l) { d.is_null = false; d.type = Locale; v_construct(&d, l); } +#ifndef QT_NO_REGEXP +QVariant::QVariant(const QRegExp ®Exp) { d.is_null = false; d.type = RegExp; v_construct(&d, regExp); } +#endif +QVariant::QVariant(Qt::GlobalColor color) { create(62, &color); } + +/*! + Returns the storage type of the value stored in the variant. + Although this function is declared as returning QVariant::Type, + the return value should be interpreted as QMetaType::Type. In + particular, QVariant::UserType is returned here only if the value + is equal or greater than QMetaType::User. + + Note that return values in the ranges QVariant::Char through + QVariant::RegExp and QVariant::Font through QVariant::Transform + correspond to the values in the ranges QMetaType::QChar through + QMetaType::QRegExp and QMetaType::QFont through QMetaType::QQuaternion. + + Pay particular attention when working with char and QChar + variants. Note that there is no QVariant constructor specifically + for type char, but there is one for QChar. For a variant of type + QChar, this function returns QVariant::Char, which is the same as + QMetaType::QChar, but for a variant of type \c char, this function + returns QMetaType::Char, which is \e not the same as + QVariant::Char. + + Also note that the types \c void*, \c long, \c short, \c unsigned + \c long, \c unsigned \c short, \c unsigned \c char, \c float, \c + QObject*, and \c QWidget* are represented in QMetaType::Type but + not in QVariant::Type, and they can be returned by this function. + However, they are considered to be user defined types when tested + against QVariant::Type. + + To test whether an instance of QVariant contains a data type that + is compatible with the data type you are interested in, use + canConvert(). +*/ + +QVariant::Type QVariant::type() const +{ + return d.type >= QMetaType::User ? UserType : static_cast(d.type); +} + +/*! + Returns the storage type of the value stored in the variant. For + non-user types, this is the same as type(). + + \sa type() +*/ + +int QVariant::userType() const +{ + return d.type; +} + +/*! + Assigns the value of the variant \a variant to this variant. +*/ +QVariant& QVariant::operator=(const QVariant &variant) +{ + if (this == &variant) + return *this; + + clear(); + if (variant.d.is_shared) { + variant.d.data.shared->ref.ref(); + d = variant.d; + } else if (variant.d.type > Char && variant.d.type < UserType) { + d.type = variant.d.type; + handler->construct(&d, variant.constData()); + d.is_null = variant.d.is_null; + } else { + d = variant.d; + } + + return *this; +} + +/*! + \fn void QVariant::swap(QVariant &other) + \since 4.8 + + Swaps variant \a other with this variant. This operation is very + fast and never fails. +*/ + +/*! + \fn void QVariant::detach() + + \internal +*/ + +void QVariant::detach() +{ + if (!d.is_shared || d.data.shared->ref == 1) + return; + + Private dd; + dd.type = d.type; + handler->construct(&dd, constData()); + if (!d.data.shared->ref.deref()) + handler->clear(&d); + d.data.shared = dd.data.shared; +} + +/*! + \fn bool QVariant::isDetached() const + + \internal +*/ + +// ### Qt 5: change typeName()(and froends= to return a QString. Suggestion from Harald. +/*! + Returns the name of the type stored in the variant. The returned + strings describe the C++ datatype used to store the data: for + example, "QFont", "QString", or "QVariantList". An Invalid + variant returns 0. +*/ +const char *QVariant::typeName() const +{ + return typeToName(Type(d.type)); +} + +/*! + Convert this variant to type Invalid and free up any resources + used. +*/ +void QVariant::clear() +{ + if ((d.is_shared && !d.data.shared->ref.deref()) || (!d.is_shared && d.type < UserType && d.type > Char)) + handler->clear(&d); + d.type = Invalid; + d.is_null = true; + d.is_shared = false; +} + +/*! + Converts the enum representation of the storage type, \a typ, to + its string representation. + + Returns a null pointer if the type is QVariant::Invalid or doesn't exist. +*/ +const char *QVariant::typeToName(Type typ) +{ + if (typ == Invalid) + return 0; + if (typ == UserType) + return "UserType"; + + return QMetaType::typeName(typ); +} + + +/*! + Converts the string representation of the storage type given in \a + name, to its enum representation. + + If the string representation cannot be converted to any enum + representation, the variant is set to \c Invalid. +*/ +QVariant::Type QVariant::nameToType(const char *name) +{ + if (!name || !*name) + return Invalid; + if (strcmp(name, "Q3CString") == 0) + return ByteArray; + if (strcmp(name, "Q_LLONG") == 0) + return LongLong; + if (strcmp(name, "Q_ULLONG") == 0) + return ULongLong; + if (strcmp(name, "QIconSet") == 0) + return Icon; + if (strcmp(name, "UserType") == 0) + return UserType; + + int metaType = QMetaType::type(name); + return metaType <= int(LastGuiType) ? QVariant::Type(metaType) : UserType; +} + +#ifndef QT_NO_DATASTREAM +enum { MapFromThreeCount = 36 }; +static const ushort map_from_three[MapFromThreeCount] = +{ + QVariant::Invalid, + QVariant::Map, + QVariant::List, + QVariant::String, + QVariant::StringList, + QVariant::Font, + QVariant::Pixmap, + QVariant::Brush, + QVariant::Rect, + QVariant::Size, + QVariant::Color, + QVariant::Palette, + 63, // ColorGroup + QVariant::Icon, + QVariant::Point, + QVariant::Image, + QVariant::Int, + QVariant::UInt, + QVariant::Bool, + QVariant::Double, + QVariant::ByteArray, + QVariant::Polygon, + QVariant::Region, + QVariant::Bitmap, + QVariant::Cursor, + QVariant::SizePolicy, + QVariant::Date, + QVariant::Time, + QVariant::DateTime, + QVariant::ByteArray, + QVariant::BitArray, + QVariant::KeySequence, + QVariant::Pen, + QVariant::LongLong, + QVariant::ULongLong, + QVariant::EasingCurve +}; + +/*! + Internal function for loading a variant from stream \a s. Use the + stream operators instead. + + \internal +*/ +void QVariant::load(QDataStream &s) +{ + clear(); + + quint32 u; + s >> u; + if (s.version() < QDataStream::Qt_4_0) { + if (u >= MapFromThreeCount) + return; + u = map_from_three[u]; + } + qint8 is_null = false; + if (s.version() >= QDataStream::Qt_4_2) + s >> is_null; + if (u == QVariant::UserType) { + QByteArray name; + s >> name; + u = QMetaType::type(name); + if (!u) { + s.setStatus(QDataStream::ReadCorruptData); + return; + } + } + create(static_cast(u), 0); + d.is_null = is_null; + + if (!isValid()) { + // Since we wrote something, we should read something + QString x; + s >> x; + d.is_null = true; + return; + } + + // const cast is safe since we operate on a newly constructed variant + if (!QMetaType::load(s, d.type, const_cast(constData()))) { + s.setStatus(QDataStream::ReadCorruptData); + qWarning("QVariant::load: unable to load type %d.", d.type); + } +} + +/*! + Internal function for saving a variant to the stream \a s. Use the + stream operators instead. + + \internal +*/ +void QVariant::save(QDataStream &s) const +{ + quint32 tp = type(); + if (s.version() < QDataStream::Qt_4_0) { + int i; + for (i = MapFromThreeCount - 1; i >= 0; i--) { + if (map_from_three[i] == tp) { + tp = i; + break; + } + } + if (i == -1) { + s << QVariant(); + return; + } + } + s << tp; + if (s.version() >= QDataStream::Qt_4_2) + s << qint8(d.is_null); + if (tp == QVariant::UserType) { + s << QMetaType::typeName(userType()); + } + + if (!isValid()) { + s << QString(); + return; + } + + if (!QMetaType::save(s, d.type, constData())) { + Q_ASSERT_X(false, "QVariant::save", "Invalid type to save"); + qWarning("QVariant::save: unable to save type %d.", d.type); + } +} + +/*! + \since 4.4 + + Reads a variant \a p from the stream \a s. + + \sa \link datastreamformat.html Format of the QDataStream + operators \endlink +*/ +QDataStream& operator>>(QDataStream &s, QVariant &p) +{ + p.load(s); + return s; +} + +/*! + Writes a variant \a p to the stream \a s. + + \sa \link datastreamformat.html Format of the QDataStream + operators \endlink +*/ +QDataStream& operator<<(QDataStream &s, const QVariant &p) +{ + p.save(s); + return s; +} + +/*! + Reads a variant type \a p in enum representation from the stream \a s. +*/ +QDataStream& operator>>(QDataStream &s, QVariant::Type &p) +{ + quint32 u; + s >> u; + p = (QVariant::Type)u; + + return s; +} + +/*! + Writes a variant type \a p to the stream \a s. +*/ +QDataStream& operator<<(QDataStream &s, const QVariant::Type p) +{ + s << static_cast(p); + + return s; +} + +#endif //QT_NO_DATASTREAM + +/*! + \fn bool QVariant::isValid() const + + Returns true if the storage type of this variant is not + QVariant::Invalid; otherwise returns false. +*/ + +template +inline T qVariantToHelper(const QVariant::Private &d, QVariant::Type t, + const QVariant::Handler *handler, T * = 0) +{ + if (d.type == t) + return *v_cast(&d); + + T ret; + handler->convert(&d, t, &ret, 0); + return ret; +} + +/*! + \fn QStringList QVariant::toStringList() const + + Returns the variant as a QStringList if the variant has type() + StringList, \l String, or \l List of a type that can be converted + to QString; otherwise returns an empty list. + + \sa canConvert(), convert() +*/ +QStringList QVariant::toStringList() const +{ + return qVariantToHelper(d, StringList, handler); +} + +/*! + Returns the variant as a QString if the variant has type() \l + String, \l Bool, \l ByteArray, \l Char, \l Date, \l DateTime, \l + Double, \l Int, \l LongLong, \l StringList, \l Time, \l UInt, or + \l ULongLong; otherwise returns an empty string. + + \sa canConvert(), convert() +*/ +QString QVariant::toString() const +{ + return qVariantToHelper(d, String, handler); +} + +/*! + Returns the variant as a QMap if the variant + has type() \l Map; otherwise returns an empty map. + + \sa canConvert(), convert() +*/ +QVariantMap QVariant::toMap() const +{ + return qVariantToHelper(d, Map, handler); +} + +/*! + Returns the variant as a QHash if the variant + has type() \l Hash; otherwise returns an empty map. + + \sa canConvert(), convert() +*/ +QVariantHash QVariant::toHash() const +{ + return qVariantToHelper(d, Hash, handler); +} + +/*! + \fn QDate QVariant::toDate() const + + Returns the variant as a QDate if the variant has type() \l Date, + \l DateTime, or \l String; otherwise returns an invalid date. + + If the type() is \l String, an invalid date will be returned if the + string cannot be parsed as a Qt::ISODate format date. + + \sa canConvert(), convert() +*/ +QDate QVariant::toDate() const +{ + return qVariantToHelper(d, Date, handler); +} + +/*! + \fn QTime QVariant::toTime() const + + Returns the variant as a QTime if the variant has type() \l Time, + \l DateTime, or \l String; otherwise returns an invalid time. + + If the type() is \l String, an invalid time will be returned if + the string cannot be parsed as a Qt::ISODate format time. + + \sa canConvert(), convert() +*/ +QTime QVariant::toTime() const +{ + return qVariantToHelper(d, Time, handler); +} + +/*! + \fn QDateTime QVariant::toDateTime() const + + Returns the variant as a QDateTime if the variant has type() \l + DateTime, \l Date, or \l String; otherwise returns an invalid + date/time. + + If the type() is \l String, an invalid date/time will be returned + if the string cannot be parsed as a Qt::ISODate format date/time. + + \sa canConvert(), convert() +*/ +QDateTime QVariant::toDateTime() const +{ + return qVariantToHelper(d, DateTime, handler); +} + +/*! + \since 4.7 + \fn QEasingCurve QVariant::toEasingCurve() const + + Returns the variant as a QEasingCurve if the variant has type() \l + EasingCurve; otherwise returns a default easing curve. + + \sa canConvert(), convert() +*/ +#ifndef QT_BOOTSTRAPPED +QEasingCurve QVariant::toEasingCurve() const +{ + return qVariantToHelper(d, EasingCurve, handler); +} +#endif + +/*! + \fn QByteArray QVariant::toByteArray() const + + Returns the variant as a QByteArray if the variant has type() \l + ByteArray or \l String (converted using QString::fromAscii()); + otherwise returns an empty byte array. + + \sa canConvert(), convert() +*/ +QByteArray QVariant::toByteArray() const +{ + return qVariantToHelper(d, ByteArray, handler); +} + +#ifndef QT_NO_GEOM_VARIANT +/*! + \fn QPoint QVariant::toPoint() const + + Returns the variant as a QPoint if the variant has type() + \l Point or \l PointF; otherwise returns a null QPoint. + + \sa canConvert(), convert() +*/ +QPoint QVariant::toPoint() const +{ + return qVariantToHelper(d, Point, handler); +} + +/*! + \fn QRect QVariant::toRect() const + + Returns the variant as a QRect if the variant has type() \l Rect; + otherwise returns an invalid QRect. + + \sa canConvert(), convert() +*/ +QRect QVariant::toRect() const +{ + return qVariantToHelper(d, Rect, handler); +} + +/*! + \fn QSize QVariant::toSize() const + + Returns the variant as a QSize if the variant has type() \l Size; + otherwise returns an invalid QSize. + + \sa canConvert(), convert() +*/ +QSize QVariant::toSize() const +{ + return qVariantToHelper(d, Size, handler); +} + +/*! + \fn QSizeF QVariant::toSizeF() const + + Returns the variant as a QSizeF if the variant has type() \l + SizeF; otherwise returns an invalid QSizeF. + + \sa canConvert(), convert() +*/ +QSizeF QVariant::toSizeF() const +{ + return qVariantToHelper(d, SizeF, handler); +} + +/*! + \fn QRectF QVariant::toRectF() const + + Returns the variant as a QRectF if the variant has type() \l Rect + or \l RectF; otherwise returns an invalid QRectF. + + \sa canConvert(), convert() +*/ +QRectF QVariant::toRectF() const +{ + return qVariantToHelper(d, RectF, handler); +} + +/*! + \fn QLineF QVariant::toLineF() const + + Returns the variant as a QLineF if the variant has type() \l + LineF; otherwise returns an invalid QLineF. + + \sa canConvert(), convert() +*/ +QLineF QVariant::toLineF() const +{ + return qVariantToHelper(d, LineF, handler); +} + +/*! + \fn QLine QVariant::toLine() const + + Returns the variant as a QLine if the variant has type() \l Line; + otherwise returns an invalid QLine. + + \sa canConvert(), convert() +*/ +QLine QVariant::toLine() const +{ + return qVariantToHelper(d, Line, handler); +} + +/*! + \fn QPointF QVariant::toPointF() const + + Returns the variant as a QPointF if the variant has type() \l + Point or \l PointF; otherwise returns a null QPointF. + + \sa canConvert(), convert() +*/ +QPointF QVariant::toPointF() const +{ + return qVariantToHelper(d, PointF, handler); +} + +#endif // QT_NO_GEOM_VARIANT + +/*! + \fn QUrl QVariant::toUrl() const + + Returns the variant as a QUrl if the variant has type() + \l Url; otherwise returns an invalid QUrl. + + \sa canConvert(), convert() +*/ +QUrl QVariant::toUrl() const +{ + return qVariantToHelper(d, Url, handler); +} + +/*! + \fn QLocale QVariant::toLocale() const + + Returns the variant as a QLocale if the variant has type() + \l Locale; otherwise returns an invalid QLocale. + + \sa canConvert(), convert() +*/ +QLocale QVariant::toLocale() const +{ + return qVariantToHelper(d, Locale, handler); +} + +/*! + \fn QRegExp QVariant::toRegExp() const + \since 4.1 + + Returns the variant as a QRegExp if the variant has type() \l + RegExp; otherwise returns an empty QRegExp. + + \sa canConvert(), convert() +*/ +#ifndef QT_NO_REGEXP +QRegExp QVariant::toRegExp() const +{ + return qVariantToHelper(d, RegExp, handler); +} +#endif + +/*! + \fn QChar QVariant::toChar() const + + Returns the variant as a QChar if the variant has type() \l Char, + \l Int, or \l UInt; otherwise returns an invalid QChar. + + \sa canConvert(), convert() +*/ +QChar QVariant::toChar() const +{ + return qVariantToHelper(d, Char, handler); +} + +/*! + Returns the variant as a QBitArray if the variant has type() + \l BitArray; otherwise returns an empty bit array. + + \sa canConvert(), convert() +*/ +QBitArray QVariant::toBitArray() const +{ + return qVariantToHelper(d, BitArray, handler); +} + +template +inline T qNumVariantToHelper(const QVariant::Private &d, + const QVariant::Handler *handler, bool *ok, const T& val) +{ + uint t = qMetaTypeId(); + if (ok) + *ok = true; + if (d.type == t) + return val; + + T ret; + if (!handler->convert(&d, QVariant::Type(t), &ret, ok) && ok) + *ok = false; + return ret; +} + +/*! + Returns the variant as an int if the variant has type() \l Int, + \l Bool, \l ByteArray, \l Char, \l Double, \l LongLong, \l + String, \l UInt, or \l ULongLong; otherwise returns 0. + + If \a ok is non-null: \c{*}\a{ok} is set to true if the value could be + converted to an int; otherwise \c{*}\a{ok} is set to false. + + \bold{Warning:} If the value is convertible to a \l LongLong but is too + large to be represented in an int, the resulting arithmetic overflow will + not be reflected in \a ok. A simple workaround is to use QString::toInt(). + Fixing this bug has been postponed to Qt 5 in order to avoid breaking existing code. + + \sa canConvert(), convert() +*/ +int QVariant::toInt(bool *ok) const +{ + return qNumVariantToHelper(d, handler, ok, d.data.i); +} + +/*! + Returns the variant as an unsigned int if the variant has type() + \l UInt, \l Bool, \l ByteArray, \l Char, \l Double, \l Int, \l + LongLong, \l String, or \l ULongLong; otherwise returns 0. + + If \a ok is non-null: \c{*}\a{ok} is set to true if the value could be + converted to an unsigned int; otherwise \c{*}\a{ok} is set to false. + + \bold{Warning:} If the value is convertible to a \l ULongLong but is too + large to be represented in an unsigned int, the resulting arithmetic overflow will + not be reflected in \a ok. A simple workaround is to use QString::toUInt(). + Fixing this bug has been postponed to Qt 5 in order to avoid breaking existing code. + + \sa canConvert(), convert() +*/ +uint QVariant::toUInt(bool *ok) const +{ + return qNumVariantToHelper(d, handler, ok, d.data.u); +} + +/*! + Returns the variant as a long long int if the variant has type() + \l LongLong, \l Bool, \l ByteArray, \l Char, \l Double, \l Int, + \l String, \l UInt, or \l ULongLong; otherwise returns 0. + + If \a ok is non-null: \c{*}\c{ok} is set to true if the value could be + converted to an int; otherwise \c{*}\c{ok} is set to false. + + \sa canConvert(), convert() +*/ +qlonglong QVariant::toLongLong(bool *ok) const +{ + return qNumVariantToHelper(d, handler, ok, d.data.ll); +} + +/*! + Returns the variant as as an unsigned long long int if the + variant has type() \l ULongLong, \l Bool, \l ByteArray, \l Char, + \l Double, \l Int, \l LongLong, \l String, or \l UInt; otherwise + returns 0. + + If \a ok is non-null: \c{*}\a{ok} is set to true if the value could be + converted to an int; otherwise \c{*}\a{ok} is set to false. + + \sa canConvert(), convert() +*/ +qulonglong QVariant::toULongLong(bool *ok) const +{ + return qNumVariantToHelper(d, handler, ok, d.data.ull); +} + +/*! + Returns the variant as a bool if the variant has type() Bool. + + Returns true if the variant has type() \l Bool, \l Char, \l Double, + \l Int, \l LongLong, \l UInt, or \l ULongLong and the value is + non-zero, or if the variant has type \l String or \l ByteArray and + its lower-case content is not empty, "0" or "false"; otherwise + returns false. + + \sa canConvert(), convert() +*/ +bool QVariant::toBool() const +{ + if (d.type == Bool) + return d.data.b; + + bool res = false; + handler->convert(&d, Bool, &res, 0); + + return res; +} + +/*! + Returns the variant as a double if the variant has type() \l + Double, \l QMetaType::Float, \l Bool, \l ByteArray, \l Int, \l LongLong, \l String, \l + UInt, or \l ULongLong; otherwise returns 0.0. + + If \a ok is non-null: \c{*}\a{ok} is set to true if the value could be + converted to a double; otherwise \c{*}\a{ok} is set to false. + + \sa canConvert(), convert() +*/ +double QVariant::toDouble(bool *ok) const +{ + return qNumVariantToHelper(d, handler, ok, d.data.d); +} + +/*! + Returns the variant as a float if the variant has type() \l + Double, \l QMetaType::Float, \l Bool, \l ByteArray, \l Int, \l LongLong, \l String, \l + UInt, or \l ULongLong; otherwise returns 0.0. + + \since 4.6 + + If \a ok is non-null: \c{*}\a{ok} is set to true if the value could be + converted to a double; otherwise \c{*}\a{ok} is set to false. + + \sa canConvert(), convert() +*/ +float QVariant::toFloat(bool *ok) const +{ + return qNumVariantToHelper(d, handler, ok, d.data.f); +} + +/*! + Returns the variant as a qreal if the variant has type() \l + Double, \l QMetaType::Float, \l Bool, \l ByteArray, \l Int, \l LongLong, \l String, \l + UInt, or \l ULongLong; otherwise returns 0.0. + + \since 4.6 + + If \a ok is non-null: \c{*}\a{ok} is set to true if the value could be + converted to a double; otherwise \c{*}\a{ok} is set to false. + + \sa canConvert(), convert() +*/ +qreal QVariant::toReal(bool *ok) const +{ + return qNumVariantToHelper(d, handler, ok, d.data.real); +} + +/*! + Returns the variant as a QVariantList if the variant has type() + \l List or \l StringList; otherwise returns an empty list. + + \sa canConvert(), convert() +*/ +QVariantList QVariant::toList() const +{ + return qVariantToHelper(d, List, handler); +} + +/*! \fn QVariant::canCast(Type t) const + Use canConvert() instead. +*/ + +/*! \fn QVariant::cast(Type t) + Use convert() instead. +*/ + + +static const quint32 qCanConvertMatrix[QVariant::LastCoreType + 1] = +{ +/*Invalid*/ 0, + +/*Bool*/ 1 << QVariant::Double | 1 << QVariant::Int | 1 << QVariant::UInt + | 1 << QVariant::LongLong | 1 << QVariant::ULongLong | 1 << QVariant::ByteArray + | 1 << QVariant::String | 1 << QVariant::Char, + +/*Int*/ 1 << QVariant::UInt | 1 << QVariant::String | 1 << QVariant::Double + | 1 << QVariant::Bool | 1 << QVariant::LongLong | 1 << QVariant::ULongLong + | 1 << QVariant::Char | 1 << QVariant::ByteArray, + +/*UInt*/ 1 << QVariant::Int | 1 << QVariant::String | 1 << QVariant::Double + | 1 << QVariant::Bool | 1 << QVariant::LongLong | 1 << QVariant::ULongLong + | 1 << QVariant::Char | 1 << QVariant::ByteArray, + +/*LLong*/ 1 << QVariant::Int | 1 << QVariant::String | 1 << QVariant::Double + | 1 << QVariant::Bool | 1 << QVariant::UInt | 1 << QVariant::ULongLong + | 1 << QVariant::Char | 1 << QVariant::ByteArray, + +/*ULlong*/ 1 << QVariant::Int | 1 << QVariant::String | 1 << QVariant::Double + | 1 << QVariant::Bool | 1 << QVariant::UInt | 1 << QVariant::LongLong + | 1 << QVariant::Char | 1 << QVariant::ByteArray, + +/*double*/ 1 << QVariant::Int | 1 << QVariant::String | 1 << QVariant::ULongLong + | 1 << QVariant::Bool | 1 << QVariant::UInt | 1 << QVariant::LongLong + | 1 << QVariant::ByteArray, + +/*QChar*/ 1 << QVariant::Int | 1 << QVariant::UInt | 1 << QVariant::LongLong + | 1 << QVariant::ULongLong, + +/*QMap*/ 0, + +/*QList*/ 1 << QVariant::StringList, + +/*QString*/ 1 << QVariant::StringList | 1 << QVariant::ByteArray | 1 << QVariant::Int + | 1 << QVariant::UInt | 1 << QVariant::Bool | 1 << QVariant::Double + | 1 << QVariant::Date | 1 << QVariant::Time | 1 << QVariant::DateTime + | 1 << QVariant::LongLong | 1 << QVariant::ULongLong | 1 << QVariant::Char + | 1 << QVariant::Url, + +/*QStringList*/ 1 << QVariant::List | 1 << QVariant::String, + +/*QByteArray*/ 1 << QVariant::String | 1 << QVariant::Int | 1 << QVariant::UInt | 1 << QVariant::Bool + | 1 << QVariant::Double | 1 << QVariant::LongLong | 1 << QVariant::ULongLong, + +/*QBitArray*/ 0, + +/*QDate*/ 1 << QVariant::String | 1 << QVariant::DateTime, + +/*QTime*/ 1 << QVariant::String | 1 << QVariant::DateTime, + +/*QDateTime*/ 1 << QVariant::String | 1 << QVariant::Date, + +/*QUrl*/ 1 << QVariant::String, + +/*QLocale*/ 0, + +/*QRect*/ 1 << QVariant::RectF, + +/*QRectF*/ 1 << QVariant::Rect, + +/*QSize*/ 1 << QVariant::SizeF, + +/*QSizeF*/ 1 << QVariant::Size, + +/*QLine*/ 1 << QVariant::LineF, + +/*QLineF*/ 1 << QVariant::Line, + +/*QPoint*/ 1 << QVariant::PointF, + +/*QPointF*/ 1 << QVariant::Point, + +/*QRegExp*/ 0, + +/*QHash*/ 0, + +/*QEasingCurve*/ 0 +}; + +/*! + Returns true if the variant's type can be cast to the requested + type, \a t. Such casting is done automatically when calling the + toInt(), toBool(), ... methods. + + The following casts are done automatically: + + \table + \header \o Type \o Automatically Cast To + \row \o \l Bool \o \l Char, \l Double, \l Int, \l LongLong, \l String, \l UInt, \l ULongLong + \row \o \l ByteArray \o \l Double, \l Int, \l LongLong, \l String, \l UInt, \l ULongLong + \row \o \l Char \o \l Bool, \l Int, \l UInt, \l LongLong, \l ULongLong + \row \o \l Color \o \l String + \row \o \l Date \o \l DateTime, \l String + \row \o \l DateTime \o \l Date, \l String, \l Time + \row \o \l Double \o \l Bool, \l Int, \l LongLong, \l String, \l UInt, \l ULongLong + \row \o \l Font \o \l String + \row \o \l Int \o \l Bool, \l Char, \l Double, \l LongLong, \l String, \l UInt, \l ULongLong + \row \o \l KeySequence \o \l Int, \l String + \row \o \l List \o \l StringList (if the list's items can be converted to strings) + \row \o \l LongLong \o \l Bool, \l ByteArray, \l Char, \l Double, \l Int, \l String, \l UInt, \l ULongLong + \row \o \l Point \o PointF + \row \o \l Rect \o RectF + \row \o \l String \o \l Bool, \l ByteArray, \l Char, \l Color, \l Date, \l DateTime, \l Double, + \l Font, \l Int, \l KeySequence, \l LongLong, \l StringList, \l Time, \l UInt, + \l ULongLong + \row \o \l StringList \o \l List, \l String (if the list contains exactly one item) + \row \o \l Time \o \l String + \row \o \l UInt \o \l Bool, \l Char, \l Double, \l Int, \l LongLong, \l String, \l ULongLong + \row \o \l ULongLong \o \l Bool, \l Char, \l Double, \l Int, \l LongLong, \l String, \l UInt + \endtable + + \sa convert() +*/ +bool QVariant::canConvert(Type t) const +{ + //we can treat floats as double + //the reason for not doing it the "proper" way is that QMetaType::Float's value is 135, + //which can't be handled by qCanConvertMatrix + //In addition QVariant::Type doesn't have a Float value, so we're using QMetaType::Float + const uint currentType = ((d.type == QMetaType::Float) ? QVariant::Double : d.type); + if (uint(t) == uint(QMetaType::Float)) t = QVariant::Double; + + if (currentType == uint(t)) + return true; + + if (currentType > QVariant::LastCoreType || t > QVariant::LastCoreType) { + switch (uint(t)) { + case QVariant::Int: + return currentType == QVariant::KeySequence + || currentType == QMetaType::ULong + || currentType == QMetaType::Long + || currentType == QMetaType::UShort + || currentType == QMetaType::UChar + || currentType == QMetaType::Char + || currentType == QMetaType::Short; + case QVariant::Image: + return currentType == QVariant::Pixmap || currentType == QVariant::Bitmap; + case QVariant::Pixmap: + return currentType == QVariant::Image || currentType == QVariant::Bitmap + || currentType == QVariant::Brush; + case QVariant::Bitmap: + return currentType == QVariant::Pixmap || currentType == QVariant::Image; + case QVariant::ByteArray: + return currentType == QVariant::Color; + case QVariant::String: + return currentType == QVariant::KeySequence || currentType == QVariant::Font + || currentType == QVariant::Color; + case QVariant::KeySequence: + return currentType == QVariant::String || currentType == QVariant::Int; + case QVariant::Font: + return currentType == QVariant::String; + case QVariant::Color: + return currentType == QVariant::String || currentType == QVariant::ByteArray + || currentType == QVariant::Brush; + case QVariant::Brush: + return currentType == QVariant::Color || currentType == QVariant::Pixmap; + case QMetaType::Long: + case QMetaType::Char: + case QMetaType::UChar: + case QMetaType::ULong: + case QMetaType::Short: + case QMetaType::UShort: + return qCanConvertMatrix[QVariant::Int] & (1 << currentType) || currentType == QVariant::Int; + default: + return false; + } + } + + if(t == String && currentType == StringList) + return v_cast(&d)->count() == 1; + else + return qCanConvertMatrix[t] & (1 << currentType); +} + +/*! + Casts the variant to the requested type, \a t. If the cast cannot be + done, the variant is cleared. Returns true if the current type of + the variant was successfully cast; otherwise returns false. + + \warning For historical reasons, converting a null QVariant results + in a null value of the desired type (e.g., an empty string for + QString) and a result of false. + + \sa canConvert(), clear() +*/ + +bool QVariant::convert(Type t) +{ + if (d.type == uint(t)) + return true; + + QVariant oldValue = *this; + + clear(); + if (!oldValue.canConvert(t)) + return false; + + create(t, 0); + if (oldValue.isNull()) + return false; + + bool isOk = true; + if (!handler->convert(&oldValue.d, t, data(), &isOk)) + isOk = false; + d.is_null = !isOk; + return isOk; +} + +/*! + \fn bool operator==(const QVariant &v1, const QVariant &v2) + + \relates QVariant + + Returns true if \a v1 and \a v2 are equal; otherwise returns false. + + \warning This function doesn't support custom types registered + with qRegisterMetaType(). +*/ +/*! + \fn bool operator!=(const QVariant &v1, const QVariant &v2) + + \relates QVariant + + Returns false if \a v1 and \a v2 are equal; otherwise returns true. + + \warning This function doesn't support custom types registered + with qRegisterMetaType(). +*/ + +/*! \fn bool QVariant::operator==(const QVariant &v) const + + Compares this QVariant with \a v and returns true if they are + equal; otherwise returns false. + + In the case of custom types, their equalness operators are not called. + Instead the values' addresses are compared. +*/ + +/*! + \fn bool QVariant::operator!=(const QVariant &v) const + + Compares this QVariant with \a v and returns true if they are not + equal; otherwise returns false. + + \warning This function doesn't support custom types registered + with qRegisterMetaType(). +*/ + +static bool qIsNumericType(uint tp) +{ + return (tp >= QVariant::Bool && tp <= QVariant::Double) + || (tp >= QMetaType::Long && tp <= QMetaType::Float); +} + +static bool qIsFloatingPoint(uint tp) +{ + return tp == QVariant::Double || tp == QMetaType::Float; +} + +/*! \internal + */ +bool QVariant::cmp(const QVariant &v) const +{ + QVariant v2 = v; + if (d.type != v2.d.type) { + if (qIsNumericType(d.type) && qIsNumericType(v.d.type)) { + if (qIsFloatingPoint(d.type) || qIsFloatingPoint(v.d.type)) + return qFuzzyCompare(toReal(), v.toReal()); + else + return toLongLong() == v.toLongLong(); + } + if (!v2.canConvert(Type(d.type)) || !v2.convert(Type(d.type))) + return false; + } + return handler->compare(&d, &v2.d); +} + +/*! \internal + */ + +const void *QVariant::constData() const +{ + return d.is_shared ? d.data.shared->ptr : reinterpret_cast(&d.data.ptr); +} + +/*! + \fn const void* QVariant::data() const + + \internal +*/ + +/*! \internal */ +void* QVariant::data() +{ + detach(); + return const_cast(constData()); +} + + +#ifdef QT3_SUPPORT +/*! \internal + */ +void *QVariant::castOrDetach(Type t) +{ + if (d.type != uint(t)) { + if (!convert(t)) + create(t, 0); + } else { + detach(); + } + return data(); +} +#endif + +/*! + Returns true if this is a NULL variant, false otherwise. +*/ +bool QVariant::isNull() const +{ + return handler->isNull(&d); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QVariant &v) +{ +#ifndef Q_BROKEN_DEBUG_STREAM + dbg.nospace() << "QVariant(" << v.typeName() << ", "; + QVariant::handler->debugStream(dbg, v); + dbg.nospace() << ')'; + return dbg.space(); +#else + qWarning("This compiler doesn't support streaming QVariant to QDebug"); + return dbg; + Q_UNUSED(v); +#endif +} + +QDebug operator<<(QDebug dbg, const QVariant::Type p) +{ +#ifndef Q_BROKEN_DEBUG_STREAM + dbg.nospace() << "QVariant::" << QVariant::typeToName(p); + return dbg.space(); +#else + qWarning("This compiler doesn't support streaming QVariant::Type to QDebug"); + return dbg; + Q_UNUSED(p); +#endif +} +#endif + +/*! + \fn int &QVariant::asInt() + + Use toInt() instead. +*/ + +/*! + \fn uint &QVariant::asUInt() + + Use toUInt() instead. +*/ + +/*! + \fn qlonglong &QVariant::asLongLong() + + Use toLongLong() instead. +*/ + +/*! + \fn qulonglong &QVariant::asULongLong() + + Use toULongLong() instead. +*/ + +/*! + \fn bool &QVariant::asBool() + + Use toBool() instead. +*/ + +/*! + \fn double &QVariant::asDouble() + + Use toDouble() instead. +*/ + +/*! + \fn QByteArray &QVariant::asByteArray() + + Use toByteArray() instead. +*/ + +/*! + \fn QBitArray &QVariant::asBitArray() + + Use toBitArray() instead. +*/ + +/*! + \fn QString &QVariant::asString() + + Use toString() instead. +*/ + +/*! + \fn QStringList &QVariant::asStringList() + + Use toStringList() instead. +*/ + +/*! + \fn QDate &QVariant::asDate() + + Use toDate() instead. +*/ + +/*! + \fn QTime &QVariant::asTime() + + Use toTime() instead. +*/ + +/*! + \fn QDateTime &QVariant::asDateTime() + + Use toDateTime() instead. +*/ + +/*! + \fn QList &QVariant::asList() + + Use toList() instead. +*/ + +/*! + \fn QMap &QVariant::asMap() + + Use toMap() instead. +*/ + +/*! + \fn QVariant::QVariant(bool b, int dummy) + + Use the QVariant(bool) constructor instead. + +*/ + +/*! + \fn const QByteArray QVariant::toCString() const + + Use toByteArray() instead. +*/ + +/*! + \fn QByteArray &QVariant::asCString() + + Use toByteArray() instead. +*/ + +/*! + \fn QPoint &QVariant::asPoint() + + Use toPoint() instead. + */ + +/*! + \fn QRect &QVariant::asRect() + + Use toRect() instead. + */ + +/*! + \fn QSize &QVariant::asSize() + + Use toSize() instead. + */ + +/*! \fn void QVariant::setValue(const T &value) + + Stores a copy of \a value. If \c{T} is a type that QVariant + doesn't support, QMetaType is used to store the value. A compile + error will occur if QMetaType doesn't handle the type. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 4 + + \sa value(), fromValue(), canConvert() + */ + +/*! \fn T QVariant::value() const + + Returns the stored value converted to the template type \c{T}. + Call canConvert() to find out whether a type can be converted. + If the value cannot be converted, \l{default-constructed value} + will be returned. + + If the type \c{T} is supported by QVariant, this function behaves + exactly as toString(), toInt() etc. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 5 + + \sa setValue(), fromValue(), canConvert() +*/ + +/*! \fn bool QVariant::canConvert() const + + Returns true if the variant can be converted to the template type \c{T}, + otherwise false. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 6 + + \sa convert() +*/ + +/*! \fn static QVariant QVariant::fromValue(const T &value) + + Returns a QVariant containing a copy of \a value. Behaves + exactly like setValue() otherwise. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 7 + + \note If you are working with custom types, you should use + the Q_DECLARE_METATYPE() macro to register your custom type. + + \sa setValue(), value() +*/ + +/*! + \fn QVariant qVariantFromValue(const T &value) + \relates QVariant + \obsolete + + Returns a variant containing a copy of the given \a value + with template type \c{T}. + + This function is equivalent to QVariant::fromValue(\a value). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + For example, a QObject pointer can be stored in a variant with the + following code: + + \snippet doc/src/snippets/code/src_corelib_kernel_qvariant.cpp 8 + + \sa QVariant::fromValue() +*/ + +/*! \fn void qVariantSetValue(QVariant &variant, const T &value) + \relates QVariant + \obsolete + + Sets the contents of the given \a variant to a copy of the + \a value with the specified template type \c{T}. + + This function is equivalent to QVariant::setValue(\a value). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QVariant::setValue() +*/ + +/*! + \fn T qvariant_cast(const QVariant &value) + \relates QVariant + + Returns the given \a value converted to the template type \c{T}. + + This function is equivalent to QVariant::value(). + + \sa QVariant::value() +*/ + +/*! \fn T qVariantValue(const QVariant &value) + \relates QVariant + \obsolete + + Returns the given \a value converted to the template type \c{T}. + + This function is equivalent to + \l{QVariant::value()}{QVariant::value}(\a value). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QVariant::value(), qvariant_cast() +*/ + +/*! \fn bool qVariantCanConvert(const QVariant &value) + \relates QVariant + \obsolete + + Returns true if the given \a value can be converted to the + template type specified; otherwise returns false. + + This function is equivalent to QVariant::canConvert(\a value). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QVariant::canConvert() +*/ + +/*! + \typedef QVariantList + \relates QVariant + + Synonym for QList. +*/ + +/*! + \typedef QVariantMap + \relates QVariant + + Synonym for QMap. +*/ + +/*! + \typedef QVariantHash + \relates QVariant + \since 4.5 + + Synonym for QHash. +*/ + +/*! + \typedef QVariant::DataPtr + \internal +*/ + +/*! + \fn DataPtr &QVariant::data_ptr() + \internal +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h new file mode 100644 index 0000000000..1b8cb7fd0b --- /dev/null +++ b/src/corelib/kernel/qvariant.h @@ -0,0 +1,618 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QVARIANT_H +#define QVARIANT_H + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +class QBitArray; +class QDataStream; +class QDate; +class QDateTime; +class QEasingCurve; +class QLine; +class QLineF; +class QLocale; +class QMatrix; +class QTransform; +class QStringList; +class QTime; +class QPoint; +class QPointF; +class QSize; +class QSizeF; +class QRect; +class QRectF; +#ifndef QT_NO_REGEXP +class QRegExp; +#endif +class QTextFormat; +class QTextLength; +class QUrl; +class QVariant; +class QVariantComparisonHelper; + +template +inline QVariant qVariantFromValue(const T &); + +template +inline T qvariant_cast(const QVariant &); + +class Q_CORE_EXPORT QVariant +{ + public: + enum Type { + Invalid = 0, + + Bool = 1, + Int = 2, + UInt = 3, + LongLong = 4, + ULongLong = 5, + Double = 6, + Char = 7, + Map = 8, + List = 9, + String = 10, + StringList = 11, + ByteArray = 12, + BitArray = 13, + Date = 14, + Time = 15, + DateTime = 16, + Url = 17, + Locale = 18, + Rect = 19, + RectF = 20, + Size = 21, + SizeF = 22, + Line = 23, + LineF = 24, + Point = 25, + PointF = 26, + RegExp = 27, + Hash = 28, + EasingCurve = 29, + LastCoreType = EasingCurve, + + // value 62 is internally reserved +#ifdef QT3_SUPPORT + ColorGroup = 63, +#endif + Font = 64, + Pixmap = 65, + Brush = 66, + Color = 67, + Palette = 68, + Icon = 69, + Image = 70, + Polygon = 71, + Region = 72, + Bitmap = 73, + Cursor = 74, + SizePolicy = 75, + KeySequence = 76, + Pen = 77, + TextLength = 78, + TextFormat = 79, + Matrix = 80, + Transform = 81, + Matrix4x4 = 82, + Vector2D = 83, + Vector3D = 84, + Vector4D = 85, + Quaternion = 86, + LastGuiType = Quaternion, + + UserType = 127, +#ifdef QT3_SUPPORT + IconSet = Icon, + CString = ByteArray, + PointArray = Polygon, +#endif + LastType = 0xffffffff // need this so that gcc >= 3.4 allocates 32 bits for Type + }; + + inline QVariant(); + ~QVariant(); + QVariant(Type type); + QVariant(int typeOrUserType, const void *copy); + QVariant(int typeOrUserType, const void *copy, uint flags); + QVariant(const QVariant &other); + +#ifndef QT_NO_DATASTREAM + QVariant(QDataStream &s); +#endif + + QVariant(int i); + QVariant(uint ui); + QVariant(qlonglong ll); + QVariant(qulonglong ull); + QVariant(bool b); + QVariant(double d); + QVariant(float f) { d.is_null = false; d.type = QMetaType::Float; d.data.f = f; } +#ifndef QT_NO_CAST_FROM_ASCII + QT_ASCII_CAST_WARN_CONSTRUCTOR QVariant(const char *str); +#endif + + QVariant(const QByteArray &bytearray); + QVariant(const QBitArray &bitarray); + QVariant(const QString &string); + QVariant(const QLatin1String &string); + QVariant(const QStringList &stringlist); + QVariant(const QChar &qchar); + QVariant(const QDate &date); + QVariant(const QTime &time); + QVariant(const QDateTime &datetime); + QVariant(const QList &list); + QVariant(const QMap &map); + QVariant(const QHash &hash); +#ifndef QT_NO_GEOM_VARIANT + QVariant(const QSize &size); + QVariant(const QSizeF &size); + QVariant(const QPoint &pt); + QVariant(const QPointF &pt); + QVariant(const QLine &line); + QVariant(const QLineF &line); + QVariant(const QRect &rect); + QVariant(const QRectF &rect); +#endif + QVariant(const QUrl &url); + QVariant(const QLocale &locale); +#ifndef QT_NO_REGEXP + QVariant(const QRegExp ®Exp); +#endif +#ifndef QT_BOOTSTRAPPED + QVariant(const QEasingCurve &easing); +#endif + QVariant(Qt::GlobalColor color); + + QVariant& operator=(const QVariant &other); +#ifdef Q_COMPILER_RVALUE_REFS + inline QVariant &operator=(QVariant &&other) + { qSwap(d, other.d); return *this; } +#endif + + inline void swap(QVariant &other) { qSwap(d, other.d); } + + Type type() const; + int userType() const; + const char *typeName() const; + + bool canConvert(Type t) const; + bool convert(Type t); + +#ifdef QT3_SUPPORT + inline QT3_SUPPORT bool canCast(Type t) const + { return canConvert(t); } + inline QT3_SUPPORT bool cast(Type t) + { return convert(t); } +#endif + + inline bool isValid() const; + bool isNull() const; + + void clear(); + + void detach(); + inline bool isDetached() const; + + int toInt(bool *ok = 0) const; + uint toUInt(bool *ok = 0) const; + qlonglong toLongLong(bool *ok = 0) const; + qulonglong toULongLong(bool *ok = 0) const; + bool toBool() const; + double toDouble(bool *ok = 0) const; + float toFloat(bool *ok = 0) const; + qreal toReal(bool *ok = 0) const; + QByteArray toByteArray() const; + QBitArray toBitArray() const; + QString toString() const; + QStringList toStringList() const; + QChar toChar() const; + QDate toDate() const; + QTime toTime() const; + QDateTime toDateTime() const; + QList toList() const; + QMap toMap() const; + QHash toHash() const; + +#ifndef QT_NO_GEOM_VARIANT + QPoint toPoint() const; + QPointF toPointF() const; + QRect toRect() const; + QSize toSize() const; + QSizeF toSizeF() const; + QLine toLine() const; + QLineF toLineF() const; + QRectF toRectF() const; +#endif + QUrl toUrl() const; + QLocale toLocale() const; +#ifndef QT_NO_REGEXP + QRegExp toRegExp() const; +#endif +#ifndef QT_BOOTSTRAPPED + QEasingCurve toEasingCurve() const; +#endif + +#ifdef QT3_SUPPORT + inline QT3_SUPPORT int &asInt(); + inline QT3_SUPPORT uint &asUInt(); + inline QT3_SUPPORT qlonglong &asLongLong(); + inline QT3_SUPPORT qulonglong &asULongLong(); + inline QT3_SUPPORT bool &asBool(); + inline QT3_SUPPORT double &asDouble(); + inline QT3_SUPPORT QByteArray &asByteArray(); + inline QT3_SUPPORT QBitArray &asBitArray(); + inline QT3_SUPPORT QString &asString(); + inline QT3_SUPPORT QStringList &asStringList(); + inline QT3_SUPPORT QDate &asDate(); + inline QT3_SUPPORT QTime &asTime(); + inline QT3_SUPPORT QDateTime &asDateTime(); + inline QT3_SUPPORT QList &asList(); + inline QT3_SUPPORT QMap &asMap(); + inline QT3_SUPPORT QPoint &asPoint(); + inline QT3_SUPPORT QRect &asRect(); + inline QT3_SUPPORT QSize &asSize(); +#endif //QT3_SUPPORT + +#ifndef QT_NO_DATASTREAM + void load(QDataStream &ds); + void save(QDataStream &ds) const; +#endif + static const char *typeToName(Type type); + static Type nameToType(const char *name); + +#ifdef QT3_SUPPORT + inline QT3_SUPPORT_CONSTRUCTOR QVariant(bool val, int) { create(Bool, &val); } + inline QT3_SUPPORT const QByteArray toCString() const { return toByteArray(); } + inline QT3_SUPPORT QByteArray &asCString() { return *reinterpret_cast(castOrDetach(ByteArray)); } +#endif + + void *data(); + const void *constData() const; + inline const void *data() const { return constData(); } + + template + inline void setValue(const T &value); + + template + inline T value() const + { return qvariant_cast(*this); } + + template + static inline QVariant fromValue(const T &value) + { return qVariantFromValue(value); } + + template + bool canConvert() const + { return canConvert(Type(qMetaTypeId())); } + + public: +#ifndef qdoc + struct PrivateShared + { + inline PrivateShared(void *v) : ptr(v), ref(1) { } + void *ptr; + QAtomicInt ref; + }; + struct Private + { + inline Private(): type(Invalid), is_shared(false), is_null(true) { data.ptr = 0; } + inline Private(const Private &other) + : data(other.data), type(other.type), + is_shared(other.is_shared), is_null(other.is_null) + {} + union Data + { + char c; + int i; + uint u; + bool b; + double d; + float f; + qreal real; + qlonglong ll; + qulonglong ull; + QObject *o; + void *ptr; + PrivateShared *shared; + } data; + uint type : 30; + uint is_shared : 1; + uint is_null : 1; + }; + public: + typedef void (*f_construct)(Private *, const void *); + typedef void (*f_clear)(Private *); + typedef bool (*f_null)(const Private *); +#ifndef QT_NO_DATASTREAM + typedef void (*f_load)(Private *, QDataStream &); + typedef void (*f_save)(const Private *, QDataStream &); +#endif + typedef bool (*f_compare)(const Private *, const Private *); + typedef bool (*f_convert)(const QVariant::Private *d, Type t, void *, bool *); + typedef bool (*f_canConvert)(const QVariant::Private *d, Type t); + typedef void (*f_debugStream)(QDebug, const QVariant &); + struct Handler { + f_construct construct; + f_clear clear; + f_null isNull; +#ifndef QT_NO_DATASTREAM + f_load load; + f_save save; +#endif + f_compare compare; + f_convert convert; + f_canConvert canConvert; + f_debugStream debugStream; + }; +#endif + + inline bool operator==(const QVariant &v) const + { return cmp(v); } + inline bool operator!=(const QVariant &v) const + { return !cmp(v); } + +protected: + friend inline bool qvariant_cast_helper(const QVariant &, QVariant::Type, void *); + friend int qRegisterGuiVariant(); + friend int qUnregisterGuiVariant(); + friend inline bool operator==(const QVariant &, const QVariantComparisonHelper &); +#ifndef QT_NO_DEBUG_STREAM + friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QVariant &); +#endif + Private d; + + static const Handler *handler; + + void create(int type, const void *copy); +#ifdef QT3_SUPPORT + void *castOrDetach(Type t); +#endif + bool cmp(const QVariant &other) const; + +private: + // force compile error, prevent QVariant(bool) to be called + inline QVariant(void *) { Q_ASSERT(false); } +#ifdef QT_NO_CAST_FROM_ASCII + // force compile error when implicit conversion is not wanted + inline QVariant(const char *) { Q_ASSERT(false); } +#endif +#ifndef QT3_SUPPORT + // force compile error, prevent QVariant(QVariant::Type, int) to be called + inline QVariant(bool, int) { Q_ASSERT(false); } +#endif +public: + typedef Private DataPtr; + inline DataPtr &data_ptr() { return d; } +}; + +typedef QList QVariantList; +typedef QMap QVariantMap; +typedef QHash QVariantHash; + +inline bool qvariant_cast_helper(const QVariant &v, QVariant::Type tp, void *ptr) +{ return QVariant::handler->convert(&v.d, tp, ptr, 0); } + +template +inline QVariant qVariantFromValue(const T &t) +{ + return QVariant(qMetaTypeId(reinterpret_cast(0)), &t, QTypeInfo::isPointer); +} + +template <> +inline QVariant qVariantFromValue(const QVariant &t) { return t; } + +template +inline void qVariantSetValue(QVariant &v, const T &t) +{ + //if possible we reuse the current QVariant private + const uint type = qMetaTypeId(reinterpret_cast(0)); + QVariant::Private &d = v.data_ptr(); + if (v.isDetached() && (type == d.type || (type <= uint(QVariant::Char) && d.type <= uint(QVariant::Char)))) { + d.type = type; + d.is_null = false; + T *old = reinterpret_cast(d.is_shared ? d.data.shared->ptr : &d.data.ptr); + if (QTypeInfo::isComplex) + old->~T(); + new (old) T(t); //call the copy constructor + } else { + v = QVariant(type, &t, QTypeInfo::isPointer); + } +} + +template <> +inline void qVariantSetValue(QVariant &v, const QVariant &t) +{ + v = t; +} + + +inline QVariant::QVariant() {} +inline bool QVariant::isValid() const { return d.type != Invalid; } + +#ifdef QT3_SUPPORT +inline int &QVariant::asInt() +{ return *reinterpret_cast(castOrDetach(Int)); } +inline uint &QVariant::asUInt() +{ return *reinterpret_cast(castOrDetach(UInt)); } +inline qlonglong &QVariant::asLongLong() +{ return *reinterpret_cast(castOrDetach(LongLong)); } +inline qulonglong &QVariant::asULongLong() +{ return *reinterpret_cast(castOrDetach(ULongLong)); } +inline bool &QVariant::asBool() +{ return *reinterpret_cast(castOrDetach(Bool)); } +inline double &QVariant::asDouble() +{ return *reinterpret_cast(castOrDetach(Double)); } +inline QByteArray& QVariant::asByteArray() +{ return *reinterpret_cast(castOrDetach(ByteArray)); } +inline QBitArray& QVariant::asBitArray() +{ return *reinterpret_cast(castOrDetach(BitArray)); } +inline QString& QVariant::asString() +{ return *reinterpret_cast(castOrDetach(String)); } +inline QStringList& QVariant::asStringList() +{ return *reinterpret_cast(castOrDetach(StringList)); } +inline QDate& QVariant::asDate() +{ return *reinterpret_cast(castOrDetach(Date)); } +inline QTime& QVariant::asTime() +{ return *reinterpret_cast(castOrDetach(Time)); } +inline QDateTime& QVariant::asDateTime() +{ return *reinterpret_cast(castOrDetach(DateTime)); } +inline QList& QVariant::asList() +{ return *reinterpret_cast *>(castOrDetach(List)); } +inline QMap& QVariant::asMap() +{ return *reinterpret_cast *>(castOrDetach(Map)); } +inline QPoint &QVariant::asPoint() +{ return *reinterpret_cast(castOrDetach(Point)); } +inline QRect &QVariant::asRect() +{ return *reinterpret_cast(castOrDetach(Rect)); } +inline QSize &QVariant::asSize() +{ return *reinterpret_cast(castOrDetach(Size)); } +#endif //QT3_SUPPORT + +template +inline void QVariant::setValue(const T &avalue) +{ qVariantSetValue(*this, avalue); } + +#ifndef QT_NO_DATASTREAM +Q_CORE_EXPORT QDataStream& operator>> (QDataStream& s, QVariant& p); +Q_CORE_EXPORT QDataStream& operator<< (QDataStream& s, const QVariant& p); +Q_CORE_EXPORT QDataStream& operator>> (QDataStream& s, QVariant::Type& p); +Q_CORE_EXPORT QDataStream& operator<< (QDataStream& s, const QVariant::Type p); +#endif + +inline bool QVariant::isDetached() const +{ return !d.is_shared || d.data.shared->ref == 1; } + + +#ifdef qdoc + inline bool operator==(const QVariant &v1, const QVariant &v2); + inline bool operator!=(const QVariant &v1, const QVariant &v2); +#else + +/* Helper class to add one more level of indirection to prevent + implicit casts. +*/ +class QVariantComparisonHelper +{ +public: + inline QVariantComparisonHelper(const QVariant &var) + : v(&var) {} +private: + friend inline bool operator==(const QVariant &, const QVariantComparisonHelper &); + const QVariant *v; +}; + +inline bool operator==(const QVariant &v1, const QVariantComparisonHelper &v2) +{ + return v1.cmp(*v2.v); +} + +inline bool operator!=(const QVariant &v1, const QVariantComparisonHelper &v2) +{ + return !operator==(v1, v2); +} +#endif + +#ifndef QT_MOC +template inline T qvariant_cast(const QVariant &v) +{ + const int vid = qMetaTypeId(static_cast(0)); + if (vid == v.userType()) + return *reinterpret_cast(v.constData()); + if (vid < int(QMetaType::User)) { + T t; + if (qvariant_cast_helper(v, QVariant::Type(vid), &t)) + return t; + } + return T(); +} + +template<> inline QVariant qvariant_cast(const QVariant &v) +{ + if (v.userType() == QMetaType::QVariant) + return *reinterpret_cast(v.constData()); + return v; +} + +#ifdef QT_DEPRECATED +template +inline QT_DEPRECATED T qVariantValue(const QVariant &variant) +{ return qvariant_cast(variant); } + +template +inline QT_DEPRECATED bool qVariantCanConvert(const QVariant &variant) +{ return variant.template canConvert(); } +#endif + +#endif +Q_DECLARE_SHARED(QVariant) +Q_DECLARE_TYPEINFO(QVariant, Q_MOVABLE_TYPE); + +#ifndef QT_NO_DEBUG_STREAM +Q_CORE_EXPORT QDebug operator<<(QDebug, const QVariant &); +Q_CORE_EXPORT QDebug operator<<(QDebug, const QVariant::Type); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_BUILTIN_METATYPE(QVariantList, QVariantList) +Q_DECLARE_BUILTIN_METATYPE(QVariantMap, QVariantMap) +Q_DECLARE_BUILTIN_METATYPE(QVariantHash, QVariantHash) + +QT_END_HEADER + +#endif // QVARIANT_H diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h new file mode 100644 index 0000000000..a30d2df5f7 --- /dev/null +++ b/src/corelib/kernel/qvariant_p.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QVARIANT_P_H +#define QVARIANT_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. +// + +// takes a type, returns the internal void* pointer cast +// to a pointer of the input type + +#include +#include + +QT_BEGIN_NAMESPACE + +#ifdef Q_CC_SUN // Sun CC picks the wrong overload, so introduce awful hack + +template +inline T *v_cast(const QVariant::Private *nd, T * = 0) +{ + QVariant::Private *d = const_cast(nd); + return ((sizeof(T) > sizeof(QVariant::Private::Data)) + ? static_cast(d->data.shared->ptr) + : static_cast(static_cast(&d->data.c))); +} + +#else // every other compiler in this world + +template +inline const T *v_cast(const QVariant::Private *d, T * = 0) +{ + return ((sizeof(T) > sizeof(QVariant::Private::Data)) + ? static_cast(d->data.shared->ptr) + : static_cast(static_cast(&d->data.c))); +} + +template +inline T *v_cast(QVariant::Private *d, T * = 0) +{ + return ((sizeof(T) > sizeof(QVariant::Private::Data)) + ? static_cast(d->data.shared->ptr) + : static_cast(static_cast(&d->data.c))); +} + +#endif + + +//a simple template that avoids to allocate 2 memory chunks when creating a QVariant +template class QVariantPrivateSharedEx : public QVariant::PrivateShared +{ +public: + QVariantPrivateSharedEx() : QVariant::PrivateShared(&m_t) { } + QVariantPrivateSharedEx(const T&t) : QVariant::PrivateShared(&m_t), m_t(t) { } + +private: + T m_t; +}; + +// constructs a new variant if copy is 0, otherwise copy-constructs +template +inline void v_construct(QVariant::Private *x, const void *copy, T * = 0) +{ + if (sizeof(T) > sizeof(QVariant::Private::Data)) { + x->data.shared = copy ? new QVariantPrivateSharedEx(*static_cast(copy)) + : new QVariantPrivateSharedEx; + x->is_shared = true; + } else { + if (copy) + new (&x->data.ptr) T(*static_cast(copy)); + else + new (&x->data.ptr) T; + } +} + +template +inline void v_construct(QVariant::Private *x, const T &t) +{ + if (sizeof(T) > sizeof(QVariant::Private::Data)) { + x->data.shared = new QVariantPrivateSharedEx(t); + x->is_shared = true; + } else { + new (&x->data.ptr) T(t); + } +} + +// deletes the internal structures +template +inline void v_clear(QVariant::Private *d, T* = 0) +{ + + if (sizeof(T) > sizeof(QVariant::Private::Data)) { + //now we need to cast + //because QVariant::PrivateShared doesn't have a virtual destructor + delete static_cast*>(d->data.shared); + } else { + v_cast(d)->~T(); + } + +} + +Q_CORE_EXPORT const QVariant::Handler *qcoreVariantHandler(); + +QT_END_NAMESPACE + +#endif // QVARIANT_P_H diff --git a/src/corelib/kernel/qwineventnotifier_p.cpp b/src/corelib/kernel/qwineventnotifier_p.cpp new file mode 100644 index 0000000000..afdc26502e --- /dev/null +++ b/src/corelib/kernel/qwineventnotifier_p.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 "qwineventnotifier_p.h" + +#include "qeventdispatcher_win_p.h" +#include "qcoreapplication.h" + +#include + +QT_BEGIN_NAMESPACE + +/* + \class QWinEventNotifier + \brief The QWinEventNotifier class provides support for the Windows Wait functions. + + The QWinEventNotifier class makes it possible to use the wait + functions on windows in a asynchronous manner. With this class + you can register a HANDLE to an event and get notification when + that event becomes signalled. The state of the event is not modified + in the process so if it is a manual reset event you will need to + reset it after the notification. +*/ + + +QWinEventNotifier::QWinEventNotifier(QObject *parent) + : QObject(parent), handleToEvent(0), enabled(false) +{} + +QWinEventNotifier::QWinEventNotifier(HANDLE hEvent, QObject *parent) + : QObject(parent), handleToEvent(hEvent), enabled(false) +{ + Q_D(QObject); + QEventDispatcherWin32 *eventDispatcher = qobject_cast(d->threadData->eventDispatcher); + Q_ASSERT_X(eventDispatcher, "QWinEventNotifier::QWinEventNotifier()", + "Cannot create a win event notifier without a QEventDispatcherWin32"); + eventDispatcher->registerEventNotifier(this); + enabled = true; +} + +QWinEventNotifier::~QWinEventNotifier() +{ + setEnabled(false); +} + +void QWinEventNotifier::setHandle(HANDLE hEvent) +{ + setEnabled(false); + handleToEvent = hEvent; +} + +HANDLE QWinEventNotifier::handle() const +{ + return handleToEvent; +} + +bool QWinEventNotifier::isEnabled() const +{ + return enabled; +} + +void QWinEventNotifier::setEnabled(bool enable) +{ + if (enabled == enable) // no change + return; + enabled = enable; + + Q_D(QObject); + QEventDispatcherWin32 *eventDispatcher = qobject_cast(d->threadData->eventDispatcher); + if (!eventDispatcher) // perhaps application is shutting down + return; + + if (enabled) + eventDispatcher->registerEventNotifier(this); + else + eventDispatcher->unregisterEventNotifier(this); +} + +bool QWinEventNotifier::event(QEvent * e) +{ + if (e->type() == QEvent::ThreadChange) { + if (enabled) { + QMetaObject::invokeMethod(this, "setEnabled", Qt::QueuedConnection, + Q_ARG(bool, enabled)); + setEnabled(false); + } + } + QObject::event(e); // will activate filters + if (e->type() == QEvent::WinEventAct) { + emit activated(handleToEvent); + return true; + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qwineventnotifier_p.h b/src/corelib/kernel/qwineventnotifier_p.h new file mode 100644 index 0000000000..9d7ab6a1c8 --- /dev/null +++ b/src/corelib/kernel/qwineventnotifier_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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 QWINEVENTNOTIFIER_P_H +#define QWINEVENTNOTIFIER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "QtCore/qobject.h" +#include "QtCore/qt_windows.h" + +QT_BEGIN_NAMESPACE + +class Q_CORE_EXPORT QWinEventNotifier : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QObject) + +public: + explicit QWinEventNotifier(QObject *parent = 0); + explicit QWinEventNotifier(HANDLE hEvent, QObject *parent = 0); + ~QWinEventNotifier(); + + void setHandle(HANDLE hEvent); + HANDLE handle() const; + + bool isEnabled() const; + +public Q_SLOTS: + void setEnabled(bool enable); + +Q_SIGNALS: + void activated(HANDLE hEvent); + +protected: + bool event(QEvent * e); + +private: + Q_DISABLE_COPY(QWinEventNotifier) + + HANDLE handleToEvent; + bool enabled; +}; + +QT_END_NAMESPACE + +#endif // QWINEVENTNOTIFIER_P_H -- cgit v1.2.3