diff options
527 files changed, 27785 insertions, 6438 deletions
@@ -43,6 +43,7 @@ my %modules = ( # path to module name map "QtDBus" => "$basedir/src/dbus", "QtWebKit" => "$basedir/src/3rdparty/webkit/WebCore", "phonon" => "$basedir/src/phonon", + "QtMultimedia" => "$basedir/src/multimedia", ); my %moduleheaders = ( # restrict the module headers to those found in relative path "QtWebKit" => "../WebKit/qt/Api", diff --git a/config.tests/mac/defaultarch.test b/config.tests/mac/defaultarch.test index 4502af7..80f244a 100755 --- a/config.tests/mac/defaultarch.test +++ b/config.tests/mac/defaultarch.test @@ -3,7 +3,7 @@ COMPILER=$1 VERBOSE=$2 WORKDIR=$3 -QT_MAC_DEFUALT_ARCH= +QT_MAC_DEFAULT_ARCH= touch defaultarch.c @@ -17,17 +17,17 @@ rm -f defaultarch.c defaultarch.o # detect our known archs. if echo "$FIlE_OUTPUT" | grep '\<i386\>' > /dev/null 2>&1; then - QT_MAC_DEFUALT_ARCH=x86 # configure knows it as "x86" not "i386" + QT_MAC_DEFAULT_ARCH=x86 # configure knows it as "x86" not "i386" fi if echo "$FIlE_OUTPUT" | grep '\<x86_64\>' > /dev/null 2>&1; then - QT_MAC_DEFUALT_ARCH=x86_64 + QT_MAC_DEFAULT_ARCH=x86_64 fi if echo "$FIlE_OUTPUT" | grep '\<ppc\>' > /dev/null 2>&1; then - QT_MAC_DEFUALT_ARCH=ppc + QT_MAC_DEFAULT_ARCH=ppc fi if echo "$FIlE_OUTPUT" | grep '\<ppc64\>' > /dev/null 2>&1; then - QT_MAC_DEFUALT_ARCH=ppc64 + QT_MAC_DEFAULT_ARCH=ppc64 fi -[ "$VERBOSE" = "yes" ] && echo "setting QT_MAC_DEFUALT_ARCH to \"$QT_MAC_DEFUALT_ARCH\"" -export QT_MAC_DEFUALT_ARCH +[ "$VERBOSE" = "yes" ] && echo "setting QT_MAC_DEFAULT_ARCH to \"$QT_MAC_DEFAULT_ARCH\"" +export QT_MAC_DEFAULT_ARCH diff --git a/config.tests/unix/alsa/alsa.pro b/config.tests/unix/alsa/alsa.pro new file mode 100644 index 0000000..4931d38 --- /dev/null +++ b/config.tests/unix/alsa/alsa.pro @@ -0,0 +1,4 @@ +SOURCES = alsatest.cpp +LIBS+=-lasound +CONFIG -= qt dylib +mac:CONFIG -= app_bundle diff --git a/config.tests/unix/alsa/alsatest.cpp b/config.tests/unix/alsa/alsatest.cpp new file mode 100644 index 0000000..af2f5d9 --- /dev/null +++ b/config.tests/unix/alsa/alsatest.cpp @@ -0,0 +1,6 @@ +#include <alsa/asoundlib.h> +int main(int argc,char **argv) +{ + return 0; +} + diff --git a/config.tests/unix/openssl/openssl.pri b/config.tests/unix/openssl/openssl.pri index bc95479..f069396 100644 --- a/config.tests/unix/openssl/openssl.pri +++ b/config.tests/unix/openssl/openssl.pri @@ -1,9 +1,3 @@ -!cross_compile { - TRY_INCLUDEPATHS = /include /usr/include /usr/local/include $$QMAKE_INCDIR $$INCLUDEPATH - # LSB doesn't allow using headers from /include or /usr/include - linux-lsb-g++:TRY_INCLUDEPATHS = $$QMAKE_INCDIR $$INCLUDEPATH - for(p, TRY_INCLUDEPATHS) { - pp = $$join(p, "", "", "/openssl") - exists($$pp):INCLUDEPATH *= $$p - } -} +# Empty file since Qt 4.6 +# I'm too lazy to find all places where this file is included + @@ -609,6 +609,7 @@ CFG_NOBUILD_PARTS="" CFG_RELEASE_QMAKE=no CFG_PHONON=auto CFG_PHONON_BACKEND=yes +CFG_MULTIMEDIA=yes CFG_SVG=yes CFG_WEBKIT=auto # (yes|no|auto) @@ -709,6 +710,7 @@ OPT_VERBOSE=no OPT_HELP= CFG_SILENT=no CFG_GRAPHICS_SYSTEM=default +CFG_ALSA=auto # initalize variables used for installation QT_INSTALL_PREFIX= @@ -848,7 +850,7 @@ while [ "$#" -gt 0 ]; do VAL=no ;; #Qt style yes options - -incremental|-qvfb|-profile|-shared|-static|-sm|-xinerama|-xshape|-xshape|-xinput|-reduce-exports|-pch|-separate-debug-info|-stl|-freetype|-xcursor|-xfixes|-xrandr|-xrender|-mitshm|-fontconfig|-xkb|-nis|-qdbus|-dbus|-dbus-linked|-glib|-gstreamer|-gtkstyle|-cups|-iconv|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-accessibility|-confirm-license|-gnumake|-framework|-qt3support|-debug-and-release|-exceptions|-cocoa|-universal|-prefix-install|-silent|-armfpa|-optimized-qmake|-dwarf2|-reduce-relocations|-sse|-openssl|-openssl-linked|-ptmalloc|-xmlpatterns|-phonon|-phonon-backend|-svg|-webkit|-scripttools|-rpath|-force-pkg-config) + -incremental|-qvfb|-profile|-shared|-static|-sm|-xinerama|-xshape|-xshape|-xinput|-reduce-exports|-pch|-separate-debug-info|-stl|-freetype|-xcursor|-xfixes|-xrandr|-xrender|-mitshm|-fontconfig|-xkb|-nis|-qdbus|-dbus|-dbus-linked|-glib|-gstreamer|-gtkstyle|-cups|-iconv|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-accessibility|-confirm-license|-gnumake|-framework|-qt3support|-debug-and-release|-exceptions|-cocoa|-universal|-prefix-install|-silent|-armfpa|-optimized-qmake|-dwarf2|-reduce-relocations|-sse|-openssl|-openssl-linked|-ptmalloc|-xmlpatterns|-phonon|-phonon-backend|-multimedia|-svg|-webkit|-scripttools|-rpath|-force-pkg-config) VAR=`echo $1 | sed "s,^-\(.*\),\1,"` VAL=yes ;; @@ -1976,6 +1978,13 @@ while [ "$#" -gt 0 ]; do UNKNOWN_OPT=yes fi ;; + multimedia) + if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then + CFG_MULTIMEDIA="$VAL" + else + UNKNOWN_OPT=yes + fi + ;; *) UNKNOWN_OPT=yes ;; @@ -3152,6 +3161,7 @@ Usage: $relconf [-h] [-prefix <dir>] [-prefix-install] [-bindir <dir>] [-libdir [-no-optimized-qmake] [-optimized-qmake] [-no-xmlpatterns] [-xmlpatterns] [-no-phonon] [-phonon] [-no-phonon-backend] [-phonon-backend] [-no-openssl] [-openssl] [-openssl-linked] + [-no-multimedia] [-multimedia] [-no-gtkstyle] [-gtkstyle] [-no-svg] [-svg] [-no-webkit] [-webkit] [-no-scripttools] [-scripttools] @@ -3292,6 +3302,9 @@ fi -no-phonon-backend.. Do not build the platform phonon plugin. + -phonon-backend..... Build the platform phonon plugin. + -no-multimedia ..... Do not build the multimedia module. + * -multimedia ........ Build the multimedia module. + -no-svg ............ Do not build the SVG module. + -svg ............... Build the SVG module. @@ -5664,6 +5677,14 @@ if [ "$CFG_PTMALLOC" != "no" ]; then QMakeVar add QMAKE_LFLAGS "$outpath/lib/libptmalloc3.a" fi +if [ "$CFG_ALSA" = "auto" ]; then + if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/unix/alsa "alsa" $L_FLAGS $I_FLAGS $l_FLAGS; then + CFG_ALSA=yes + else + CFG_ALSA=no + fi +fi + #------------------------------------------------------------------------------- # ask for all that hasn't been auto-detected or specified in the arguments #------------------------------------------------------------------------------- @@ -5707,17 +5728,23 @@ if [ "$CFG_MAC_DWARF2" = "yes" ]; then QT_CONFIG="$QT_CONFIG dwarf2" fi -# Set the default arch. Select 32-bit/carbon if nothing else has -# been specified on the configure line. +# Set the default arch. +# Carbon builds: 32 bit x86/ppc. +# For "-cocoa" builds on snow leopard : compiler default (64-bit). +# For "-cocoa" builds on leopard : compiler default (32-bit). if [ "$PLATFORM_MAC" = "yes" ] && [ "$CFG_MAC_ARCHS" == "" ]; then source "$mactests/defaultarch.test" "$TEST_COMPILER" "$OPT_VERBOSE" "$mactests" - if [ "$QT_MAC_DEFUALT_ARCH" == "x86_64" ]; then - CFG_MAC_ARCHS=" x86" - elif [ "$QT_MAC_DEFUALT_ARCH" == "ppc64" ]; then - CFG_MAC_ARCHS=" ppc" - else - CFG_MAC_ARCHS=" $QT_MAC_DEFUALT_ARCH" + if [ "$CFG_MAC_COCOA" != "yes" ]; then + if [ "$QT_MAC_DEFAULT_ARCH" == "x86_64" ]; then + CFG_MAC_ARCHS=" x86" + elif [ "$QT_MAC_DEFAULT_ARCH" == "ppc64" ]; then + CFG_MAC_ARCHS=" ppc" + else + CFG_MAC_ARCHS=" $QT_MAC_DEFAULT_ARCH" + fi + else + CFG_MAC_ARCHS=" $QT_MAC_DEFAULT_ARCH" fi [ "$OPT_VERBOSE" == "yes" ] && echo "Setting Mac architechture to$CFG_MAC_ARCHS." @@ -6098,6 +6125,10 @@ if [ "$CFG_EXCEPTIONS" != "no" ]; then QTCONFIG_CONFIG="$QTCONFIG_CONFIG exceptions" fi +if [ "$CFG_ALSA" = "yes" ]; then + QT_CONFIG="$QT_CONFIG alsa" +fi + # # Some Qt modules are too advanced in C++ for some old compilers # Detect here the platforms where they are known to work. @@ -6269,6 +6300,12 @@ else QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_SCRIPTTOOLS" fi +if [ "$CFG_MULTIMEDIA" = "yes" ]; then + QT_CONFIG="$QT_CONFIG multimedia" +else + QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_MULTIMEDIA" +fi + if [ "$CFG_EXCEPTIONS" = "no" ]; then case "$COMPILER" in g++*) @@ -6770,6 +6807,7 @@ fi [ "$CFG_XFIXES" = "runtime" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_RUNTIME_XFIXES" [ "$CFG_XRANDR" = "runtime" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_RUNTIME_XRANDR" [ "$CFG_XINPUT" = "runtime" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_RUNTIME_XINPUT" +[ "$CFG_ALSA" = "no" ] && QCONFIG_FLAGS="$QCONFIG_FLAGS QT_NO_ALSA" # sort QCONFIG_FLAGS for neatness if we can [ '!' -z "$AWK" ] && QCONFIG_FLAGS=`echo $QCONFIG_FLAGS | $AWK '{ gsub(" ", "\n"); print }' | sort | uniq` @@ -7129,6 +7167,7 @@ echo "QtConcurrent code.... $CFG_CONCURRENT" echo "QtScriptTools module $CFG_SCRIPTTOOLS" echo "QtXmlPatterns module $CFG_XMLPATTERNS" echo "Phonon module ....... $CFG_PHONON" +echo "Multimedia module ... $CFG_MULTIMEDIA" echo "SVG module .......... $CFG_SVG" echo "WebKit module ....... $CFG_WEBKIT" echo "STL support ......... $CFG_STL" @@ -7279,6 +7318,7 @@ if [ "$PLATFORM_MAC" = "yes" ] && [ "$CFG_FRAMEWORK" = "yes" ] && [ "$CFG_DEBUG" echo "NOTE: Mac OS X frameworks implicitly build debug and release Qt libraries." echo fi +echo "alsa support ........ $CFG_ALSA" echo sepath=`echo "$relpath" | sed -e 's/\\./\\\\./g'` diff --git a/configure.exe b/configure.exe Binary files differindex 322819e..cf5885a 100644 --- a/configure.exe +++ b/configure.exe diff --git a/demos/qtdemo/xml/examples.xml b/demos/qtdemo/xml/examples.xml index 006bfd0..1b0b533 100644 --- a/demos/qtdemo/xml/examples.xml +++ b/demos/qtdemo/xml/examples.xml @@ -154,6 +154,11 @@ <category dirname="phonon" name="Phonon"> <example filename="musicplayer" name="Music Player" /> </category> + <category dirname="multimedia/audio" name="Multimedia"> + <example filename="audiodevices" name="Audio Devices" /> + <example filename="audiooutput" name="Audio Output" /> + <example filename="audioinput" name="Audio Input" /> + </category> <category dirname="richtext" name="Rich Text"> <example filename="calendar" name="Calendar" /> <example filename="orderform" name="Order Form" /> diff --git a/demos/sub-attaq/custompropertyanimation.cpp b/demos/sub-attaq/custompropertyanimation.cpp index 9282f42..27a4eba 100644 --- a/demos/sub-attaq/custompropertyanimation.cpp +++ b/demos/sub-attaq/custompropertyanimation.cpp @@ -96,7 +96,7 @@ void CustomPropertyAnimation::updateState(QAbstractAnimation::State oldState, QA setKeyValues(values); } - if (animProp && !startValue().isValid() && currentTime() == 0 + if ((animProp && !startValue().isValid() && currentTime() == 0) || (currentTime() == duration() && currentLoop() == (loopCount() - 1))) { setStartValue(def); } diff --git a/demos/sub-attaq/states.cpp b/demos/sub-attaq/states.cpp index d63737f..d6c0b5a 100644 --- a/demos/sub-attaq/states.cpp +++ b/demos/sub-attaq/states.cpp @@ -269,9 +269,10 @@ void UpdateScoreState::onEntry(QEvent *e) /** Win transition */ UpdateScoreTransition::UpdateScoreTransition(GraphicsScene *scene, PlayState *game, QAbstractState *target) - : QSignalTransition(scene,SIGNAL(subMarineDestroyed(int)), QList<QAbstractState*>() << target), + : QSignalTransition(scene,SIGNAL(subMarineDestroyed(int))), game(game), scene(scene) { + setTargetState(target); } bool UpdateScoreTransition::eventTest(QEvent *event) @@ -288,9 +289,10 @@ bool UpdateScoreTransition::eventTest(QEvent *event) /** Win transition */ WinTransition::WinTransition(GraphicsScene *scene, PlayState *game, QAbstractState *target) - : QSignalTransition(scene,SIGNAL(allSubMarineDestroyed(int)), QList<QAbstractState*>() << target), + : QSignalTransition(scene,SIGNAL(allSubMarineDestroyed(int))), game(game), scene(scene) { + setTargetState(target); } bool WinTransition::eventTest(QEvent *event) diff --git a/dist/changes-4.6.0 b/dist/changes-4.6.0 index 383c6b7..7a6decf 100644 --- a/dist/changes-4.6.0 +++ b/dist/changes-4.6.0 @@ -34,9 +34,15 @@ information about a particular change. - Significant external contribution from Milan Burda for planned removal of (non-unicode) Windows 9x/ME support. + - QRegion is no longer a GDI object by default. This means it is no + longer subjuect to gui-thread only nor does it potentially impact + the 10.000 GDI object limit per process. By explicitly calling + .handle() a GDI object will be created and memory managed by + QRegion. The native handle is for reading out only. Any GDI calls + made on the HRGN handle will not affect the QRegion. **************************************************************************** -* Important Behavior Changes * +* Important Behavior Changes * **************************************************************************** - The experimental Direct3D paint engine has been removed. The reason for @@ -71,33 +77,32 @@ information about a particular change. QGraphicsItem::ItemSendsGeometryChanges (which is enabled by default by QGraphicsWidget and QGraphicsProxyWidget). -- QDesktopWidget on X11 no longer emits the resized(int) signal when screens - are added or removed. This was not done on other platforms. Use the - screenCountChanged signal instead - -- QUrl's parser is more strict when for hostnames in URLs. QUrl now - enforces STD 3 rules: - - * each individual hostname section (between dots) must be at most - 63 ASCII characters in length; - - * only letters, digits, and the hyphen character are allowed in the - ASCII range; letters outside the ASCII range follow the normal - IDN rules - - That means QUrl no longer accepts some URLs that were invalid - before, but weren't interpreted as such. - -- The Unix configure-time check for STL is stricter now in Qt - 4.6.0. This means some legacy STL implementations may fail to pass - the test and, therefore, Qt will automatically disable STL support. - - This is a binary-compatible change: existing code will continue to - work without being recompiled. However, it affects the source code, - since some STL-compatibility API will not be enabled. - - Platforms affected by this change: - * solaris-cc-* with the default (Cstd) C++ STL library - recommendation: use -library=stlport4 - See Sun Studio's documentation for the effects of this option - + - QDesktopWidget on X11 no longer emits the resized(int) signal when screens + are added or removed. This was not done on other platforms. Use the + screenCountChanged signal instead + + - QUrl's parser is more strict when for hostnames in URLs. QUrl now + enforces STD 3 rules: + + * each individual hostname section (between dots) must be at most + 63 ASCII characters in length; + + * only letters, digits, and the hyphen character are allowed in the + ASCII range; letters outside the ASCII range follow the normal + IDN rules + + That means QUrl no longer accepts some URLs that were invalid + before, but weren't interpreted as such. + + - The Unix configure-time check for STL is stricter now in Qt + 4.6.0. This means some legacy STL implementations may fail to pass + the test and, therefore, Qt will automatically disable STL support. + + This is a binary-compatible change: existing code will continue to + work without being recompiled. However, it affects the source code, + since some STL-compatibility API will not be enabled. + + Platforms affected by this change: + * solaris-cc-* with the default (Cstd) C++ STL library + recommendation: use -library=stlport4 + See Sun Studio's documentation for the effects of this option diff --git a/doc/src/classes.qdoc b/doc/src/classes.qdoc index 9a5d3ec..e955a5a 100644 --- a/doc/src/classes.qdoc +++ b/doc/src/classes.qdoc @@ -40,6 +40,18 @@ ****************************************************************************/ /*! + \group groups + \title Grouped Classes + \ingroup classlists + + This page provides a way of navigating Qt's classes by grouping + related classes together. Some classes may appear in more than one group. + + \generatelist{related} + +*/ + +/*! \page classes.html \title Qt's Classes \ingroup classlists @@ -65,3 +77,77 @@ \generatelist{namespaces} */ + +/*! + \page obsoleteclasses.html + \title Obsolete Classes + \ingroup classlists + + + This is a list of Qt classes that are obsolete (deprecated). These + classes are provided to keep old source code working but they are + no longer maintained. We strongly advise against using these + classes in new code. + + \generatelist obsoleteclasses + + \sa {Qt's Classes}, {Qt's Modules} +*/ + +/*! + \page annotated.html + \title Annotated Class Index + \ingroup classlists + + Qt's classes with brief descriptions: + + \generatelist annotatedclasses +*/ + +/*! + \page functions.html + \title Member Function Index + \ingroup classlists + + Here is the list of all the documented member functions in the Qt + API with links to the class documentation for each function. + + \generatelist functionindex +*/ + +/*! + \page hierarchy.html + + \title Class Inheritance Hierarchy + \ingroup classlists + + This list shows the C++ class inheritance relations between the + classes in the Qt API. + + \generatelist classhierarchy +*/ + +/*! + \page mainclasses.html + \title Qt's Main Classes + \ingroup classlists + + These are the most frequently used Qt classes. For the complete + list see \link classes.html Qt's Classes \endlink. + + \generatelist mainclasses +*/ + +/*! + \page compatclasses.html + \title Qt 3 Compatibility Classes + \ingroup classlists + + This is a list of the classes that Qt provides for compatibility + with Qt 3. The vast majority of these are provided by the + Qt3Support module. + + \generatelist compatclasses + + \sa {Qt's Classes}, {Qt's Modules} +*/ diff --git a/doc/src/phonon-api.qdoc b/doc/src/classes/phonon-api.qdoc index 09274bf..09274bf 100644 --- a/doc/src/phonon-api.qdoc +++ b/doc/src/classes/phonon-api.qdoc diff --git a/doc/src/q3asciicache.qdoc b/doc/src/classes/q3asciicache.qdoc index 43537cc..43537cc 100644 --- a/doc/src/q3asciicache.qdoc +++ b/doc/src/classes/q3asciicache.qdoc diff --git a/doc/src/q3asciidict.qdoc b/doc/src/classes/q3asciidict.qdoc index 9a51db1..9a51db1 100644 --- a/doc/src/q3asciidict.qdoc +++ b/doc/src/classes/q3asciidict.qdoc diff --git a/doc/src/q3cache.qdoc b/doc/src/classes/q3cache.qdoc index d8799b6..d8799b6 100644 --- a/doc/src/q3cache.qdoc +++ b/doc/src/classes/q3cache.qdoc diff --git a/doc/src/q3dict.qdoc b/doc/src/classes/q3dict.qdoc index 0e6d51d..0e6d51d 100644 --- a/doc/src/q3dict.qdoc +++ b/doc/src/classes/q3dict.qdoc diff --git a/doc/src/q3intcache.qdoc b/doc/src/classes/q3intcache.qdoc index dfff679..dfff679 100644 --- a/doc/src/q3intcache.qdoc +++ b/doc/src/classes/q3intcache.qdoc diff --git a/doc/src/q3intdict.qdoc b/doc/src/classes/q3intdict.qdoc index cef2e79..cef2e79 100644 --- a/doc/src/q3intdict.qdoc +++ b/doc/src/classes/q3intdict.qdoc diff --git a/doc/src/q3memarray.qdoc b/doc/src/classes/q3memarray.qdoc index b9c1f73..b9c1f73 100644 --- a/doc/src/q3memarray.qdoc +++ b/doc/src/classes/q3memarray.qdoc diff --git a/doc/src/q3popupmenu.qdoc b/doc/src/classes/q3popupmenu.qdoc index a2cfe08..a2cfe08 100644 --- a/doc/src/q3popupmenu.qdoc +++ b/doc/src/classes/q3popupmenu.qdoc diff --git a/doc/src/q3ptrdict.qdoc b/doc/src/classes/q3ptrdict.qdoc index 38ca0bb..38ca0bb 100644 --- a/doc/src/q3ptrdict.qdoc +++ b/doc/src/classes/q3ptrdict.qdoc diff --git a/doc/src/q3ptrlist.qdoc b/doc/src/classes/q3ptrlist.qdoc index 3000940..3000940 100644 --- a/doc/src/q3ptrlist.qdoc +++ b/doc/src/classes/q3ptrlist.qdoc diff --git a/doc/src/q3ptrqueue.qdoc b/doc/src/classes/q3ptrqueue.qdoc index b3af5f6..b3af5f6 100644 --- a/doc/src/q3ptrqueue.qdoc +++ b/doc/src/classes/q3ptrqueue.qdoc diff --git a/doc/src/q3ptrstack.qdoc b/doc/src/classes/q3ptrstack.qdoc index 1650d69..1650d69 100644 --- a/doc/src/q3ptrstack.qdoc +++ b/doc/src/classes/q3ptrstack.qdoc diff --git a/doc/src/q3ptrvector.qdoc b/doc/src/classes/q3ptrvector.qdoc index fa78de5..fa78de5 100644 --- a/doc/src/q3ptrvector.qdoc +++ b/doc/src/classes/q3ptrvector.qdoc diff --git a/doc/src/q3sqlfieldinfo.qdoc b/doc/src/classes/q3sqlfieldinfo.qdoc index ba064f3..ba064f3 100644 --- a/doc/src/q3sqlfieldinfo.qdoc +++ b/doc/src/classes/q3sqlfieldinfo.qdoc diff --git a/doc/src/q3sqlrecordinfo.qdoc b/doc/src/classes/q3sqlrecordinfo.qdoc index 64236d2..64236d2 100644 --- a/doc/src/q3sqlrecordinfo.qdoc +++ b/doc/src/classes/q3sqlrecordinfo.qdoc diff --git a/doc/src/q3valuelist.qdoc b/doc/src/classes/q3valuelist.qdoc index fd73763..fd73763 100644 --- a/doc/src/q3valuelist.qdoc +++ b/doc/src/classes/q3valuelist.qdoc diff --git a/doc/src/q3valuestack.qdoc b/doc/src/classes/q3valuestack.qdoc index e3ae677..e3ae677 100644 --- a/doc/src/q3valuestack.qdoc +++ b/doc/src/classes/q3valuestack.qdoc diff --git a/doc/src/q3valuevector.qdoc b/doc/src/classes/q3valuevector.qdoc index 353b7fa..353b7fa 100644 --- a/doc/src/q3valuevector.qdoc +++ b/doc/src/classes/q3valuevector.qdoc diff --git a/doc/src/qalgorithms.qdoc b/doc/src/classes/qalgorithms.qdoc index 7634322..7634322 100644 --- a/doc/src/qalgorithms.qdoc +++ b/doc/src/classes/qalgorithms.qdoc diff --git a/doc/src/qcache.qdoc b/doc/src/classes/qcache.qdoc index 6c88ede..6c88ede 100644 --- a/doc/src/qcache.qdoc +++ b/doc/src/classes/qcache.qdoc diff --git a/doc/src/qcolormap.qdoc b/doc/src/classes/qcolormap.qdoc index 95f7dc0..95f7dc0 100644 --- a/doc/src/qcolormap.qdoc +++ b/doc/src/classes/qcolormap.qdoc diff --git a/doc/src/qdesktopwidget.qdoc b/doc/src/classes/qdesktopwidget.qdoc index 56a882d..56a882d 100644 --- a/doc/src/qdesktopwidget.qdoc +++ b/doc/src/classes/qdesktopwidget.qdoc diff --git a/doc/src/qiterator.qdoc b/doc/src/classes/qiterator.qdoc index 416b4bc..416b4bc 100644 --- a/doc/src/qiterator.qdoc +++ b/doc/src/classes/qiterator.qdoc diff --git a/doc/src/qstyles.qdoc b/doc/src/classes/qmacstyle.qdoc index ae2d95b..ae2d95b 100644 --- a/doc/src/qstyles.qdoc +++ b/doc/src/classes/qmacstyle.qdoc diff --git a/doc/src/qnamespace.qdoc b/doc/src/classes/qnamespace.qdoc index 2d40fdd..a49e079 100644 --- a/doc/src/qnamespace.qdoc +++ b/doc/src/classes/qnamespace.qdoc @@ -1884,7 +1884,10 @@ We refer to a top-level window that has a parent as a \e secondary window. - \value Sheet Indicates that the widget is a Macintosh sheet. + \value Sheet Indicates that the window is a Macintosh sheet. Since + using a sheet implies window modality, the recommended + way is to use QWidget::setWindowModality(), or + QDialog::open(), instead. \value Drawer Indicates that the widget is a Macintosh drawer. @@ -2415,6 +2418,30 @@ */ /*! + \enum Qt::InputMethodHint + + \value ImhNone No hints. + \value ImhHiddenText Characters should be hidden, as is typically used when entering passwords. + This is automatically set when setting QLineEdit::echoMode to \c Password. + \value ImhNumbersOnly Only number input is allowed. + \value ImhUppercaseOnly Only upper case letter input is allowed. + \value ImhLowercaseOnly Only lower case letter input is allowed. + \value ImhNoAutoUppercase The input method should not try to automatically switch to upper case + when a sentence ends. + \value ImhPreferNumbers Numbers are preferred (but not required). + \value ImhPreferUppercase Upper case letters are preferred (but not required). + \value ImhPreferLowercase Lower case letters are preferred (but not required). + \value ImhNoPredictiveText Do not use predictive text (i.e. dictionary lookup) while typing. + \value ImhDialableCharactersOnly Only characters suitable for phone dialling are allowed. + + \note If several flags ending with \c Only are ORed together, the resulting character set will + consist of the union of the specified sets. For instance specifying \c ImhNumbersOnly and + \c ImhUppercaseOnly would yield a set consisting of numbers and uppercase letters. + + \sa QGraphicsItem::inputMethodHints() +*/ + +/*! \enum Qt::InputMethodQuery \value ImMicroFocus The rectangle covering the area of the input cursor in widget coordinates. diff --git a/doc/src/qpagesetupdialog.qdoc b/doc/src/classes/qpagesetupdialog.qdoc index 7f0b09e..7f0b09e 100644 --- a/doc/src/qpagesetupdialog.qdoc +++ b/doc/src/classes/qpagesetupdialog.qdoc diff --git a/doc/src/qpaintdevice.qdoc b/doc/src/classes/qpaintdevice.qdoc index 0f4e9a0..0f4e9a0 100644 --- a/doc/src/qpaintdevice.qdoc +++ b/doc/src/classes/qpaintdevice.qdoc diff --git a/doc/src/qpair.qdoc b/doc/src/classes/qpair.qdoc index 6d8a0f9..6d8a0f9 100644 --- a/doc/src/qpair.qdoc +++ b/doc/src/classes/qpair.qdoc diff --git a/doc/src/qpatternistdummy.cpp b/doc/src/classes/qpatternistdummy.cpp index a690184..a690184 100644 --- a/doc/src/qpatternistdummy.cpp +++ b/doc/src/classes/qpatternistdummy.cpp diff --git a/doc/src/qplugin.qdoc b/doc/src/classes/qplugin.qdoc index 4fbd198..4fbd198 100644 --- a/doc/src/qplugin.qdoc +++ b/doc/src/classes/qplugin.qdoc diff --git a/doc/src/qprintdialog.qdoc b/doc/src/classes/qprintdialog.qdoc index 8011f62..8011f62 100644 --- a/doc/src/qprintdialog.qdoc +++ b/doc/src/classes/qprintdialog.qdoc diff --git a/doc/src/qprinterinfo.qdoc b/doc/src/classes/qprinterinfo.qdoc index a4ffeb2..a4ffeb2 100644 --- a/doc/src/qprinterinfo.qdoc +++ b/doc/src/classes/qprinterinfo.qdoc diff --git a/doc/src/qset.qdoc b/doc/src/classes/qset.qdoc index 0db3775..0db3775 100644 --- a/doc/src/qset.qdoc +++ b/doc/src/classes/qset.qdoc diff --git a/doc/src/qsignalspy.qdoc b/doc/src/classes/qsignalspy.qdoc index 4ee7590..4ee7590 100644 --- a/doc/src/qsignalspy.qdoc +++ b/doc/src/classes/qsignalspy.qdoc diff --git a/doc/src/qsizepolicy.qdoc b/doc/src/classes/qsizepolicy.qdoc index c74beb8..c74beb8 100644 --- a/doc/src/qsizepolicy.qdoc +++ b/doc/src/classes/qsizepolicy.qdoc diff --git a/doc/src/classes/qtdesigner-api.qdoc b/doc/src/classes/qtdesigner-api.qdoc new file mode 100644 index 0000000..d7c47b1 --- /dev/null +++ b/doc/src/classes/qtdesigner-api.qdoc @@ -0,0 +1,1413 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QDesignerMemberSheetExtension + + \brief The QDesignerMemberSheetExtension class allows you to + manipulate a widget's member functions which is displayed when + configuring connections using Qt Designer's mode for editing + signals and slots. + + \inmodule QtDesigner + + QDesignerMemberSheetExtension is a collection of functions that is + typically used to query a widget's member functions, and to + manipulate the member functions' appearance in \QD's signals and + slots editing mode. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 2 + + When implementing a custom widget plugin, a pointer to \QD's + current QDesignerFormEditorInterface object (\c formEditor in the + example above) is provided by the + QDesignerCustomWidgetInterface::initialize() function's parameter. + + The member sheet (and any other extension), can be retrieved by + querying \QD's extension manager using the qt_extension() + function. When you want to release the extension, you only need to + delete the pointer. + + All widgets have a default member sheet used in \QD's signals and + slots editing mode with the widget's member functions. But + QDesignerMemberSheetExtension also provides an interface for + creating custom member sheet extensions. + + \warning \QD uses the QDesignerMemberSheetExtension to facilitate + the signal and slot editing mode. Whenever a connection between + two widgets is requested, \QD will query for the widgets' member + sheet extensions. If a widget has an implemented member sheet + extension, this extension will override the default member sheet. + + To create a member sheet extension, your extension class must + inherit from both QObject and QDesignerMemberSheetExtension. Then, + since we are implementing an interface, we must ensure that it's + made known to the meta object system using the Q_INTERFACES() + macro: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 3 + + This enables \QD to use qobject_cast() to query for + supported interfaces using nothing but a QObject pointer. + + In \QD the extensions are not created until they are + required. For that reason, when implementing a member sheet + extension, you must also create a QExtensionFactory, i.e a class + that is able to make an instance of your extension, and register + it using \QD's \l {QExtensionManager}{extension manager}. + + When a widget's member sheet extension is required, \QD's \l + {QExtensionManager}{extension manager} will run through all its + registered factories calling QExtensionFactory::createExtension() + for each until the first one that is able to create a member sheet + extension for that widget, is found. This factory will then make + an instance of the extension. If no such factory is found, \QD + will use the default member sheet. + + There are four available types of extensions in \QD: + QDesignerContainerExtension, QDesignerMemberSheetExtension, + QDesignerPropertySheetExtension and + QDesignerTaskMenuExtension. \QD's behavior is the same whether the + requested extension is associated with a multi page container, a + member sheet, a property sheet or a task menu. + + The QExtensionFactory class provides a standard extension + factory, and can also be used as an interface for custom + extension factories. You can either create a new + QExtensionFactory and reimplement the + QExtensionFactory::createExtension() function. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 4 + + Or you can use an existing factory, expanding the + QExtensionFactory::createExtension() function to make the factory + able to create a member sheet extension as well. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 5 + + For a complete example using an extension class, see \l + {designer/taskmenuextension}{Task Menu Extension example}. The + example shows how to create a custom widget plugin for Qt + Designer, and how to to use the QDesignerTaskMenuExtension class + to add custom items to \QD's task menu. + + \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget + Extensions} +*/ + +/*! + \fn QDesignerMemberSheetExtension::~QDesignerMemberSheetExtension() + + Destroys the member sheet extension. +*/ + +/*! + \fn int QDesignerMemberSheetExtension::count() const + + Returns the extension's number of member functions. +*/ + +/*! + \fn int QDesignerMemberSheetExtension::indexOf(const QString &name) const + + Returns the index of the member function specified by the given \a + name. + + \sa memberName() +*/ + +/*! + \fn QString QDesignerMemberSheetExtension::memberName(int index) const + + Returns the name of the member function with the given \a index. + + \sa indexOf() +*/ + +/*! + \fn QString QDesignerMemberSheetExtension::memberGroup(int index) const + + Returns the name of the member group specified for the function + with the given \a index. + + \sa indexOf(), setMemberGroup() +*/ + +/*! + \fn void QDesignerMemberSheetExtension::setMemberGroup(int index, const QString &group) + + Sets the member group of the member function with the given \a + index, to \a group. + + \sa indexOf(), memberGroup() +*/ + +/*! + \fn bool QDesignerMemberSheetExtension::isVisible(int index) const + + Returns true if the member function with the given \a index is + visible in \QD's signal and slot editor, otherwise false. + + \sa indexOf(), setVisible() +*/ + +/*! + \fn void QDesignerMemberSheetExtension::setVisible(int index, bool visible) + + If \a visible is true, the member function with the given \a index + is visible in \QD's signals and slots editing mode; otherwise the + member function is hidden. + + \sa indexOf(), isVisible() +*/ + +/*! + \fn virtual bool QDesignerMemberSheetExtension::isSignal(int index) const + + Returns true if the member function with the given \a index is a + signal, otherwise false. + + \sa indexOf() +*/ + +/*! + \fn bool QDesignerMemberSheetExtension::isSlot(int index) const + + Returns true if the member function with the given \a index is a + slot, otherwise false. + + \sa indexOf() +*/ + +/*! + \fn bool QDesignerMemberSheetExtension::inheritedFromWidget(int index) const + + Returns true if the member function with the given \a index is + inherited from QWidget, otherwise false. + + \sa indexOf() +*/ + +/*! + \fn QString QDesignerMemberSheetExtension::declaredInClass(int index) const + + Returns the name of the class in which the member function with + the given \a index is declared. + + \sa indexOf() +*/ + +/*! + \fn QString QDesignerMemberSheetExtension::signature(int index) const + + Returns the signature of the member function with the given \a + index. + + \sa indexOf() +*/ + +/*! + \fn QList<QByteArray> QDesignerMemberSheetExtension::parameterTypes(int index) const + + Returns the parameter types of the member function with the given + \a index, as a QByteArray list. + + \sa indexOf(), parameterNames() +*/ + +/*! + \fn QList<QByteArray> QDesignerMemberSheetExtension::parameterNames(int index) const + + Returns the parameter names of the member function with the given + \a index, as a QByteArray list. + + \sa indexOf(), parameterTypes() +*/ + + +// Doc: Interface only + +/*! + \class QDesignerLayoutDecorationExtension + \brief The QDesignerLayoutDecorationExtension class provides an extension to a layout in \QD. + \inmodule QtDesigner + \internal +*/ + +/*! + \enum QDesignerLayoutDecorationExtension::InsertMode + + This enum describes the modes that are used to insert items into a layout. + + \value InsertWidgetMode Widgets are inserted into empty cells in a layout. + \value InsertRowMode Whole rows are inserted into a vertical or grid layout. + \value InsertColumnMode Whole columns are inserted into a horizontal or grid layout. +*/ + +/*! + \fn virtual QDesignerLayoutDecorationExtension::~QDesignerLayoutDecorationExtension() + + Destroys the extension. +*/ + +/*! + \fn virtual QList<QWidget*> QDesignerLayoutDecorationExtension::widgets(QLayout *layout) const + + Returns the widgets that are managed by the given \a layout. + + \sa insertWidget(), removeWidget() +*/ + +/*! + \fn QRect QDesignerLayoutDecorationExtension::itemInfo(int index) const + + Returns the rectangle covered by the item at the given \a index in the layout. +*/ + +/*! + \fn int QDesignerLayoutDecorationExtension::indexOf(QWidget *widget) const + + Returns the index of the specified \a widget in the layout. +*/ + +/*! + \fn int QDesignerLayoutDecorationExtension::indexOf(QLayoutItem *item) const + + Returns the index of the specified layout \a item. +*/ + +/*! + \fn QDesignerLayoutDecorationExtension::InsertMode QDesignerLayoutDecorationExtension::currentInsertMode() const + + Returns the current insertion mode. +*/ + +/*! + \fn int QDesignerLayoutDecorationExtension::currentIndex() const + + Returns the current index in the layout. +*/ + +/*! + \fn QPair<int, int> QDesignerLayoutDecorationExtension::currentCell() const + + Returns a pair containing the row and column of the current cell in the layout. +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::insertWidget(QWidget *widget, const QPair<int, int> &cell) + + Inserts the given \a widget into the specified \a cell in the layout. + + \sa removeWidget() +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::removeWidget(QWidget *widget) + + Removes the specified \a widget from the layout. + + \sa insertWidget() +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::insertRow(int row) + + Inserts a new row into the form at the position specified by \a row. +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::insertColumn(int column) + + Inserts a new column into the form at the position specified by \a column. +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::simplify() + + Simplifies the layout by removing unnecessary empty rows and columns, and by changing the + number of rows or columns spanned by widgets. +*/ + +/*! + \fn int QDesignerLayoutDecorationExtension::findItemAt(const QPoint &position) const + + Returns the index of the item in the layout that covers the given \a position. +*/ + +/*! + \fn int QDesignerLayoutDecorationExtension::findItemAt(int row, int column) const + + Returns the item in the layout that occupies the specified \a row and \a column in the layout. + + Currently, this only applies to grid layouts. +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::adjustIndicator(const QPoint &position, int index) + + Adjusts the indicator for the item specified by \a index so that + it lies at the given \a position on the form. +*/ + + +// Doc: Interface only + +/*! + \class QDesignerContainerExtension + \brief The QDesignerContainerExtension class allows you to add pages to + a custom multi-page container in Qt Designer's workspace. + \inmodule QtDesigner + + QDesignerContainerExtension provide an interface for creating + custom container extensions. A container extension consists of a + collection of functions that \QD needs to manage a multi-page + container plugin, and a list of the container's pages. + + \image containerextension-example.png + + \warning This is \e not an extension for container plugins in + general, only custom \e multi-page containers. + + To create a container extension, your extension class must inherit + from both QObject and QDesignerContainerExtension. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 6 + + Since we are implementing an interface, we must ensure that it's + made known to the meta object system using the Q_INTERFACES() + macro. This enables \QD to use the qobject_cast() function to + query for supported interfaces using nothing but a QObject + pointer. + + You must reimplement several functions to enable \QD to manage a + custom multi-page container widget: \QD uses count() to keep track + of the number pages in your container, widget() to return the page + at a given index in the list of the container's pages, and + currentIndex() to return the list index of the selected page. \QD + uses the addWidget() function to add a given page to the + container, expecting it to be appended to the list of pages, while + it expects the insertWidget() function to add a given page to the + container by inserting it at a given index. + + In \QD the extensions are not created until they are + required. For that reason you must also create a + QExtensionFactory, i.e a class that is able to make an instance of + your extension, and register it using \QD's \l + {QExtensionManager}{extension manager}. + + When a container extension is required, \QD's \l + {QExtensionManager}{extension manager} will run through all its + registered factories calling QExtensionFactory::createExtension() + for each until the first one that is able to create a container + extension, is found. This factory will then create the extension + for the plugin. + + There are four available types of extensions in \QD: + QDesignerContainerExtension , QDesignerMemberSheetExtension, + QDesignerPropertySheetExtension and QDesignerTaskMenuExtension. + \QD's behavior is the same whether the requested extension is + associated with a multi page container, a member sheet, a property + sheet or a task menu. + + The QExtensionFactory class provides a standard extension factory, + and can also be used as an interface for custom extension + factories. You can either create a new QExtensionFactory and + reimplement the QExtensionFactory::createExtension() function. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 7 + + Or you can use an existing factory, expanding the + QExtensionFactory::createExtension() function to make the factory + able to create a container extension as well. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 8 + + For a complete example using the QDesignerContainerExtension + class, see the \l {designer/containerextension}{Container + Extension example}. The example shows how to create a custom + multi-page plugin for \QD. + + \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget + Extensions} +*/ + +/*! + \fn QDesignerContainerExtension::~QDesignerContainerExtension() + + Destroys the extension. +*/ + +/*! + \fn int QDesignerContainerExtension::count() const + + Returns the number of pages in the container. +*/ + +/*! + \fn QWidget *QDesignerContainerExtension::widget(int index) const + + Returns the page at the given \a index in the extension's list of + pages. + + \sa addWidget(), insertWidget() +*/ + +/*! + \fn int QDesignerContainerExtension::currentIndex() const + + Returns the index of the currently selected page in the + container. + + \sa setCurrentIndex() +*/ + +/*! + \fn void QDesignerContainerExtension::setCurrentIndex(int index) + + Sets the currently selected page in the container to be the + page at the given \a index in the extension's list of pages. + + \sa currentIndex() +*/ + +/*! + \fn void QDesignerContainerExtension::addWidget(QWidget *page) + + Adds the given \a page to the container by appending it to the + extension's list of pages. + + \sa insertWidget(), remove(), widget() +*/ + +/*! + \fn void QDesignerContainerExtension::insertWidget(int index, QWidget *page) + + Adds the given \a page to the container by inserting it at the + given \a index in the extension's list of pages. + + \sa addWidget(), remove(), widget() +*/ + +/*! + \fn void QDesignerContainerExtension::remove(int index) + + Removes the page at the given \a index from the extension's list + of pages. + + \sa addWidget(), insertWidget() +*/ + + +// Doc: Interface only + +/*! + \class QDesignerTaskMenuExtension + \brief The QDesignerTaskMenuExtension class allows you to add custom + menu entries to Qt Designer's task menu. + \inmodule QtDesigner + + QDesignerTaskMenuExtension provides an interface for creating + custom task menu extensions. It is typically used to create task + menu entries that are specific to a plugin in \QD. + + \QD uses the QDesignerTaskMenuExtension to feed its task + menu. Whenever a task menu is requested, \QD will query + for the selected widget's task menu extension. + + \image taskmenuextension-example-faded.png + + A task menu extension is a collection of QActions. The actions + appear as entries in the task menu when the plugin with the + specified extension is selected. The image above shows the custom + \gui {Edit State...} action which appears in addition to \QD's + default task menu entries: \gui Cut, \gui Copy, \gui Paste etc. + + To create a custom task menu extension, your extension class must + inherit from both QObject and QDesignerTaskMenuExtension. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 9 + + Since we are implementing an interface, we must ensure that it + is made known to the meta-object system using the Q_INTERFACES() + macro. This enables \QD to use the qobject_cast() function to + query for supported interfaces using nothing but a QObject + pointer. + + You must reimplement the taskActions() function to return a list + of actions that will be included in \QD task menu. Optionally, you + can reimplement the preferredEditAction() function to set the + action that is invoked when selecting your plugin and pressing + \key F2. The preferred edit action must be one of the actions + returned by taskActions() and, if it's not defined, pressing the + \key F2 key will simply be ignored. + + In \QD, extensions are not created until they are required. A + task menu extension, for example, is created when you click the + right mouse button over a widget in \QD's workspace. For that + reason you must also construct an extension factory, using either + QExtensionFactory or a subclass, and register it using \QD's + \l {QExtensionManager}{extension manager}. + + When a task menu extension is required, \QD's \l + {QExtensionManager}{extension manager} will run through all its + registered factories calling QExtensionFactory::createExtension() + for each until it finds one that is able to create a task menu + extension for the selected widget. This factory will then make an + instance of the extension. + + There are four available types of extensions in \QD: + QDesignerContainerExtension, QDesignerMemberSheetExtension, + QDesignerPropertySheetExtension, and QDesignerTaskMenuExtension. + \QD's behavior is the same whether the requested extension is + associated with a container, a member sheet, a property sheet or a + task menu. + + The QExtensionFactory class provides a standard extension factory, + and can also be used as an interface for custom extension + factories. You can either create a new QExtensionFactory and + reimplement the QExtensionFactory::createExtension() function. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 10 + + Or you can use an existing factory, expanding the + QExtensionFactory::createExtension() function to make the factory + able to create a task menu extension as well. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 11 + + For a complete example using the QDesignerTaskMenuExtension class, + see the \l {designer/taskmenuextension}{Task Menu Extension + example}. The example shows how to create a custom widget plugin + for \QD, and how to to use the QDesignerTaskMenuExtension + class to add custom items to \QD's task menu. + + \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget + Extensions} +*/ + +/*! + \fn QDesignerTaskMenuExtension::~QDesignerTaskMenuExtension() + + Destroys the task menu extension. +*/ + +/*! + \fn QAction *QDesignerTaskMenuExtension::preferredEditAction() const + + Returns the action that is invoked when selecting a plugin with + the specified extension and pressing \key F2. + + The action must be one of the actions returned by taskActions(). +*/ + +/*! + \fn QList<QAction*> QDesignerTaskMenuExtension::taskActions() const + + Returns the task menu extension as a list of actions which will be + included in \QD's task menu when a plugin with the specified + extension is selected. + + The function must be reimplemented to add actions to the list. +*/ + + +// Doc: Interface only + +/*! + \class QDesignerCustomWidgetCollectionInterface + + \brief The QDesignerCustomWidgetCollectionInterface class allows + you to include several custom widgets in one single library. + + \inmodule QtDesigner + + When implementing a custom widget plugin, you build it as a + separate library. If you want to include several custom widget + plugins in the same library, you must in addition subclass + QDesignerCustomWidgetCollectionInterface. + + QDesignerCustomWidgetCollectionInterface contains one single + function returning a list of the collection's + QDesignerCustomWidgetInterface objects. For example, if you have + several custom widgets \c CustomWidgetOne, \c CustomWidgetTwo and + \c CustomWidgetThree, the class definition may look like this: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 12 + + In the class constructor you add the interfaces to your custom + widgets to the list which you return in the customWidgets() + function: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 13 + + Note that instead of exporting each custom widget plugin using the + Q_EXPORT_PLUGIN2() macro, you export the entire collection. The + Q_EXPORT_PLUGIN2() macro ensures that \QD can access and construct + the custom widgets. Without this macro, there is no way for \QD to + use them. + + \sa QDesignerCustomWidgetInterface, {Creating Custom Widgets for + Qt Designer} +*/ + +/*! + \fn QDesignerCustomWidgetCollectionInterface::~QDesignerCustomWidgetCollectionInterface() { + + Destroys the custom widget collection interface. +*/ + +/*! + \fn QList<QDesignerCustomWidgetInterface*> QDesignerCustomWidgetCollectionInterface::customWidgets() const + + Returns a list of interfaces to the collection's custom widgets. +*/ + + +// Doc: Interface only + +/*! + \class QDesignerCustomWidgetInterface + + \brief The QDesignerCustomWidgetInterface class enables Qt Designer + to access and construct custom widgets. + + \inmodule QtDesigner + + QDesignerCustomWidgetInterface provides a custom widget with an + interface. The class contains a set of functions that must be subclassed + to return basic information about the widget, such as its class name and + the name of its header file. Other functions must be implemented to + initialize the plugin when it is loaded, and to construct instances of + the custom widget for \QD to use. + + When implementing a custom widget you must subclass + QDesignerCustomWidgetInterface to expose your widget to \QD. For + example, this is the declaration for the plugin used in the + \l{Custom Widget Plugin Example}{Custom Widget Plugin example} that + enables an analog clock custom widget to be used by \QD: + + \snippet examples/designer/customwidgetplugin/customwidgetplugin.h 0 + + Note that the only part of the class definition that is specific + to this particular custom widget is the class name. In addition, + since we are implementing an interface, we must ensure that it's + made known to the meta object system using the Q_INTERFACES() + macro. This enables \QD to use the qobject_cast() function to + query for supported interfaces using nothing but a QObject + pointer. + + After \QD loads a custom widget plugin, it calls the interface's + initialize() function to enable it to set up any resources that it + may need. This function is called with a QDesignerFormEditorInterface + parameter that provides the plugin with a gateway to all of \QD's API. + + \QD constructs instances of the custom widget by calling the plugin's + createWidget() function with a suitable parent widget. Plugins must + construct and return an instance of a custom widget with the specified + parent widget. + + In the implementation of the class you must remember to export + your custom widget plugin to \QD using the Q_EXPORT_PLUGIN2() + macro. For example, if a library called \c libcustomwidgetplugin.so + (on Unix) or \c libcustomwidget.dll (on Windows) contains a widget + class called \c MyCustomWidget, we can export it by adding the + following line to the file containing the plugin implementation: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 14 + + This macro ensures that \QD can access and construct the custom widget. + Without this macro, there is no way for \QD to use it. + + When implementing a custom widget plugin, you build it as a + separate library. If you want to include several custom widget + plugins in the same library, you must in addition subclass + QDesignerCustomWidgetCollectionInterface. + + \warning If your custom widget plugin contains QVariant + properties, be aware that only the following \l + {QVariant::Type}{types} are supported: + + \list + \o QVariant::ByteArray + \o QVariant::Bool + \o QVariant::Color + \o QVariant::Cursor + \o QVariant::Date + \o QVariant::DateTime + \o QVariant::Double + \o QVariant::Int + \o QVariant::Point + \o QVariant::Rect + \o QVariant::Size + \o QVariant::SizePolicy + \o QVariant::String + \o QVariant::Time + \o QVariant::UInt + \endlist + + For a complete example using the QDesignerCustomWidgetInterface + class, see the \l {designer/customwidgetplugin}{Custom Widget + Example}. The example shows how to create a custom widget plugin + for \QD. + + \sa QDesignerCustomWidgetCollectionInterface {Creating Custom + Widgets for Qt Designer} +*/ + +/*! + \fn QDesignerCustomWidgetInterface::~QDesignerCustomWidgetInterface() + + Destroys the custom widget interface. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::name() const + + Returns the class name of the custom widget supplied by the interface. + + The name returned \e must be identical to the class name used for the + custom widget. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::group() const + + Returns the name of the group to which the custom widget belongs. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::toolTip() const + + Returns a short description of the widget that can be used by \QD + in a tool tip. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::whatsThis() const + + Returns a description of the widget that can be used by \QD in + "What's This?" help for the widget. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::includeFile() const + + Returns the path to the include file that \l uic uses when + creating code for the custom widget. +*/ + +/*! + \fn QIcon QDesignerCustomWidgetInterface::icon() const + + Returns the icon used to represent the custom widget in \QD's + widget box. +*/ + +/*! + \fn bool QDesignerCustomWidgetInterface::isContainer() const + + Returns true if the custom widget is intended to be used as a + container; otherwise returns false. + + Most custom widgets are not used to hold other widgets, so their + implementations of this function will return false, but custom + containers will return true to ensure that they behave correctly + in \QD. +*/ + +/*! + \fn QWidget *QDesignerCustomWidgetInterface::createWidget(QWidget *parent) + + Returns a new instance of the custom widget, with the given \a + parent. +*/ + +/*! + \fn bool QDesignerCustomWidgetInterface::isInitialized() const + + Returns true if the widget has been initialized; otherwise returns + false. + + \sa initialize() +*/ + +/*! + \fn void QDesignerCustomWidgetInterface::initialize(QDesignerFormEditorInterface *formEditor) + + Initializes the widget for use with the specified \a formEditor + interface. + + \sa isInitialized() +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::domXml() const + + Returns the XML that is used to describe the custom widget's + properties to \QD. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::codeTemplate() const + + This function is reserved for future use by \QD. + + \omit + Returns the code template that \QD includes in forms that contain + the custom widget when they are saved. + \endomit +*/ + +/*! + \macro QDESIGNER_WIDGET_EXPORT + \relates QDesignerCustomWidgetInterface + \since 4.1 + + This macro is used when defining custom widgets to ensure that they are + correctly exported from plugins for use with \QD. + + On some platforms, the symbols required by \QD to create new widgets + are removed from plugins by the build system, making them unusable. + Using this macro ensures that the symbols are retained on those platforms, + and has no side effects on other platforms. + + For example, the \l{designer/worldtimeclockplugin}{World Time Clock Plugin} + example exports a custom widget class with the following declaration: + + \snippet examples/designer/worldtimeclockplugin/worldtimeclock.h 0 + \dots + \snippet examples/designer/worldtimeclockplugin/worldtimeclock.h 2 + + \sa {Creating Custom Widgets for Qt Designer} +*/ + + +// Doc: Abstract class + +/*! + \class QDesignerDnDItemInterface + \brief The QDesignerDnDItemInterface class provides an interface that is used to manage items + during a drag and drop operation. + \inmodule QtDesigner + \internal +*/ + +/*! + \enum QDesignerDnDItemInterface::DropType + + This enum describes the result of a drag and drop operation. + + \value MoveDrop The item was moved. + \value CopyDrop The item was copied. +*/ + +/*! + \fn QDesignerDnDItemInterface::QDesignerDnDItemInterface() + + Constructs a new interface to a drag and drop item. +*/ + +/*! + \fn QDesignerDnDItemInterface::~QDesignerDnDItemInterface() + + Destroys the interface to the item. +*/ + +/*! + \fn DomUI *QDesignerDnDItemInterface::domUi() const + + Returns a user interface object for the item. +*/ + +/*! + \fn QWidget *QDesignerDnDItemInterface::widget() const + + Returns the widget being copied or moved in the drag and drop operation. + + \sa source() +*/ + +/*! + \fn QWidget *QDesignerDnDItemInterface::decoration() const + + Returns the widget used to represent the item. +*/ + +/*! + \fn QPoint QDesignerDnDItemInterface::hotSpot() const + + Returns the cursor's hotspot. + + \sa QDrag::hotSpot() +*/ + +/*! + \fn DropType QDesignerDnDItemInterface::type() const + + Returns the type of drag and drop operation in progress. +*/ + +/*! + \fn QWidget *QDesignerDnDItemInterface::source() const + + Returns the widget that is the source of the drag and drop operation; i.e. the original + container of the widget being dragged. + + \sa widget() +*/ + + +// Doc: Abstract class + +/*! + \class QDesignerIconCacheInterface + \brief The QDesignerIconCacheInterface class provides an interface to \QD's icon cache. + \inmodule QtDesigner + \internal +*/ + +/*! + \fn QDesignerIconCacheInterface::QDesignerIconCacheInterface(QObject *parent) + + Constructs a new interface with the given \a parent. +*/ + +/*! + \fn QIcon QDesignerIconCacheInterface::nameToIcon(const QString &filePath, const QString &qrcPath) + + Returns the icon associated with the name specified by \a filePath in the resource + file specified by \a qrcPath. + + If \a qrcPath refers to a valid resource file, the name used for the file path is a path + within those resources; otherwise the file path refers to a local file. + + \sa {The Qt Resource System}, nameToPixmap() +*/ + +/*! + \fn QPixmap QDesignerIconCacheInterface::nameToPixmap(const QString &filePath, const QString &qrcPath) + + Returns the pixmap associated with the name specified by \a filePath in the resource + file specified by \a qrcPath. + + If \a qrcPath refers to a valid resource file, the name used for the file path is a path + within those resources; otherwise the file path refers to a local file. + + \sa {The Qt Resource System}, nameToIcon() +*/ + +/*! + \fn QString QDesignerIconCacheInterface::iconToFilePath(const QIcon &icon) const + + Returns the file path associated with the given \a icon. The file path is a path within + an application resources. +*/ + +/*! + \fn QString QDesignerIconCacheInterface::iconToQrcPath(const QIcon &icon) const + + Returns the path to the resource file that refers to the specified \a icon. The resource + path refers to a local file. +*/ + +/*! + \fn QString QDesignerIconCacheInterface::pixmapToFilePath(const QPixmap &pixmap) const + + Returns the file path associated with the given \a pixmap. The file path is a path within + an application resources. +*/ + +/*! + \fn QString QDesignerIconCacheInterface::pixmapToQrcPath(const QPixmap &pixmap) const + + Returns the path to the resource file that refers to the specified \a pixmap. The resource + path refers to a local file. +*/ + +/*! + \fn QList<QPixmap> QDesignerIconCacheInterface::pixmapList() const + + Returns a list of pixmaps for the icons provided by the icon cache. +*/ + +/*! + \fn QList<QIcon> QDesignerIconCacheInterface::iconList() const + + Returns a list of icons provided by the icon cache. +*/ + +/*! + \fn QString QDesignerIconCacheInterface::resolveQrcPath(const QString &filePath, const QString &qrcPath, const QString &workingDirectory) const + + Returns a path to a resource specified by the \a filePath within + the resource file located at \a qrcPath. If \a workingDirectory is + a valid path to a directory, the path returned will be relative to + that directory; otherwise an absolute path is returned. + + \omit + ### Needs checking + \endomit +*/ + + +// Doc: Interface only + +/*! + \class QDesignerPropertySheetExtension + + \brief The QDesignerPropertySheetExtension class allows you to + manipulate a widget's properties which is displayed in Qt + Designer's property editor. + + \sa QDesignerDynamicPropertySheetExtension + + \inmodule QtDesigner + + QDesignerPropertySheetExtension provides a collection of functions that + are typically used to query a widget's properties, and to + manipulate the properties' appearance in the property editor. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 15 + + Note that if you change the value of a property using the + QDesignerPropertySheetExtension::setProperty() function, the undo + stack is not updated. To ensure that a property's value can be + reverted using the undo stack, you must use the + QDesignerFormWindowCursorInterface::setProperty() function, or its + buddy \l + {QDesignerFormWindowCursorInterface::setWidgetProperty()}{setWidgetProperty()}, + instead. + + When implementing a custom widget plugin, a pointer to \QD's + current QDesignerFormEditorInterface object (\c formEditor in the + example above) is provided by the + QDesignerCustomWidgetInterface::initialize() function's parameter. + + The property sheet, or any other extension, can be retrieved by + querying \QD's extension manager using the qt_extension() + function. When you want to release the extension, you only need to + delete the pointer. + + All widgets have a default property sheet which populates \QD's + property editor with the widget's properties (i.e the ones defined + with the Q_PROPERTY() macro). But QDesignerPropertySheetExtension + also provides an interface for creating custom property sheet + extensions. + + \warning \QD uses the QDesignerPropertySheetExtension to feed its + property editor. Whenever a widget is selected in its workspace, + \QD will query for the widget's property sheet extension. If the + selected widget has an implemented property sheet extension, this + extension will override the default property sheet. + + To create a property sheet extension, your extension class must + inherit from both QObject and + QDesignerPropertySheetExtension. Then, since we are implementing + an interface, we must ensure that it's made known to the meta + object system using the Q_INTERFACES() macro: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 16 + + This enables \QD to use qobject_cast() to query for supported + interfaces using nothing but a QObject pointer. + + In \QD the extensions are not created until they are + required. For that reason, when implementing a property sheet + extension, you must also create a QExtensionFactory, i.e a class + that is able to make an instance of your extension, and register + it using \QD's \l {QExtensionManager}{extension manager}. + + When a property sheet extension is required, \QD's \l + {QExtensionManager}{extension manager} will run through all its + registered factories calling QExtensionFactory::createExtension() + for each until the first one that is able to create a property + sheet extension for the selected widget, is found. This factory + will then make an instance of the extension. If no such factory + can be found, \QD will use the default property sheet. + + There are four available types of extensions in \QD: + QDesignerContainerExtension, QDesignerMemberSheetExtension, + QDesignerPropertySheetExtension and QDesignerTaskMenuExtension. Qt + Designer's behavior is the same whether the requested extension is + associated with a multi page container, a member sheet, a property + sheet or a task menu. + + The QExtensionFactory class provides a standard extension factory, + and can also be used as an interface for custom extension + factories. You can either create a new QExtensionFactory and + reimplement the QExtensionFactory::createExtension() function. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 17 + + Or you can use an existing factory, expanding the + QExtensionFactory::createExtension() function to make the factory + able to create a property sheet extension extension as well. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 18 + + For a complete example using an extension class, see the \l + {designer/taskmenuextension}{Task Menu Extension example}. The + example shows how to create a custom widget plugin for Qt + Designer, and how to to use the QDesignerTaskMenuExtension class + to add custom items to \QD's task menu. + + \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget + Extensions} +*/ + +/*! + \fn QDesignerPropertySheetExtension::~QDesignerPropertySheetExtension() + + Destroys the property sheet extension. +*/ + +/*! + \fn int QDesignerPropertySheetExtension::count() const + + Returns the selected widget's number of properties. +*/ + +/*! + \fn int QDesignerPropertySheetExtension::indexOf(const QString &name) const + + Returns the index for a given property \a name. + + \sa propertyName() +*/ + +/*! + \fn QString QDesignerPropertySheetExtension::propertyName(int index) const + + Returns the name of the property at the given \a index. + + \sa indexOf() +*/ + +/*! + \fn QString QDesignerPropertySheetExtension::propertyGroup(int index) const + + Returns the property group for the property at the given \a index. + + \QD's property editor supports property groups, i.e. sections of + related properties. A property can be related to a group using the + setPropertyGroup() function. The default group of any property is + the name of the class that defines it. For example, the + QObject::objectName property appears within the QObject property + group. + + \sa indexOf(), setPropertyGroup() +*/ + +/*! + \fn void QDesignerPropertySheetExtension::setPropertyGroup(int index, const QString &group) + + Sets the property group for the property at the given \a index to + \a group. + + Relating a property to a group makes it appear within that group's + section in the property editor. The default property group of any + property is the name of the class that defines it. For example, + the QObject::objectName property appears within the QObject + property group. + + \sa indexOf(), property(), propertyGroup() +*/ + +/*! + \fn bool QDesignerPropertySheetExtension::hasReset(int index) const + + Returns true if the property at the given \a index has a reset + button in \QD's property editor, otherwise false. + + \sa indexOf(), reset() +*/ + +/*! + \fn bool QDesignerPropertySheetExtension::reset(int index) + + Resets the value of the property at the given \a index, to the + default value. Returns true if a default value could be found, otherwise false. + + \sa indexOf(), hasReset(), isChanged() +*/ + +/*! + \fn bool QDesignerPropertySheetExtension::isVisible(int index) const + + Returns true if the property at the given \a index is visible in + \QD's property editor, otherwise false. + + \sa indexOf(), setVisible() +*/ + +/*! + \fn void QDesignerPropertySheetExtension::setVisible(int index, bool visible) + + If \a visible is true, the property at the given \a index is + visible in \QD's property editor; otherwise the property is + hidden. + + \sa indexOf(), isVisible() +*/ + +/*! + \fn bool QDesignerPropertySheetExtension::isAttribute(int index) const + + Returns true if the property at the given \a index is an attribute, + which will be \e excluded from the UI file, otherwise false. + + \sa indexOf(), setAttribute() +*/ + +/*! + \fn void QDesignerPropertySheetExtension::setAttribute(int index, bool attribute) + + If \a attribute is true, the property at the given \a index is + made an attribute which will be \e excluded from the UI file; + otherwise it will be included. + + \sa indexOf(), isAttribute() +*/ + +/*! + \fn QVariant QDesignerPropertySheetExtension::property(int index) const + + Returns the value of the property at the given \a index. + + \sa indexOf(), setProperty(), propertyGroup() +*/ + +/*! + \fn void QDesignerPropertySheetExtension::setProperty(int index, const QVariant &value) + + Sets the \a value of the property at the given \a index. + + \warning If you change the value of a property using this + function, the undo stack is not updated. To ensure that a + property's value can be reverted using the undo stack, you must + use the QDesignerFormWindowCursorInterface::setProperty() + function, or its buddy \l + {QDesignerFormWindowCursorInterface::setWidgetProperty()}{setWidgetProperty()}, + instead. + + \sa indexOf(), property(), propertyGroup() +*/ + +/*! + \fn bool QDesignerPropertySheetExtension::isChanged(int index) const + + Returns true if the value of the property at the given \a index + differs from the property's default value, otherwise false. + + \sa indexOf(), setChanged(), reset() +*/ + +/*! + \fn void QDesignerPropertySheetExtension::setChanged(int index, bool changed) + + Sets whether the property at the given \a index is different from + its default value, or not, depending on the \a changed parameter. + + \sa indexOf(), isChanged() +*/ + +// Doc: Interface only + +/*! + \class QDesignerDynamicPropertySheetExtension + + \brief The QDesignerDynamicPropertySheetExtension class allows you to + manipulate a widget's dynamic properties in Qt Designer's property editor. + + \sa QDesignerPropertySheetExtension, {QObject#Dynamic Properties}{Dynamic Properties} + + \inmodule QtDesigner + \since 4.3 +*/ + +/*! + \fn QDesignerDynamicPropertySheetExtension::~QDesignerDynamicPropertySheetExtension() + + Destroys the dynamic property sheet extension. +*/ + +/*! + \fn bool QDesignerDynamicPropertySheetExtension::dynamicPropertiesAllowed() const + + Returns true if the widget supports dynamic properties; otherwise returns false. +*/ + +/*! + \fn int QDesignerDynamicPropertySheetExtension::addDynamicProperty(const QString &propertyName, const QVariant &value) + + Adds a dynamic property named \a propertyName and sets its value to \a value. + Returns the index of the property if it was added successfully; otherwise returns -1 to + indicate failure. +*/ + +/*! + \fn bool QDesignerDynamicPropertySheetExtension::removeDynamicProperty(int index) + + Removes the dynamic property at the given \a index. + Returns true if the operation succeeds; otherwise returns false. +*/ + +/*! + \fn bool QDesignerDynamicPropertySheetExtension::isDynamicProperty(int index) const + + Returns true if the property at the given \a index is a dynamic property; otherwise + returns false. +*/ + +/*! + \fn bool QDesignerDynamicPropertySheetExtension::canAddDynamicProperty(const QString &propertyName) const + + Returns true if \a propertyName is a valid, unique name for a dynamic + property; otherwise returns false. + +*/ diff --git a/doc/src/qtendian.qdoc b/doc/src/classes/qtendian.qdoc index dcffb5d..dcffb5d 100644 --- a/doc/src/qtendian.qdoc +++ b/doc/src/classes/qtendian.qdoc diff --git a/doc/src/qtestevent.qdoc b/doc/src/classes/qtestevent.qdoc index 2e111b3..2e111b3 100644 --- a/doc/src/qtestevent.qdoc +++ b/doc/src/classes/qtestevent.qdoc diff --git a/doc/src/qvarlengtharray.qdoc b/doc/src/classes/qvarlengtharray.qdoc index ac6bb6e..ac6bb6e 100644 --- a/doc/src/qvarlengtharray.qdoc +++ b/doc/src/classes/qvarlengtharray.qdoc diff --git a/doc/src/qwaitcondition.qdoc b/doc/src/classes/qwaitcondition.qdoc index ae94e35..ae94e35 100644 --- a/doc/src/qwaitcondition.qdoc +++ b/doc/src/classes/qwaitcondition.qdoc diff --git a/doc/src/designer-manual.qdoc b/doc/src/designer-manual.qdoc index c67865d..ab33185 100644 --- a/doc/src/designer-manual.qdoc +++ b/doc/src/designer-manual.qdoc @@ -402,7 +402,7 @@ while setting as few widget size constraints as possible. For a more technical perspective on Qt's layout classes, refer to the - \l{Layout Classes} document. + \l{Layout Management} documentation. */ diff --git a/doc/src/examples-overview.qdoc b/doc/src/examples-overview.qdoc index 4313c43..3db4a45 100644 --- a/doc/src/examples-overview.qdoc +++ b/doc/src/examples-overview.qdoc @@ -238,6 +238,17 @@ These examples demonstrate the basic techniques used to take advantage of OpenGL in Qt applications. + \section1 \l{Qt Examples#Multimedia}{Multimedia} + + \l{Qt Examples#Multimedia} + + Qt provides low-level audio support on linux,windows and mac platforms by default and + an audio plugin API to allow developers to implement there own audio support for + custom devices and platforms. + + These examples demonstrate the basic techniques used to take advantage of + Audio API in Qt applications. + \section1 \l{Qt Examples#SQL}{SQL} \l{Qt Examples#SQL}{\inlineimage sql-examples.png diff --git a/doc/src/examples.qdoc b/doc/src/examples.qdoc index e85acd1..74a9bd8 100644 --- a/doc/src/examples.qdoc +++ b/doc/src/examples.qdoc @@ -255,6 +255,14 @@ \o \l{phonon/musicplayer}{Music Player}\raisedaster \endlist + \section1 Multimedia + + \list + \o \l{multimedia/audio/audiodevices}{Audio Devices}\raisedaster + \o \l{multimedia/audio/audiooutput}{Audio Output}\raisedaster + \o \l{multimedia/audio/audioinput}{Audio Input}\raisedaster + \endlist + \section1 Qt Designer \list diff --git a/doc/src/obsoleteclasses.qdoc b/doc/src/examples/audiodevices.qdoc index 3658dfc..3b8cdd8 100644 --- a/doc/src/obsoleteclasses.qdoc +++ b/doc/src/examples/audiodevices.qdoc @@ -40,20 +40,15 @@ ****************************************************************************/ /*! - \page obsoleteclasses.html - \group obsolete - \title Obsolete Classes - \ingroup classlists - \ingroup groups + \example multimedia/audio/audiodevices + \title Audio Devices Example - \brief Qt classes that are obsolete (deprecated). + The Audio Devices example demonstrates the basic use of QAudioDeviceInfo class + provided with Qt. - This is a list of Qt classes that are obsolete (deprecated). These - classes are provided to keep old source code working but they are - no longer maintained. We strongly advise against using these - classes in new code. + Qt provides the QAudioDeviceInfo class to enable audio querying within + a standard application user interface. - \generatelist obsoleteclasses - - \sa {Qt's Classes}, {Qt's Modules} + This example allows you to browse audio devices available and try out different + configurations to see if they are supported. */ diff --git a/doc/src/examples/audioinput.qdoc b/doc/src/examples/audioinput.qdoc new file mode 100644 index 0000000..5dfe76e --- /dev/null +++ b/doc/src/examples/audioinput.qdoc @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example multimedia/audio/audioinput + \title AudioInput Example + + The Audio Input example demonstrates the basic use of QAudioInput class + provided with Qt. + + Qt provides the QAudioInput class to enable audio functionality within + a standard application user interface. + + This example uses a fast-fourier transform on the input audio from the microphone + and displays the output. +*/ diff --git a/doc/src/examples/audiooutput.qdoc b/doc/src/examples/audiooutput.qdoc new file mode 100644 index 0000000..9c8a75f --- /dev/null +++ b/doc/src/examples/audiooutput.qdoc @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example multimedia/audio/audiooutput + \title Audio Output Example + + The Audio Output example demonstrates the basic use of the QAudioOutput class + provided with Qt. + + This example provides a tone generator to supply continuous audio playback. + The first button allows pause and resume of the playback. + The second button allows toggling between push and pull modes of operation. +*/ diff --git a/doc/src/examples/collidingmice-example.qdoc b/doc/src/examples/collidingmice-example.qdoc index 5b124f9..f627fbf 100644 --- a/doc/src/examples/collidingmice-example.qdoc +++ b/doc/src/examples/collidingmice-example.qdoc @@ -95,11 +95,11 @@ the global qrand() function which is a thread-safe version of the standard C++ rand() function. - Then we call the \l {QGraphicsItem::rotate()}{rotate()} function + Then we call the \l {QGraphicsItem::setRotation()}{setRotation()} function inherited from QGraphicsItem. Items live in their own local coordinate system. Their coordinates are usually centered around (0, 0), and this is also the center for all transformations. By - calling the item's \l {QGraphicsItem::rotate()}{rotate()} function + calling the item's \l {QGraphicsItem::setRotation()}{setRotation()} function we alter the direction in which the mouse will start moving. When the QGraphicsScene decides to advance the scene a frame it will call diff --git a/doc/src/examples/googlechat.qdoc b/doc/src/examples/googlechat.qdoc index 42a0198..7c9d7db 100644 --- a/doc/src/examples/googlechat.qdoc +++ b/doc/src/examples/googlechat.qdoc @@ -48,4 +48,5 @@ \image googlechat-example.png + \note The Google Talk instant messaging service is a registered trademark of Google Inc. */ diff --git a/doc/src/examples/googlesuggest.qdoc b/doc/src/examples/googlesuggest.qdoc index 1752420..e3d2b1f 100644 --- a/doc/src/examples/googlesuggest.qdoc +++ b/doc/src/examples/googlesuggest.qdoc @@ -48,4 +48,5 @@ \image googlesuggest-example.png + \note The Google Suggest suggestion service is a registered trademark of Google Inc. */ diff --git a/doc/src/groups.qdoc b/doc/src/groups.qdoc index 2733853..f7296a3 100644 --- a/doc/src/groups.qdoc +++ b/doc/src/groups.qdoc @@ -40,24 +40,6 @@ ****************************************************************************/ /*! - \group groups - \title Grouped Classes - \ingroup classlists - - This page provides a way of navigating Qt's classes by grouping - related classes together. Some classes may appear in more than one group. - - \generatelist{related} - - \omit - \row - \o \l{Component Model} - \o Interfaces and helper classes for the Qt Component Model. - \endomit - -*/ - -/*! \group advanced \title Advanced Widgets \ingroup groups @@ -147,12 +129,6 @@ */ -/* \group componentmodel - \title Component Model - - These classes and interfaces form the basis of the \l{Qt Component Model}. - -*/ /*! \group database @@ -226,22 +202,8 @@ */ /*! - \group explicitly-shared - \ingroup groups - - \title Explicitly Shared Classes - \brief Classes that use explicit sharing to manage internal data. - - \keyword explicit sharing - \keyword explicitly shared - - Unlike many of Qt's data types, which use \l{implicit sharing}, these - classes use explicit sharing to manage internal data. -*/ - -/*! \group geomanagement - \title Layout Management + \title Layout Classes \ingroup groups \brief Classes handling automatic resizing and moving of widgets, for @@ -401,103 +363,6 @@ */ /*! - \group shared - \title Implicitly Shared Classes - \ingroup architecture - \ingroup groups - - \brief Classes that use reference counting for fast copying. - - \keyword implicit data sharing - \keyword implicit sharing - \keyword implicitly shared - \keyword reference counting - \keyword shared implicitly - \keyword shared classes - - Many C++ classes in Qt use implicit data sharing to maximize - resource usage and minimize copying. Implicitly shared classes are - both safe and efficient when passed as arguments, because only a - pointer to the data is passed around, and the data is copied only - if and when a function writes to it, i.e., \e {copy-on-write}. - - \tableofcontents - - \section1 Overview - - A shared class consists of a pointer to a shared data block that - contains a reference count and the data. - - When a shared object is created, it sets the reference count to 1. The - reference count is incremented whenever a new object references the - shared data, and decremented when the object dereferences the shared - data. The shared data is deleted when the reference count becomes - zero. - - \keyword deep copy - \keyword shallow copy - - When dealing with shared objects, there are two ways of copying an - object. We usually speak about \e deep and \e shallow copies. A deep - copy implies duplicating an object. A shallow copy is a reference - copy, i.e. just a pointer to a shared data block. Making a deep copy - can be expensive in terms of memory and CPU. Making a shallow copy is - very fast, because it only involves setting a pointer and incrementing - the reference count. - - Object assignment (with operator=()) for implicitly shared objects is - implemented using shallow copies. - - The benefit of sharing is that a program does not need to duplicate - data unnecessarily, which results in lower memory use and less copying - of data. Objects can easily be assigned, sent as function arguments, - and returned from functions. - - Implicit sharing takes place behind the scenes; the programmer - does not need to worry about it. Even in multithreaded - applications, implicit sharing takes place, as explained in - \l{Threads and Implicit Sharing}. - - \section1 Implicit Sharing in Detail - - Implicit sharing automatically detaches the object from a shared - block if the object is about to change and the reference count is - greater than one. (This is often called \e {copy-on-write} or - \e {value semantics}.) - - An implicitly shared class has total control of its internal data. In - any member functions that modify its data, it automatically detaches - before modifying the data. - - The QPen class, which uses implicit sharing, detaches from the shared - data in all member functions that change the internal data. - - Code fragment: - \snippet doc/src/snippets/code/doc_src_groups.qdoc 0 - - \section1 List of Classes - - The classes listed below automatically detach from common data if - an object is about to be changed. The programmer will not even - notice that the objects are shared. Thus you should treat - separate instances of them as separate objects. They will always - behave as separate objects but with the added benefit of sharing - data whenever possible. For this reason, you can pass instances - of these classes as arguments to functions by value without - concern for the copying overhead. - - Example: - \snippet doc/src/snippets/code/doc_src_groups.qdoc 1 - - In this example, \c p1 and \c p2 share data until QPainter::begin() - is called for \c p2, because painting a pixmap will modify it. - - \warning Do not copy an implicitly shared container (QMap, - QVector, etc.) while you are iterating over it using an non-const - \l{STL-style iterator}. -*/ - -/*! \group ssl \title Secure Sockets Layer (SSL) Classes \ingroup groups diff --git a/doc/src/images/cursor-hsplit.png b/doc/src/images/cursor-hsplit.png Binary files differindex 1beda25..a5667e3 100644 --- a/doc/src/images/cursor-hsplit.png +++ b/doc/src/images/cursor-hsplit.png diff --git a/doc/src/images/cursor-vsplit.png b/doc/src/images/cursor-vsplit.png Binary files differindex a5667e3..1beda25 100644 --- a/doc/src/images/cursor-vsplit.png +++ b/doc/src/images/cursor-vsplit.png diff --git a/doc/src/implicit-sharing.qdoc b/doc/src/implicit-sharing.qdoc new file mode 100644 index 0000000..85630dc --- /dev/null +++ b/doc/src/implicit-sharing.qdoc @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* TODO: Move some of the documentation from QSharedDataPointer into this + document. */ + +/*! + \group shared + \title Implicitly Shared Classes + \ingroup architecture + \ingroup groups + + \brief Classes that use reference counting for fast copying. + + \keyword implicit data sharing + \keyword implicit sharing + \keyword implicitly shared + \keyword reference counting + \keyword shared implicitly + \keyword shared classes + + Many C++ classes in Qt use implicit data sharing to maximize + resource usage and minimize copying. Implicitly shared classes are + both safe and efficient when passed as arguments, because only a + pointer to the data is passed around, and the data is copied only + if and when a function writes to it, i.e., \e {copy-on-write}. + + \tableofcontents + + \section1 Overview + + A shared class consists of a pointer to a shared data block that + contains a reference count and the data. + + When a shared object is created, it sets the reference count to 1. The + reference count is incremented whenever a new object references the + shared data, and decremented when the object dereferences the shared + data. The shared data is deleted when the reference count becomes + zero. + + \keyword deep copy + \keyword shallow copy + + When dealing with shared objects, there are two ways of copying an + object. We usually speak about \e deep and \e shallow copies. A deep + copy implies duplicating an object. A shallow copy is a reference + copy, i.e. just a pointer to a shared data block. Making a deep copy + can be expensive in terms of memory and CPU. Making a shallow copy is + very fast, because it only involves setting a pointer and incrementing + the reference count. + + Object assignment (with operator=()) for implicitly shared objects is + implemented using shallow copies. + + The benefit of sharing is that a program does not need to duplicate + data unnecessarily, which results in lower memory use and less copying + of data. Objects can easily be assigned, sent as function arguments, + and returned from functions. + + Implicit sharing takes place behind the scenes; the programmer + does not need to worry about it. Even in multithreaded + applications, implicit sharing takes place, as explained in + \l{Threads and Implicit Sharing}. + + When implementing your own implicitly shared classes, use the + QSharedData and QSharedDataPointer classes. + + \section1 Implicit Sharing in Detail + + Implicit sharing automatically detaches the object from a shared + block if the object is about to change and the reference count is + greater than one. (This is often called \e {copy-on-write} or + \e {value semantics}.) + + An implicitly shared class has total control of its internal data. In + any member functions that modify its data, it automatically detaches + before modifying the data. + + The QPen class, which uses implicit sharing, detaches from the shared + data in all member functions that change the internal data. + + Code fragment: + \snippet doc/src/snippets/code/doc_src_groups.qdoc 0 + + + \section1 List of Classes + + The classes listed below automatically detach from common data if + an object is about to be changed. The programmer will not even + notice that the objects are shared. Thus you should treat + separate instances of them as separate objects. They will always + behave as separate objects but with the added benefit of sharing + data whenever possible. For this reason, you can pass instances + of these classes as arguments to functions by value without + concern for the copying overhead. + + Example: + \snippet doc/src/snippets/code/doc_src_groups.qdoc 1 + + In this example, \c p1 and \c p2 share data until QPainter::begin() + is called for \c p2, because painting a pixmap will modify it. + + \warning Do not copy an implicitly shared container (QMap, + QVector, etc.) while you are iterating over it using an non-const + \l{STL-style iterator}. +*/ diff --git a/doc/src/layout.qdoc b/doc/src/layout.qdoc index ec88089..9350aa8 100644 --- a/doc/src/layout.qdoc +++ b/doc/src/layout.qdoc @@ -42,9 +42,8 @@ /*! \page layout.html - \title Layout Classes + \title Layout Management \ingroup architecture - \ingroup classlists \brief A tour of the standard layout managers and an introduction to custom layouts. diff --git a/doc/src/3rdparty.qdoc b/doc/src/legal/3rdparty.qdoc index 0d86ab3..0d86ab3 100644 --- a/doc/src/3rdparty.qdoc +++ b/doc/src/legal/3rdparty.qdoc diff --git a/doc/src/commercialeditions.qdoc b/doc/src/legal/commercialeditions.qdoc index b6d80c2..b6d80c2 100644 --- a/doc/src/commercialeditions.qdoc +++ b/doc/src/legal/commercialeditions.qdoc diff --git a/doc/src/editions.qdoc b/doc/src/legal/editions.qdoc index 9ed4c9c..9ed4c9c 100644 --- a/doc/src/editions.qdoc +++ b/doc/src/legal/editions.qdoc diff --git a/doc/src/gpl.qdoc b/doc/src/legal/gpl.qdoc index 97959e8..97959e8 100644 --- a/doc/src/gpl.qdoc +++ b/doc/src/legal/gpl.qdoc diff --git a/doc/src/licenses.qdoc b/doc/src/legal/licenses.qdoc index 7597e7f..4689114 100644 --- a/doc/src/licenses.qdoc +++ b/doc/src/legal/licenses.qdoc @@ -39,6 +39,19 @@ ** ****************************************************************************/ +/*! + \group licensing + \title Licensing Information + \ingroup topics + \brief Information about licenses and licensing issues. + + These documents include information about Qt's licenses and the licenses + of third party components used in Qt. + + \generatelist{related} +*/ + + /*! \page licenses.html \title Other Licenses Used in Qt diff --git a/doc/src/opensourceedition.qdoc b/doc/src/legal/opensourceedition.qdoc index e7bb26a..e7bb26a 100644 --- a/doc/src/opensourceedition.qdoc +++ b/doc/src/legal/opensourceedition.qdoc diff --git a/doc/src/trademarks.qdoc b/doc/src/legal/trademarks.qdoc index 0e659d2..0e659d2 100644 --- a/doc/src/trademarks.qdoc +++ b/doc/src/legal/trademarks.qdoc diff --git a/doc/src/platform-notes.qdoc b/doc/src/platform-notes.qdoc index 7c97f65..4738928 100644 --- a/doc/src/platform-notes.qdoc +++ b/doc/src/platform-notes.qdoc @@ -378,9 +378,6 @@ improve support for this feature. */ - \row \o \l{QNX} \o Intel 32-bit, PowerPC \o unsupported/qnx-<arch>-g++ \o QNX 6.4 GCC - \row \o VxWorks \o Intel 32-bit, PowerPC \o unsupported/vxworks-<arch>-g++ \o VxWorks 6.7 GCC - /*! \page platform-notes-windows-ce.html \title Platform Notes - Windows CE diff --git a/doc/src/properties.qdoc b/doc/src/properties.qdoc index 5490dd0..d0a9ccc 100644 --- a/doc/src/properties.qdoc +++ b/doc/src/properties.qdoc @@ -122,6 +122,17 @@ editable property for (checkable) buttons. Note that QItemDelegate gets and sets a widget's \c USER property. + \o The presence of the \c CONSTANT attibute indicates that the property + value is constant. For a given object instance, the READ method of a + constant property must return the same value every time it is called. This + constant value may be different for different instances of the object. A + constant property cannot have a WRITE method or a NOTIFY signal. + + \o The presence of the \c FINAL attribute indicates that the property + will not be overridden by a derived class. This can be used for performance + optimizations in some cases, but is not enforced by moc. Care must be taken + never to override a \c FINAL property. + \endlist The \c READ, \c WRITE, and \c RESET functions can be inherited. diff --git a/doc/src/qsqldatatype-table.qdoc b/doc/src/qsqldatatype-table.qdoc index 2055e6a..bc88c30 100644 --- a/doc/src/qsqldatatype-table.qdoc +++ b/doc/src/qsqldatatype-table.qdoc @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the documentation of the Qt Toolkit. ** diff --git a/doc/src/qtdesigner.qdoc b/doc/src/qtdesigner.qdoc index d913b32..0959bd9 100644 --- a/doc/src/qtdesigner.qdoc +++ b/doc/src/qtdesigner.qdoc @@ -166,1376 +166,3 @@ \sa {Qt Designer Manual}, {QtUiTools Module} */ - -/*! - \class QDesignerMemberSheetExtension - - \brief The QDesignerMemberSheetExtension class allows you to - manipulate a widget's member functions which is displayed when - configuring connections using Qt Designer's mode for editing - signals and slots. - - \inmodule QtDesigner - - QDesignerMemberSheetExtension is a collection of functions that is - typically used to query a widget's member functions, and to - manipulate the member functions' appearance in \QD's signals and - slots editing mode. For example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 2 - - When implementing a custom widget plugin, a pointer to \QD's - current QDesignerFormEditorInterface object (\c formEditor in the - example above) is provided by the - QDesignerCustomWidgetInterface::initialize() function's parameter. - - The member sheet (and any other extension), can be retrieved by - querying \QD's extension manager using the qt_extension() - function. When you want to release the extension, you only need to - delete the pointer. - - All widgets have a default member sheet used in \QD's signals and - slots editing mode with the widget's member functions. But - QDesignerMemberSheetExtension also provides an interface for - creating custom member sheet extensions. - - \warning \QD uses the QDesignerMemberSheetExtension to facilitate - the signal and slot editing mode. Whenever a connection between - two widgets is requested, \QD will query for the widgets' member - sheet extensions. If a widget has an implemented member sheet - extension, this extension will override the default member sheet. - - To create a member sheet extension, your extension class must - inherit from both QObject and QDesignerMemberSheetExtension. Then, - since we are implementing an interface, we must ensure that it's - made known to the meta object system using the Q_INTERFACES() - macro: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 3 - - This enables \QD to use qobject_cast() to query for - supported interfaces using nothing but a QObject pointer. - - In \QD the extensions are not created until they are - required. For that reason, when implementing a member sheet - extension, you must also create a QExtensionFactory, i.e a class - that is able to make an instance of your extension, and register - it using \QD's \l {QExtensionManager}{extension manager}. - - When a widget's member sheet extension is required, \QD's \l - {QExtensionManager}{extension manager} will run through all its - registered factories calling QExtensionFactory::createExtension() - for each until the first one that is able to create a member sheet - extension for that widget, is found. This factory will then make - an instance of the extension. If no such factory is found, \QD - will use the default member sheet. - - There are four available types of extensions in \QD: - QDesignerContainerExtension, QDesignerMemberSheetExtension, - QDesignerPropertySheetExtension and - QDesignerTaskMenuExtension. \QD's behavior is the same whether the - requested extension is associated with a multi page container, a - member sheet, a property sheet or a task menu. - - The QExtensionFactory class provides a standard extension - factory, and can also be used as an interface for custom - extension factories. You can either create a new - QExtensionFactory and reimplement the - QExtensionFactory::createExtension() function. For example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 4 - - Or you can use an existing factory, expanding the - QExtensionFactory::createExtension() function to make the factory - able to create a member sheet extension as well. For example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 5 - - For a complete example using an extension class, see \l - {designer/taskmenuextension}{Task Menu Extension example}. The - example shows how to create a custom widget plugin for Qt - Designer, and how to to use the QDesignerTaskMenuExtension class - to add custom items to \QD's task menu. - - \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget - Extensions} -*/ - -/*! - \fn QDesignerMemberSheetExtension::~QDesignerMemberSheetExtension() - - Destroys the member sheet extension. -*/ - -/*! - \fn int QDesignerMemberSheetExtension::count() const - - Returns the extension's number of member functions. -*/ - -/*! - \fn int QDesignerMemberSheetExtension::indexOf(const QString &name) const - - Returns the index of the member function specified by the given \a - name. - - \sa memberName() -*/ - -/*! - \fn QString QDesignerMemberSheetExtension::memberName(int index) const - - Returns the name of the member function with the given \a index. - - \sa indexOf() -*/ - -/*! - \fn QString QDesignerMemberSheetExtension::memberGroup(int index) const - - Returns the name of the member group specified for the function - with the given \a index. - - \sa indexOf(), setMemberGroup() -*/ - -/*! - \fn void QDesignerMemberSheetExtension::setMemberGroup(int index, const QString &group) - - Sets the member group of the member function with the given \a - index, to \a group. - - \sa indexOf(), memberGroup() -*/ - -/*! - \fn bool QDesignerMemberSheetExtension::isVisible(int index) const - - Returns true if the member function with the given \a index is - visible in \QD's signal and slot editor, otherwise false. - - \sa indexOf(), setVisible() -*/ - -/*! - \fn void QDesignerMemberSheetExtension::setVisible(int index, bool visible) - - If \a visible is true, the member function with the given \a index - is visible in \QD's signals and slots editing mode; otherwise the - member function is hidden. - - \sa indexOf(), isVisible() -*/ - -/*! - \fn virtual bool QDesignerMemberSheetExtension::isSignal(int index) const - - Returns true if the member function with the given \a index is a - signal, otherwise false. - - \sa indexOf() -*/ - -/*! - \fn bool QDesignerMemberSheetExtension::isSlot(int index) const - - Returns true if the member function with the given \a index is a - slot, otherwise false. - - \sa indexOf() -*/ - -/*! - \fn bool QDesignerMemberSheetExtension::inheritedFromWidget(int index) const - - Returns true if the member function with the given \a index is - inherited from QWidget, otherwise false. - - \sa indexOf() -*/ - -/*! - \fn QString QDesignerMemberSheetExtension::declaredInClass(int index) const - - Returns the name of the class in which the member function with - the given \a index is declared. - - \sa indexOf() -*/ - -/*! - \fn QString QDesignerMemberSheetExtension::signature(int index) const - - Returns the signature of the member function with the given \a - index. - - \sa indexOf() -*/ - -/*! - \fn QList<QByteArray> QDesignerMemberSheetExtension::parameterTypes(int index) const - - Returns the parameter types of the member function with the given - \a index, as a QByteArray list. - - \sa indexOf(), parameterNames() -*/ - -/*! - \fn QList<QByteArray> QDesignerMemberSheetExtension::parameterNames(int index) const - - Returns the parameter names of the member function with the given - \a index, as a QByteArray list. - - \sa indexOf(), parameterTypes() -*/ - - -// Doc: Interface only - -/*! - \class QDesignerLayoutDecorationExtension - \brief The QDesignerLayoutDecorationExtension class provides an extension to a layout in \QD. - \inmodule QtDesigner - \internal -*/ - -/*! - \enum QDesignerLayoutDecorationExtension::InsertMode - - This enum describes the modes that are used to insert items into a layout. - - \value InsertWidgetMode Widgets are inserted into empty cells in a layout. - \value InsertRowMode Whole rows are inserted into a vertical or grid layout. - \value InsertColumnMode Whole columns are inserted into a horizontal or grid layout. -*/ - -/*! - \fn virtual QDesignerLayoutDecorationExtension::~QDesignerLayoutDecorationExtension() - - Destroys the extension. -*/ - -/*! - \fn virtual QList<QWidget*> QDesignerLayoutDecorationExtension::widgets(QLayout *layout) const - - Returns the widgets that are managed by the given \a layout. - - \sa insertWidget(), removeWidget() -*/ - -/*! - \fn QRect QDesignerLayoutDecorationExtension::itemInfo(int index) const - - Returns the rectangle covered by the item at the given \a index in the layout. -*/ - -/*! - \fn int QDesignerLayoutDecorationExtension::indexOf(QWidget *widget) const - - Returns the index of the specified \a widget in the layout. -*/ - -/*! - \fn int QDesignerLayoutDecorationExtension::indexOf(QLayoutItem *item) const - - Returns the index of the specified layout \a item. -*/ - -/*! - \fn QDesignerLayoutDecorationExtension::InsertMode QDesignerLayoutDecorationExtension::currentInsertMode() const - - Returns the current insertion mode. -*/ - -/*! - \fn int QDesignerLayoutDecorationExtension::currentIndex() const - - Returns the current index in the layout. -*/ - -/*! - \fn QPair<int, int> QDesignerLayoutDecorationExtension::currentCell() const - - Returns a pair containing the row and column of the current cell in the layout. -*/ - -/*! - \fn void QDesignerLayoutDecorationExtension::insertWidget(QWidget *widget, const QPair<int, int> &cell) - - Inserts the given \a widget into the specified \a cell in the layout. - - \sa removeWidget() -*/ - -/*! - \fn void QDesignerLayoutDecorationExtension::removeWidget(QWidget *widget) - - Removes the specified \a widget from the layout. - - \sa insertWidget() -*/ - -/*! - \fn void QDesignerLayoutDecorationExtension::insertRow(int row) - - Inserts a new row into the form at the position specified by \a row. -*/ - -/*! - \fn void QDesignerLayoutDecorationExtension::insertColumn(int column) - - Inserts a new column into the form at the position specified by \a column. -*/ - -/*! - \fn void QDesignerLayoutDecorationExtension::simplify() - - Simplifies the layout by removing unnecessary empty rows and columns, and by changing the - number of rows or columns spanned by widgets. -*/ - -/*! - \fn int QDesignerLayoutDecorationExtension::findItemAt(const QPoint &position) const - - Returns the index of the item in the layout that covers the given \a position. -*/ - -/*! - \fn int QDesignerLayoutDecorationExtension::findItemAt(int row, int column) const - - Returns the item in the layout that occupies the specified \a row and \a column in the layout. - - Currently, this only applies to grid layouts. -*/ - -/*! - \fn void QDesignerLayoutDecorationExtension::adjustIndicator(const QPoint &position, int index) - - Adjusts the indicator for the item specified by \a index so that - it lies at the given \a position on the form. -*/ - - -// Doc: Interface only - -/*! - \class QDesignerContainerExtension - \brief The QDesignerContainerExtension class allows you to add pages to - a custom multi-page container in Qt Designer's workspace. - \inmodule QtDesigner - - QDesignerContainerExtension provide an interface for creating - custom container extensions. A container extension consists of a - collection of functions that \QD needs to manage a multi-page - container plugin, and a list of the container's pages. - - \image containerextension-example.png - - \warning This is \e not an extension for container plugins in - general, only custom \e multi-page containers. - - To create a container extension, your extension class must inherit - from both QObject and QDesignerContainerExtension. For example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 6 - - Since we are implementing an interface, we must ensure that it's - made known to the meta object system using the Q_INTERFACES() - macro. This enables \QD to use the qobject_cast() function to - query for supported interfaces using nothing but a QObject - pointer. - - You must reimplement several functions to enable \QD to manage a - custom multi-page container widget: \QD uses count() to keep track - of the number pages in your container, widget() to return the page - at a given index in the list of the container's pages, and - currentIndex() to return the list index of the selected page. \QD - uses the addWidget() function to add a given page to the - container, expecting it to be appended to the list of pages, while - it expects the insertWidget() function to add a given page to the - container by inserting it at a given index. - - In \QD the extensions are not created until they are - required. For that reason you must also create a - QExtensionFactory, i.e a class that is able to make an instance of - your extension, and register it using \QD's \l - {QExtensionManager}{extension manager}. - - When a container extension is required, \QD's \l - {QExtensionManager}{extension manager} will run through all its - registered factories calling QExtensionFactory::createExtension() - for each until the first one that is able to create a container - extension, is found. This factory will then create the extension - for the plugin. - - There are four available types of extensions in \QD: - QDesignerContainerExtension , QDesignerMemberSheetExtension, - QDesignerPropertySheetExtension and QDesignerTaskMenuExtension. - \QD's behavior is the same whether the requested extension is - associated with a multi page container, a member sheet, a property - sheet or a task menu. - - The QExtensionFactory class provides a standard extension factory, - and can also be used as an interface for custom extension - factories. You can either create a new QExtensionFactory and - reimplement the QExtensionFactory::createExtension() function. For - example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 7 - - Or you can use an existing factory, expanding the - QExtensionFactory::createExtension() function to make the factory - able to create a container extension as well. For example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 8 - - For a complete example using the QDesignerContainerExtension - class, see the \l {designer/containerextension}{Container - Extension example}. The example shows how to create a custom - multi-page plugin for \QD. - - \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget - Extensions} -*/ - -/*! - \fn QDesignerContainerExtension::~QDesignerContainerExtension() - - Destroys the extension. -*/ - -/*! - \fn int QDesignerContainerExtension::count() const - - Returns the number of pages in the container. -*/ - -/*! - \fn QWidget *QDesignerContainerExtension::widget(int index) const - - Returns the page at the given \a index in the extension's list of - pages. - - \sa addWidget(), insertWidget() -*/ - -/*! - \fn int QDesignerContainerExtension::currentIndex() const - - Returns the index of the currently selected page in the - container. - - \sa setCurrentIndex() -*/ - -/*! - \fn void QDesignerContainerExtension::setCurrentIndex(int index) - - Sets the currently selected page in the container to be the - page at the given \a index in the extension's list of pages. - - \sa currentIndex() -*/ - -/*! - \fn void QDesignerContainerExtension::addWidget(QWidget *page) - - Adds the given \a page to the container by appending it to the - extension's list of pages. - - \sa insertWidget(), remove(), widget() -*/ - -/*! - \fn void QDesignerContainerExtension::insertWidget(int index, QWidget *page) - - Adds the given \a page to the container by inserting it at the - given \a index in the extension's list of pages. - - \sa addWidget(), remove(), widget() -*/ - -/*! - \fn void QDesignerContainerExtension::remove(int index) - - Removes the page at the given \a index from the extension's list - of pages. - - \sa addWidget(), insertWidget() -*/ - - -// Doc: Interface only - -/*! - \class QDesignerTaskMenuExtension - \brief The QDesignerTaskMenuExtension class allows you to add custom - menu entries to Qt Designer's task menu. - \inmodule QtDesigner - - QDesignerTaskMenuExtension provides an interface for creating - custom task menu extensions. It is typically used to create task - menu entries that are specific to a plugin in \QD. - - \QD uses the QDesignerTaskMenuExtension to feed its task - menu. Whenever a task menu is requested, \QD will query - for the selected widget's task menu extension. - - \image taskmenuextension-example-faded.png - - A task menu extension is a collection of QActions. The actions - appear as entries in the task menu when the plugin with the - specified extension is selected. The image above shows the custom - \gui {Edit State...} action which appears in addition to \QD's - default task menu entries: \gui Cut, \gui Copy, \gui Paste etc. - - To create a custom task menu extension, your extension class must - inherit from both QObject and QDesignerTaskMenuExtension. For - example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 9 - - Since we are implementing an interface, we must ensure that it - is made known to the meta-object system using the Q_INTERFACES() - macro. This enables \QD to use the qobject_cast() function to - query for supported interfaces using nothing but a QObject - pointer. - - You must reimplement the taskActions() function to return a list - of actions that will be included in \QD task menu. Optionally, you - can reimplement the preferredEditAction() function to set the - action that is invoked when selecting your plugin and pressing - \key F2. The preferred edit action must be one of the actions - returned by taskActions() and, if it's not defined, pressing the - \key F2 key will simply be ignored. - - In \QD, extensions are not created until they are required. A - task menu extension, for example, is created when you click the - right mouse button over a widget in \QD's workspace. For that - reason you must also construct an extension factory, using either - QExtensionFactory or a subclass, and register it using \QD's - \l {QExtensionManager}{extension manager}. - - When a task menu extension is required, \QD's \l - {QExtensionManager}{extension manager} will run through all its - registered factories calling QExtensionFactory::createExtension() - for each until it finds one that is able to create a task menu - extension for the selected widget. This factory will then make an - instance of the extension. - - There are four available types of extensions in \QD: - QDesignerContainerExtension, QDesignerMemberSheetExtension, - QDesignerPropertySheetExtension, and QDesignerTaskMenuExtension. - \QD's behavior is the same whether the requested extension is - associated with a container, a member sheet, a property sheet or a - task menu. - - The QExtensionFactory class provides a standard extension factory, - and can also be used as an interface for custom extension - factories. You can either create a new QExtensionFactory and - reimplement the QExtensionFactory::createExtension() function. For - example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 10 - - Or you can use an existing factory, expanding the - QExtensionFactory::createExtension() function to make the factory - able to create a task menu extension as well. For example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 11 - - For a complete example using the QDesignerTaskMenuExtension class, - see the \l {designer/taskmenuextension}{Task Menu Extension - example}. The example shows how to create a custom widget plugin - for \QD, and how to to use the QDesignerTaskMenuExtension - class to add custom items to \QD's task menu. - - \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget - Extensions} -*/ - -/*! - \fn QDesignerTaskMenuExtension::~QDesignerTaskMenuExtension() - - Destroys the task menu extension. -*/ - -/*! - \fn QAction *QDesignerTaskMenuExtension::preferredEditAction() const - - Returns the action that is invoked when selecting a plugin with - the specified extension and pressing \key F2. - - The action must be one of the actions returned by taskActions(). -*/ - -/*! - \fn QList<QAction*> QDesignerTaskMenuExtension::taskActions() const - - Returns the task menu extension as a list of actions which will be - included in \QD's task menu when a plugin with the specified - extension is selected. - - The function must be reimplemented to add actions to the list. -*/ - - -// Doc: Interface only - -/*! - \class QDesignerCustomWidgetCollectionInterface - - \brief The QDesignerCustomWidgetCollectionInterface class allows - you to include several custom widgets in one single library. - - \inmodule QtDesigner - - When implementing a custom widget plugin, you build it as a - separate library. If you want to include several custom widget - plugins in the same library, you must in addition subclass - QDesignerCustomWidgetCollectionInterface. - - QDesignerCustomWidgetCollectionInterface contains one single - function returning a list of the collection's - QDesignerCustomWidgetInterface objects. For example, if you have - several custom widgets \c CustomWidgetOne, \c CustomWidgetTwo and - \c CustomWidgetThree, the class definition may look like this: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 12 - - In the class constructor you add the interfaces to your custom - widgets to the list which you return in the customWidgets() - function: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 13 - - Note that instead of exporting each custom widget plugin using the - Q_EXPORT_PLUGIN2() macro, you export the entire collection. The - Q_EXPORT_PLUGIN2() macro ensures that \QD can access and construct - the custom widgets. Without this macro, there is no way for \QD to - use them. - - \sa QDesignerCustomWidgetInterface, {Creating Custom Widgets for - Qt Designer} -*/ - -/*! - \fn QDesignerCustomWidgetCollectionInterface::~QDesignerCustomWidgetCollectionInterface() { - - Destroys the custom widget collection interface. -*/ - -/*! - \fn QList<QDesignerCustomWidgetInterface*> QDesignerCustomWidgetCollectionInterface::customWidgets() const - - Returns a list of interfaces to the collection's custom widgets. -*/ - - -// Doc: Interface only - -/*! - \class QDesignerCustomWidgetInterface - - \brief The QDesignerCustomWidgetInterface class enables Qt Designer - to access and construct custom widgets. - - \inmodule QtDesigner - - QDesignerCustomWidgetInterface provides a custom widget with an - interface. The class contains a set of functions that must be subclassed - to return basic information about the widget, such as its class name and - the name of its header file. Other functions must be implemented to - initialize the plugin when it is loaded, and to construct instances of - the custom widget for \QD to use. - - When implementing a custom widget you must subclass - QDesignerCustomWidgetInterface to expose your widget to \QD. For - example, this is the declaration for the plugin used in the - \l{Custom Widget Plugin Example}{Custom Widget Plugin example} that - enables an analog clock custom widget to be used by \QD: - - \snippet examples/designer/customwidgetplugin/customwidgetplugin.h 0 - - Note that the only part of the class definition that is specific - to this particular custom widget is the class name. In addition, - since we are implementing an interface, we must ensure that it's - made known to the meta object system using the Q_INTERFACES() - macro. This enables \QD to use the qobject_cast() function to - query for supported interfaces using nothing but a QObject - pointer. - - After \QD loads a custom widget plugin, it calls the interface's - initialize() function to enable it to set up any resources that it - may need. This function is called with a QDesignerFormEditorInterface - parameter that provides the plugin with a gateway to all of \QD's API. - - \QD constructs instances of the custom widget by calling the plugin's - createWidget() function with a suitable parent widget. Plugins must - construct and return an instance of a custom widget with the specified - parent widget. - - In the implementation of the class you must remember to export - your custom widget plugin to \QD using the Q_EXPORT_PLUGIN2() - macro. For example, if a library called \c libcustomwidgetplugin.so - (on Unix) or \c libcustomwidget.dll (on Windows) contains a widget - class called \c MyCustomWidget, we can export it by adding the - following line to the file containing the plugin implementation: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 14 - - This macro ensures that \QD can access and construct the custom widget. - Without this macro, there is no way for \QD to use it. - - When implementing a custom widget plugin, you build it as a - separate library. If you want to include several custom widget - plugins in the same library, you must in addition subclass - QDesignerCustomWidgetCollectionInterface. - - \warning If your custom widget plugin contains QVariant - properties, be aware that only the following \l - {QVariant::Type}{types} are supported: - - \list - \o QVariant::ByteArray - \o QVariant::Bool - \o QVariant::Color - \o QVariant::Cursor - \o QVariant::Date - \o QVariant::DateTime - \o QVariant::Double - \o QVariant::Int - \o QVariant::Point - \o QVariant::Rect - \o QVariant::Size - \o QVariant::SizePolicy - \o QVariant::String - \o QVariant::Time - \o QVariant::UInt - \endlist - - For a complete example using the QDesignerCustomWidgetInterface - class, see the \l {designer/customwidgetplugin}{Custom Widget - Example}. The example shows how to create a custom widget plugin - for \QD. - - \sa QDesignerCustomWidgetCollectionInterface {Creating Custom - Widgets for Qt Designer} -*/ - -/*! - \fn QDesignerCustomWidgetInterface::~QDesignerCustomWidgetInterface() - - Destroys the custom widget interface. -*/ - -/*! - \fn QString QDesignerCustomWidgetInterface::name() const - - Returns the class name of the custom widget supplied by the interface. - - The name returned \e must be identical to the class name used for the - custom widget. -*/ - -/*! - \fn QString QDesignerCustomWidgetInterface::group() const - - Returns the name of the group to which the custom widget belongs. -*/ - -/*! - \fn QString QDesignerCustomWidgetInterface::toolTip() const - - Returns a short description of the widget that can be used by \QD - in a tool tip. -*/ - -/*! - \fn QString QDesignerCustomWidgetInterface::whatsThis() const - - Returns a description of the widget that can be used by \QD in - "What's This?" help for the widget. -*/ - -/*! - \fn QString QDesignerCustomWidgetInterface::includeFile() const - - Returns the path to the include file that \l uic uses when - creating code for the custom widget. -*/ - -/*! - \fn QIcon QDesignerCustomWidgetInterface::icon() const - - Returns the icon used to represent the custom widget in \QD's - widget box. -*/ - -/*! - \fn bool QDesignerCustomWidgetInterface::isContainer() const - - Returns true if the custom widget is intended to be used as a - container; otherwise returns false. - - Most custom widgets are not used to hold other widgets, so their - implementations of this function will return false, but custom - containers will return true to ensure that they behave correctly - in \QD. -*/ - -/*! - \fn QWidget *QDesignerCustomWidgetInterface::createWidget(QWidget *parent) - - Returns a new instance of the custom widget, with the given \a - parent. -*/ - -/*! - \fn bool QDesignerCustomWidgetInterface::isInitialized() const - - Returns true if the widget has been initialized; otherwise returns - false. - - \sa initialize() -*/ - -/*! - \fn void QDesignerCustomWidgetInterface::initialize(QDesignerFormEditorInterface *formEditor) - - Initializes the widget for use with the specified \a formEditor - interface. - - \sa isInitialized() -*/ - -/*! - \fn QString QDesignerCustomWidgetInterface::domXml() const - - Returns the XML that is used to describe the custom widget's - properties to \QD. -*/ - -/*! - \fn QString QDesignerCustomWidgetInterface::codeTemplate() const - - This function is reserved for future use by \QD. - - \omit - Returns the code template that \QD includes in forms that contain - the custom widget when they are saved. - \endomit -*/ - -/*! - \macro QDESIGNER_WIDGET_EXPORT - \relates QDesignerCustomWidgetInterface - \since 4.1 - - This macro is used when defining custom widgets to ensure that they are - correctly exported from plugins for use with \QD. - - On some platforms, the symbols required by \QD to create new widgets - are removed from plugins by the build system, making them unusable. - Using this macro ensures that the symbols are retained on those platforms, - and has no side effects on other platforms. - - For example, the \l{designer/worldtimeclockplugin}{World Time Clock Plugin} - example exports a custom widget class with the following declaration: - - \snippet examples/designer/worldtimeclockplugin/worldtimeclock.h 0 - \dots - \snippet examples/designer/worldtimeclockplugin/worldtimeclock.h 2 - - \sa {Creating Custom Widgets for Qt Designer} -*/ - - -// Doc: Abstract class - -/*! - \class QDesignerDnDItemInterface - \brief The QDesignerDnDItemInterface class provides an interface that is used to manage items - during a drag and drop operation. - \inmodule QtDesigner - \internal -*/ - -/*! - \enum QDesignerDnDItemInterface::DropType - - This enum describes the result of a drag and drop operation. - - \value MoveDrop The item was moved. - \value CopyDrop The item was copied. -*/ - -/*! - \fn QDesignerDnDItemInterface::QDesignerDnDItemInterface() - - Constructs a new interface to a drag and drop item. -*/ - -/*! - \fn QDesignerDnDItemInterface::~QDesignerDnDItemInterface() - - Destroys the interface to the item. -*/ - -/*! - \fn DomUI *QDesignerDnDItemInterface::domUi() const - - Returns a user interface object for the item. -*/ - -/*! - \fn QWidget *QDesignerDnDItemInterface::widget() const - - Returns the widget being copied or moved in the drag and drop operation. - - \sa source() -*/ - -/*! - \fn QWidget *QDesignerDnDItemInterface::decoration() const - - Returns the widget used to represent the item. -*/ - -/*! - \fn QPoint QDesignerDnDItemInterface::hotSpot() const - - Returns the cursor's hotspot. - - \sa QDrag::hotSpot() -*/ - -/*! - \fn DropType QDesignerDnDItemInterface::type() const - - Returns the type of drag and drop operation in progress. -*/ - -/*! - \fn QWidget *QDesignerDnDItemInterface::source() const - - Returns the widget that is the source of the drag and drop operation; i.e. the original - container of the widget being dragged. - - \sa widget() -*/ - - -// Doc: Abstract class - -/*! - \class QDesignerIconCacheInterface - \brief The QDesignerIconCacheInterface class provides an interface to \QD's icon cache. - \inmodule QtDesigner - \internal -*/ - -/*! - \fn QDesignerIconCacheInterface::QDesignerIconCacheInterface(QObject *parent) - - Constructs a new interface with the given \a parent. -*/ - -/*! - \fn QIcon QDesignerIconCacheInterface::nameToIcon(const QString &filePath, const QString &qrcPath) - - Returns the icon associated with the name specified by \a filePath in the resource - file specified by \a qrcPath. - - If \a qrcPath refers to a valid resource file, the name used for the file path is a path - within those resources; otherwise the file path refers to a local file. - - \sa {The Qt Resource System}, nameToPixmap() -*/ - -/*! - \fn QPixmap QDesignerIconCacheInterface::nameToPixmap(const QString &filePath, const QString &qrcPath) - - Returns the pixmap associated with the name specified by \a filePath in the resource - file specified by \a qrcPath. - - If \a qrcPath refers to a valid resource file, the name used for the file path is a path - within those resources; otherwise the file path refers to a local file. - - \sa {The Qt Resource System}, nameToIcon() -*/ - -/*! - \fn QString QDesignerIconCacheInterface::iconToFilePath(const QIcon &icon) const - - Returns the file path associated with the given \a icon. The file path is a path within - an application resources. -*/ - -/*! - \fn QString QDesignerIconCacheInterface::iconToQrcPath(const QIcon &icon) const - - Returns the path to the resource file that refers to the specified \a icon. The resource - path refers to a local file. -*/ - -/*! - \fn QString QDesignerIconCacheInterface::pixmapToFilePath(const QPixmap &pixmap) const - - Returns the file path associated with the given \a pixmap. The file path is a path within - an application resources. -*/ - -/*! - \fn QString QDesignerIconCacheInterface::pixmapToQrcPath(const QPixmap &pixmap) const - - Returns the path to the resource file that refers to the specified \a pixmap. The resource - path refers to a local file. -*/ - -/*! - \fn QList<QPixmap> QDesignerIconCacheInterface::pixmapList() const - - Returns a list of pixmaps for the icons provided by the icon cache. -*/ - -/*! - \fn QList<QIcon> QDesignerIconCacheInterface::iconList() const - - Returns a list of icons provided by the icon cache. -*/ - -/*! - \fn QString QDesignerIconCacheInterface::resolveQrcPath(const QString &filePath, const QString &qrcPath, const QString &workingDirectory) const - - Returns a path to a resource specified by the \a filePath within - the resource file located at \a qrcPath. If \a workingDirectory is - a valid path to a directory, the path returned will be relative to - that directory; otherwise an absolute path is returned. - - \omit - ### Needs checking - \endomit -*/ - - -// Doc: Interface only - -/*! - \class QDesignerPropertySheetExtension - - \brief The QDesignerPropertySheetExtension class allows you to - manipulate a widget's properties which is displayed in Qt - Designer's property editor. - - \sa QDesignerDynamicPropertySheetExtension - - \inmodule QtDesigner - - QDesignerPropertySheetExtension provides a collection of functions that - are typically used to query a widget's properties, and to - manipulate the properties' appearance in the property editor. For - example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 15 - - Note that if you change the value of a property using the - QDesignerPropertySheetExtension::setProperty() function, the undo - stack is not updated. To ensure that a property's value can be - reverted using the undo stack, you must use the - QDesignerFormWindowCursorInterface::setProperty() function, or its - buddy \l - {QDesignerFormWindowCursorInterface::setWidgetProperty()}{setWidgetProperty()}, - instead. - - When implementing a custom widget plugin, a pointer to \QD's - current QDesignerFormEditorInterface object (\c formEditor in the - example above) is provided by the - QDesignerCustomWidgetInterface::initialize() function's parameter. - - The property sheet, or any other extension, can be retrieved by - querying \QD's extension manager using the qt_extension() - function. When you want to release the extension, you only need to - delete the pointer. - - All widgets have a default property sheet which populates \QD's - property editor with the widget's properties (i.e the ones defined - with the Q_PROPERTY() macro). But QDesignerPropertySheetExtension - also provides an interface for creating custom property sheet - extensions. - - \warning \QD uses the QDesignerPropertySheetExtension to feed its - property editor. Whenever a widget is selected in its workspace, - \QD will query for the widget's property sheet extension. If the - selected widget has an implemented property sheet extension, this - extension will override the default property sheet. - - To create a property sheet extension, your extension class must - inherit from both QObject and - QDesignerPropertySheetExtension. Then, since we are implementing - an interface, we must ensure that it's made known to the meta - object system using the Q_INTERFACES() macro: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 16 - - This enables \QD to use qobject_cast() to query for supported - interfaces using nothing but a QObject pointer. - - In \QD the extensions are not created until they are - required. For that reason, when implementing a property sheet - extension, you must also create a QExtensionFactory, i.e a class - that is able to make an instance of your extension, and register - it using \QD's \l {QExtensionManager}{extension manager}. - - When a property sheet extension is required, \QD's \l - {QExtensionManager}{extension manager} will run through all its - registered factories calling QExtensionFactory::createExtension() - for each until the first one that is able to create a property - sheet extension for the selected widget, is found. This factory - will then make an instance of the extension. If no such factory - can be found, \QD will use the default property sheet. - - There are four available types of extensions in \QD: - QDesignerContainerExtension, QDesignerMemberSheetExtension, - QDesignerPropertySheetExtension and QDesignerTaskMenuExtension. Qt - Designer's behavior is the same whether the requested extension is - associated with a multi page container, a member sheet, a property - sheet or a task menu. - - The QExtensionFactory class provides a standard extension factory, - and can also be used as an interface for custom extension - factories. You can either create a new QExtensionFactory and - reimplement the QExtensionFactory::createExtension() function. For - example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 17 - - Or you can use an existing factory, expanding the - QExtensionFactory::createExtension() function to make the factory - able to create a property sheet extension extension as well. For - example: - - \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 18 - - For a complete example using an extension class, see the \l - {designer/taskmenuextension}{Task Menu Extension example}. The - example shows how to create a custom widget plugin for Qt - Designer, and how to to use the QDesignerTaskMenuExtension class - to add custom items to \QD's task menu. - - \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget - Extensions} -*/ - -/*! - \fn QDesignerPropertySheetExtension::~QDesignerPropertySheetExtension() - - Destroys the property sheet extension. -*/ - -/*! - \fn int QDesignerPropertySheetExtension::count() const - - Returns the selected widget's number of properties. -*/ - -/*! - \fn int QDesignerPropertySheetExtension::indexOf(const QString &name) const - - Returns the index for a given property \a name. - - \sa propertyName() -*/ - -/*! - \fn QString QDesignerPropertySheetExtension::propertyName(int index) const - - Returns the name of the property at the given \a index. - - \sa indexOf() -*/ - -/*! - \fn QString QDesignerPropertySheetExtension::propertyGroup(int index) const - - Returns the property group for the property at the given \a index. - - \QD's property editor supports property groups, i.e. sections of - related properties. A property can be related to a group using the - setPropertyGroup() function. The default group of any property is - the name of the class that defines it. For example, the - QObject::objectName property appears within the QObject property - group. - - \sa indexOf(), setPropertyGroup() -*/ - -/*! - \fn void QDesignerPropertySheetExtension::setPropertyGroup(int index, const QString &group) - - Sets the property group for the property at the given \a index to - \a group. - - Relating a property to a group makes it appear within that group's - section in the property editor. The default property group of any - property is the name of the class that defines it. For example, - the QObject::objectName property appears within the QObject - property group. - - \sa indexOf(), property(), propertyGroup() -*/ - -/*! - \fn bool QDesignerPropertySheetExtension::hasReset(int index) const - - Returns true if the property at the given \a index has a reset - button in \QD's property editor, otherwise false. - - \sa indexOf(), reset() -*/ - -/*! - \fn bool QDesignerPropertySheetExtension::reset(int index) - - Resets the value of the property at the given \a index, to the - default value. Returns true if a default value could be found, otherwise false. - - \sa indexOf(), hasReset(), isChanged() -*/ - -/*! - \fn bool QDesignerPropertySheetExtension::isVisible(int index) const - - Returns true if the property at the given \a index is visible in - \QD's property editor, otherwise false. - - \sa indexOf(), setVisible() -*/ - -/*! - \fn void QDesignerPropertySheetExtension::setVisible(int index, bool visible) - - If \a visible is true, the property at the given \a index is - visible in \QD's property editor; otherwise the property is - hidden. - - \sa indexOf(), isVisible() -*/ - -/*! - \fn bool QDesignerPropertySheetExtension::isAttribute(int index) const - - Returns true if the property at the given \a index is an attribute, - which will be \e excluded from the UI file, otherwise false. - - \sa indexOf(), setAttribute() -*/ - -/*! - \fn void QDesignerPropertySheetExtension::setAttribute(int index, bool attribute) - - If \a attribute is true, the property at the given \a index is - made an attribute which will be \e excluded from the UI file; - otherwise it will be included. - - \sa indexOf(), isAttribute() -*/ - -/*! - \fn QVariant QDesignerPropertySheetExtension::property(int index) const - - Returns the value of the property at the given \a index. - - \sa indexOf(), setProperty(), propertyGroup() -*/ - -/*! - \fn void QDesignerPropertySheetExtension::setProperty(int index, const QVariant &value) - - Sets the \a value of the property at the given \a index. - - \warning If you change the value of a property using this - function, the undo stack is not updated. To ensure that a - property's value can be reverted using the undo stack, you must - use the QDesignerFormWindowCursorInterface::setProperty() - function, or its buddy \l - {QDesignerFormWindowCursorInterface::setWidgetProperty()}{setWidgetProperty()}, - instead. - - \sa indexOf(), property(), propertyGroup() -*/ - -/*! - \fn bool QDesignerPropertySheetExtension::isChanged(int index) const - - Returns true if the value of the property at the given \a index - differs from the property's default value, otherwise false. - - \sa indexOf(), setChanged(), reset() -*/ - -/*! - \fn void QDesignerPropertySheetExtension::setChanged(int index, bool changed) - - Sets whether the property at the given \a index is different from - its default value, or not, depending on the \a changed parameter. - - \sa indexOf(), isChanged() -*/ - -// Doc: Interface only - -/*! - \class QDesignerDynamicPropertySheetExtension - - \brief The QDesignerDynamicPropertySheetExtension class allows you to - manipulate a widget's dynamic properties in Qt Designer's property editor. - - \sa QDesignerPropertySheetExtension, {QObject#Dynamic Properties}{Dynamic Properties} - - \inmodule QtDesigner - \since 4.3 -*/ - -/*! - \fn QDesignerDynamicPropertySheetExtension::~QDesignerDynamicPropertySheetExtension() - - Destroys the dynamic property sheet extension. -*/ - -/*! - \fn bool QDesignerDynamicPropertySheetExtension::dynamicPropertiesAllowed() const - - Returns true if the widget supports dynamic properties; otherwise returns false. -*/ - -/*! - \fn int QDesignerDynamicPropertySheetExtension::addDynamicProperty(const QString &propertyName, const QVariant &value) - - Adds a dynamic property named \a propertyName and sets its value to \a value. - Returns the index of the property if it was added successfully; otherwise returns -1 to - indicate failure. -*/ - -/*! - \fn bool QDesignerDynamicPropertySheetExtension::removeDynamicProperty(int index) - - Removes the dynamic property at the given \a index. - Returns true if the operation succeeds; otherwise returns false. -*/ - -/*! - \fn bool QDesignerDynamicPropertySheetExtension::isDynamicProperty(int index) const - - Returns true if the property at the given \a index is a dynamic property; otherwise - returns false. -*/ - -/*! - \fn bool QDesignerDynamicPropertySheetExtension::canAddDynamicProperty(const QString &propertyName) const - - Returns true if \a propertyName is a valid, unique name for a dynamic - property; otherwise returns false. - -*/ diff --git a/doc/src/snippets/code/doc_src_properties.qdoc b/doc/src/snippets/code/doc_src_properties.qdoc index 377cc9c..3c9109f 100644 --- a/doc/src/snippets/code/doc_src_properties.qdoc +++ b/doc/src/snippets/code/doc_src_properties.qdoc @@ -7,7 +7,9 @@ Q_PROPERTY(type name [DESIGNABLE bool] [SCRIPTABLE bool] [STORED bool] - [USER bool]) + [USER bool] + [CONSTANT] + [FINAL]) //! [0] diff --git a/doc/src/snippets/code/src_corelib_io_qfileinfo.cpp b/doc/src/snippets/code/src_corelib_io_qfileinfo.cpp index 2ab15ee..89b4f33 100644 --- a/doc/src/snippets/code/src_corelib_io_qfileinfo.cpp +++ b/doc/src/snippets/code/src_corelib_io_qfileinfo.cpp @@ -13,9 +13,9 @@ info1.size(); // returns 56201 info1.symLinkTarget(); // returns "/opt/pretty++/bin/untabify" QFileInfo info2(info1.symLinkTarget()); -info1.isSymLink(); // returns false -info1.absoluteFilePath(); // returns "/opt/pretty++/bin/untabify" -info1.size(); // returns 56201 +info2.isSymLink(); // returns false +info2.absoluteFilePath(); // returns "/opt/pretty++/bin/untabify" +info2.size(); // returns 56201 #endif //! [0] diff --git a/doc/src/snippets/code/src_corelib_tools_qlistdata.cpp b/doc/src/snippets/code/src_corelib_tools_qlistdata.cpp index 7d75e1b..3b9a756 100644 --- a/doc/src/snippets/code/src_corelib_tools_qlistdata.cpp +++ b/doc/src/snippets/code/src_corelib_tools_qlistdata.cpp @@ -190,10 +190,10 @@ QVector<QString> vect = list.toVector(); //! [23] -QSet<double> set; -set << 20.0 << 30.0 << 40.0 << ... << 70.0; +QSet<int> set; +set << 20 << 30 << 40 << ... << 70; -QList<double> list = QList<double>::fromSet(set); +QList<int> list = QList<int>::fromSet(set); qSort(list); //! [23] diff --git a/doc/src/tech-preview/images/mainwindow-docks-example.png b/doc/src/tech-preview/images/mainwindow-docks-example.png Binary files differdeleted file mode 100644 index a5641fd..0000000 --- a/doc/src/tech-preview/images/mainwindow-docks-example.png +++ /dev/null diff --git a/doc/src/tech-preview/images/mainwindow-docks.png b/doc/src/tech-preview/images/mainwindow-docks.png Binary files differdeleted file mode 100644 index 96dafc3..0000000 --- a/doc/src/tech-preview/images/mainwindow-docks.png +++ /dev/null diff --git a/doc/src/tech-preview/images/plaintext-layout.png b/doc/src/tech-preview/images/plaintext-layout.png Binary files differdeleted file mode 100644 index 9a0f9c1..0000000 --- a/doc/src/tech-preview/images/plaintext-layout.png +++ /dev/null diff --git a/doc/src/tech-preview/known-issues.html b/doc/src/tech-preview/known-issues.html deleted file mode 100644 index 885104e..0000000 --- a/doc/src/tech-preview/known-issues.html +++ /dev/null @@ -1,110 +0,0 @@ -<html> -<head> -<title>Known issues</title> -<link rel="stylesheet" href="http://qtsoftware.com/trollstyle.css"> -<style type="text/css"> - H2 { position: relative; top: 10px; } - H4 { margin-left: 15px; } - .issue { margin-left: 15px; } - .workaround { margin-left: 30px; margin-right: 30px; padding: 2px; border-style: solid; border-color: #AACA00; border-width: 1px; } -</style> -</head> -<body bgcolor="#ffffff"> -<table border="0" cellpadding="0" cellspacing="0" width="100%"> - <tr bgcolor="#E5E5E5"> - <td><img src="http://doc.trolltech.com/doctitle.png" width="443" height="32" border="0"></td> - <td align="right" valign="middle"></td></tr></table> -<h1>Known Issues: Qt 4.0.0 Technology Preview 1</h1> -<p> - This is the list of known and reported issues for the Qt 4.0.0 - Technology Preview 1. This list is updated daily. -</p> -<br><br> -<table width="100%"> - <tr><td align="left"><b><a href="#buildissues">Build Issues</a></b></td></tr> - <tr><td align="left"><b><a href="#general">General</a></b></td></tr> - <tr><td align="left"><b><a href="#demos">Demos</a></b></td></tr> - <tr><td align="left"><b><a href="#window">Windows specific</a></b></td></tr> - <tr><td align="left"><b><a href="#x11">X11 specific</a></b></td></tr> - <tr><td align="left"><b><a href="#mac">Mac specific</a></b></td></tr> -</table> -<br><br> -<!-- Build section ------------------------------------------------- --> -<a name="buildissues"></a> -<h2>Build Issues</h2> - <h4>Static libraries on Mac OS X</h4> - <p class="issue">Building a static build on mac will fail the first time.</p> - <p class="workaround">Run qmake a second time.</p> - - <h4>QTDIR</h4> - <p class="issue">Some applications(e.g. uic3) need QTDIR set and QTDIR/bin in the path to work.</p> - <p class="workaround">Set QTDIR and PATH as described in INSTALL.</p> - - <h4>ODBC driver on Windows</h4> - <p class="issue">Problems compiling the ODBC driver.</p> - <p class="workaround">Change the include from qapplication.h to qcoreapplication.h</p> - - <h4>QtGui does not link because of accessibility errors</h4> - <p class="issue">undefined reference to QAccessible::setRootObject(QObject*)</p> - <p class="workaround">Rerun configure and rebuild, if that does not help, add the contents of $QTDIR/.qt.config to $QTDIR/.qmake.cache</p> - - -<!-- General section ---------------------------------------------- --> -<a name="general"></a> -<h2>General</h2> - - <h4>No connection to Oracle</h4> - <p class="issue">I cannot connect to my Oracle server</p> - <p class="workaround">Use the Oracle >= 9 client libraries, currently there is no workaround for OCI 8</p> - - <h4>QSqlModel</h4> - <p class="issue">Only the first 16 rows from a database result are displayed</p> - <p class="workaround">You are using a database driver which does not report back the size of a result set and incremental fetching is not yet implemented in the itemviews. You can either use another database like MySQL or increase the QSQL_PREFETCH in qsqlmodel.cpp</p> - - -<!-- Demos section ------------------------------------------------- --> -<a name="demos"></a> -<h2>Demos</h2> - - <h4>-</h4> - - -<!-- Windows platform section -------------------------------------- --> -<a name="window"></a> -<h2>Windows specific</h2> - - <h4>Qt Assistant</h4> - <p class="issue">Problems with zooming and laying out text. Sometimes the text overlaps.</p> - - <h4>GDI handle leak</h4> - <p class="issue">Setting fonts through QPainter::setFont() leaks GDI - handles on windows. This is for instance visible in the OpenGL - part of the Arthur demo. The issue will be fixed before the next - preview.</p> - -<!-- X11 platform section ------------------------------------------ --> -<a name="x11"></a> -<h2>X11 specific</h2> - - <h4>-</h4> - - -<!-- Mac platform section ------------------------------------------ --> -<a name="mac"></a> -<h2>Mac specific</h2> - - <h4>QComboBox</h4> - <p class="issue">Does not have the native Mac look and feel yet. It is rendered in a Windows-like style.</p> - - <h4>Clicking on widgets</h4> - <p class="issue"><i>Jaguar 10.2 only:</i> Clicking on widgets that has a focusrect may fail.</p> - <p class="workaround">Use 10.3 for now.</p> - - -<!-- [Page footer] ------------------------------------------------- --> -<table width="100%"> - <tr><td><hr size="1" noshade></td></tr> - <tr><td align="center" valign="bottom"><a href="http://qtsoftware.com">qtsoftware.com</a></td></tr> -</table> -</body> -</html> diff --git a/doc/src/topics.qdoc b/doc/src/topics.qdoc index 7f832ab..15be0ade 100644 --- a/doc/src/topics.qdoc +++ b/doc/src/topics.qdoc @@ -150,18 +150,6 @@ These guides provide specific help about specific Qt-related topics. */ /*! -\group licensing -\title Licensing Information -\ingroup topics -\brief Information about licenses and licensing issues. - -These documents include information about Qt's licenses and the licenses -of third party components used in Qt. - -\generatelist{related} -*/ - -/*! \group platform-notes \title Platform-Specific Notes \ingroup topics diff --git a/doc/src/tutorials/addressbook-fr.qdoc b/doc/src/tutorials/addressbook-fr.qdoc index 78f6821..739f047 100644 --- a/doc/src/tutorials/addressbook-fr.qdoc +++ b/doc/src/tutorials/addressbook-fr.qdoc @@ -220,7 +220,7 @@ On remarque que le label \c AddressLabel est positionné en utilisant Qt::AlignTop comme argument optionnel. Ceci est destiné à assurer qu'il ne sera pas centré verticalement dans la cellule (1,0). Pour un aperçu rapide des layouts de Qt, - consultez la section \l{Layout Classes}. + consultez la section \l{Layout Management}. Afin d'installer l'objet layout dans un widget, il faut appeler la méthode \l{QWidget::setLayout()}{setLayout()} du widget en question: diff --git a/doc/src/tutorials/addressbook.qdoc b/doc/src/tutorials/addressbook.qdoc index 95394eb..fd08bfe 100644 --- a/doc/src/tutorials/addressbook.qdoc +++ b/doc/src/tutorials/addressbook.qdoc @@ -222,8 +222,8 @@ Notice that \c addressLabel is positioned using Qt::AlignTop as an additional argument. This is to make sure it is not vertically centered in - cell (1,0). For a basic overview on Qt Layouts, refer to the \l{Layout Classes} - document. + cell (1,0). For a basic overview on Qt Layouts, refer to the + \l{Layout Management} documentation. In order to install the layout object onto the widget, we have to invoke the widget's \l{QWidget::setLayout()}{setLayout()} function: diff --git a/examples/animation/stickman/lifecycle.cpp b/examples/animation/stickman/lifecycle.cpp index c761d87..700916d 100644 --- a/examples/animation/stickman/lifecycle.cpp +++ b/examples/animation/stickman/lifecycle.cpp @@ -56,8 +56,9 @@ public: { } KeyPressTransition(GraphicsView *receiver, Qt::Key key, QAbstractState *target) - : QSignalTransition(receiver, SIGNAL(keyPressed(int)), QList<QAbstractState*>() << target), m_key(key) + : QSignalTransition(receiver, SIGNAL(keyPressed(int))), m_key(key) { + setTargetState(target); } virtual bool eventTest(QEvent *e) @@ -78,8 +79,9 @@ class LightningStrikesTransition: public QEventTransition { public: LightningStrikesTransition(QAbstractState *target) - : QEventTransition(this, QEvent::Timer, QList<QAbstractState*>() << target) + : QEventTransition(this, QEvent::Timer) { + setTargetState(target); qsrand((uint)QDateTime::currentDateTime().toTime_t()); startTimer(1000); } diff --git a/examples/examples.pro b/examples/examples.pro index 5855e4f..e6cece9 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -26,6 +26,7 @@ SUBDIRS = \ multitouch \ gestures +contains(QT_CONFIG, multimedia):!static: SUBDIRS += multimedia contains(QT_CONFIG, phonon):!static: SUBDIRS += phonon contains(QT_CONFIG, webkit): SUBDIRS += webkit embedded:SUBDIRS += qws diff --git a/examples/gestures/imageviewer/imageviewer.pro b/examples/gestures/imageviewer/imageviewer.pro index 4c35dce..efbca00 100644 --- a/examples/gestures/imageviewer/imageviewer.pro +++ b/examples/gestures/imageviewer/imageviewer.pro @@ -1,12 +1,11 @@ -###################################################################### -# Automatically generated by qmake (2.01a) Thu Sep 11 17:18:17 2008 -###################################################################### - TEMPLATE = app TARGET = DEPENDPATH += . INCLUDEPATH += . # Input -HEADERS += imagewidget.h -SOURCES += imagewidget.cpp main.cpp +HEADERS += imagewidget.h \ + tapandholdgesture.h +SOURCES += imagewidget.cpp \ + tapandholdgesture.cpp \ + main.cpp diff --git a/examples/gestures/imageviewer/imagewidget.cpp b/examples/gestures/imageviewer/imagewidget.cpp index 717bb09..99889ed 100644 --- a/examples/gestures/imageviewer/imagewidget.cpp +++ b/examples/gestures/imageviewer/imagewidget.cpp @@ -65,7 +65,7 @@ ImageWidget::ImageWidget(QWidget *parent) panGesture = new QPanGesture(this); connect(panGesture, SIGNAL(triggered()), this, SLOT(gestureTriggered())); - tapAndHoldGesture = new QTapAndHoldGesture(this); + tapAndHoldGesture = new TapAndHoldGesture(this); connect(tapAndHoldGesture, SIGNAL(triggered()), this, SLOT(gestureTriggered())); } @@ -100,7 +100,7 @@ void ImageWidget::paintEvent(QPaintEvent*) p.setPen(QPen(Qt::gray, 2)); p.drawEllipse(touchFeedback.position, 5, 5); if (touchFeedback.doubleTapped) { - p.setPen(QPen(Qt::gray, 2, Qt::DotLine)); + p.setPen(QPen(Qt::darkGray, 2, Qt::DotLine)); p.drawEllipse(touchFeedback.position, 15, 15); } else if (touchFeedback.tapAndHoldState != 0) { QPoint pts[8] = { @@ -159,40 +159,37 @@ void ImageWidget::gestureTriggered() touchFeedback.tapped = false; touchFeedback.doubleTapped = false; - QGesture *g = qobject_cast<QGesture*>(sender()); if (sender() == panGesture) { + QPanGesture *pg = qobject_cast<QPanGesture*>(sender()); if (zoomedIn) { - // usual panning #ifndef QT_NO_CURSOR - if (g->state() == Qt::GestureStarted) - setCursor(Qt::SizeAllCursor); - else - setCursor(Qt::ArrowCursor); + switch (pg->state()) { + case Qt::GestureStarted: + case Qt::GestureUpdated: + setCursor(Qt::SizeAllCursor); + break; + default: + setCursor(Qt::ArrowCursor); + } #endif - const int dx = g->pos().x() - g->lastPos().x(); - const int dy = g->pos().y() - g->lastPos().y(); - horizontalOffset += dx; - verticalOffset += dy; + horizontalOffset += pg->lastOffset().width(); + verticalOffset += pg->lastOffset().height(); update(); } else { // only slide gesture should be accepted - const QPanGesture *pg = static_cast<const QPanGesture*>(g); - if (g->state() == Qt::GestureFinished) { + if (pg->state() == Qt::GestureFinished) { touchFeedback.sliding = false; zoomed = rotated = false; - if (pg->totalOffset().width() > 0) { - qDebug() << "slide right"; + if (pg->totalOffset().width() > 0) goNextImage(); - } else { - qDebug() << "slide left"; + else goPrevImage(); - } updateImage(); } } feedbackFadeOutTimer.start(500, this); } else if (sender() == tapAndHoldGesture) { - if (g->state() == Qt::GestureFinished) { + if (tapAndHoldGesture->state() == Qt::GestureFinished) { qDebug() << "tap and hold detected"; touchFeedback.reset(); update(); @@ -201,7 +198,7 @@ void ImageWidget::gestureTriggered() menu.addAction("Action 1"); menu.addAction("Action 2"); menu.addAction("Action 3"); - menu.exec(mapToGlobal(g->pos())); + menu.exec(mapToGlobal(tapAndHoldGesture->pos())); } feedbackFadeOutTimer.start(500, this); } diff --git a/examples/gestures/imageviewer/imagewidget.h b/examples/gestures/imageviewer/imagewidget.h index e12634d..d8d5f8d 100644 --- a/examples/gestures/imageviewer/imagewidget.h +++ b/examples/gestures/imageviewer/imagewidget.h @@ -48,6 +48,8 @@ #include <QtGui> +#include "tapandholdgesture.h" + class ImageWidget : public QWidget { Q_OBJECT @@ -79,7 +81,7 @@ private: void goToImage(int index); QPanGesture *panGesture; - QTapAndHoldGesture *tapAndHoldGesture; + TapAndHoldGesture *tapAndHoldGesture; QString path; QStringList files; diff --git a/examples/gestures/imageviewer/main.cpp b/examples/gestures/imageviewer/main.cpp index 6bda418..cd1928b 100644 --- a/examples/gestures/imageviewer/main.cpp +++ b/examples/gestures/imageviewer/main.cpp @@ -81,6 +81,9 @@ int main(int argc, char *argv[]) if (QApplication::arguments().size() > 1) w.openDirectory(QApplication::arguments().at(1)); + else + w.openDirectory(QFileDialog::getExistingDirectory(0, "Select image folder")); + return app.exec(); } diff --git a/examples/gestures/imageviewer/tapandholdgesture.cpp b/examples/gestures/imageviewer/tapandholdgesture.cpp new file mode 100644 index 0000000..ff5284e --- /dev/null +++ b/examples/gestures/imageviewer/tapandholdgesture.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tapandholdgesture.h" + +#include <QtGui/qevent.h> + +/*! + \class TapAndHoldGesture + \since 4.6 + + \brief The TapAndHoldGesture class represents a Tap-and-Hold gesture, + providing additional information. +*/ + +const int TapAndHoldGesture::iterationCount = 40; +const int TapAndHoldGesture::iterationTimeout = 50; + +/*! + Creates a new Tap and Hold gesture handler object and marks it as a child + of \a parent. + + On some platforms like Windows there is a system-wide tap and hold gesture + that cannot be overriden, hence the gesture might never trigger and default + context menu will be shown instead. +*/ +TapAndHoldGesture::TapAndHoldGesture(QWidget *parent) + : QGesture(parent), iteration(0) +{ +} + +/*! \internal */ +bool TapAndHoldGesture::filterEvent(QEvent *event) +{ + if (!event->spontaneous()) + return false; + const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); + switch (event->type()) { + case QEvent::TouchBegin: { + if (timer.isActive()) + timer.stop(); + timer.start(TapAndHoldGesture::iterationTimeout, this); + const QPoint p = ev->touchPoints().at(0).pos().toPoint(); + position = p; + break; + } + case QEvent::TouchUpdate: + if (ev->touchPoints().size() == 1) { + const QPoint startPos = ev->touchPoints().at(0).startPos().toPoint(); + const QPoint pos = ev->touchPoints().at(0).pos().toPoint(); + if ((startPos - pos).manhattanLength() > 15) + reset(); + } else { + reset(); + } + break; + case QEvent::TouchEnd: + reset(); + break; + default: + break; + } + return false; +} + +/*! \internal */ +void TapAndHoldGesture::timerEvent(QTimerEvent *event) +{ + if (event->timerId() != timer.timerId()) + return; + if (iteration == TapAndHoldGesture::iterationCount) { + timer.stop(); + setState(Qt::GestureFinished); + emit triggered(); + } else { + setState(Qt::GestureStarted); + emit triggered(); + } + ++iteration; +} + +/*! \internal */ +void TapAndHoldGesture::reset() +{ + if (state() != Qt::NoGesture) + emit cancelled(); + setState(Qt::NoGesture); + timer.stop(); + iteration = 0; +} + +/*! + \property TapAndHoldGesture::pos + + \brief The position of the gesture. +*/ +QPoint TapAndHoldGesture::pos() const +{ + return position; +} diff --git a/doc/src/annotated.qdoc b/examples/gestures/imageviewer/tapandholdgesture.h index a817df6..e0d50b5 100644 --- a/doc/src/annotated.qdoc +++ b/examples/gestures/imageviewer/tapandholdgesture.h @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the documentation of the Qt Toolkit. +** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,24 +39,35 @@ ** ****************************************************************************/ -/**************************************************************************** -** -** Documentation for class overview. -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt GUI Toolkit. -** EDITIONS: FREE, PROFESSIONAL, ENTERPRISE -** -****************************************************************************/ +#ifndef TAPANDHOLDGESTURE_H +#define TAPANDHOLDGESTURE_H + +#include <QtCore/QBasicTimer> +#include <QtGui/QGesture> +#include <QtGui/QWidget> + +class TapAndHoldGesture : public QGesture +{ + Q_OBJECT + Q_PROPERTY(QPoint pos READ pos) + +public: + TapAndHoldGesture(QWidget *parent); + + bool filterEvent(QEvent *event); + void reset(); + + QPoint pos() const; -/*! - \page annotated.html - \title Annotated Class Index - \ingroup classlists +protected: + void timerEvent(QTimerEvent *event); - Qt's classes with brief descriptions: +private: + QBasicTimer timer; + int iteration; + QPoint position; + static const int iterationCount; + static const int iterationTimeout; +}; - \generatelist annotatedclasses -*/ +#endif // TAPANDHOLDGESTURE_H diff --git a/examples/graphicsview/collidingmice/mouse.cpp b/examples/graphicsview/collidingmice/mouse.cpp index c6a67b1..4cc29dd 100644 --- a/examples/graphicsview/collidingmice/mouse.cpp +++ b/examples/graphicsview/collidingmice/mouse.cpp @@ -64,7 +64,7 @@ Mouse::Mouse() : angle(0), speed(0), mouseEyeDirection(0), color(qrand() % 256, qrand() % 256, qrand() % 256) { - rotate(qrand() % (360 * 16)); + setRotation(qrand() % (360 * 16)); } //! [0] @@ -195,7 +195,7 @@ void Mouse::advance(int step) qreal dx = ::sin(angle) * 10; mouseEyeDirection = (qAbs(dx / 5) < 1) ? 0 : dx / 5; - rotate(dx); + setRotation(rotation() + dx); setPos(mapToParent(0, -(3 + sin(speed) * 3))); } //! [11] diff --git a/examples/itemviews/frozencolumn/freezetablewidget.h b/examples/itemviews/frozencolumn/freezetablewidget.h index e22660e..f7b8e43 100644 --- a/examples/itemviews/frozencolumn/freezetablewidget.h +++ b/examples/itemviews/frozencolumn/freezetablewidget.h @@ -39,7 +39,6 @@ ** ****************************************************************************/ - #ifndef FREEZETABLEWIDGET_H #define FREEZETABLEWIDGET_H diff --git a/examples/itemviews/frozencolumn/main.cpp b/examples/itemviews/frozencolumn/main.cpp index 8ed9d3b..9f6a637 100644 --- a/examples/itemviews/frozencolumn/main.cpp +++ b/examples/itemviews/frozencolumn/main.cpp @@ -39,7 +39,6 @@ ** ****************************************************************************/ - #include <QApplication> #include <QStandardItemModel> #include <QFile> diff --git a/examples/multimedia/README b/examples/multimedia/README new file mode 100644 index 0000000..aa78e36 --- /dev/null +++ b/examples/multimedia/README @@ -0,0 +1,34 @@ + +The example launcher provided with Qt can be used to explore each of the +examples in this directory. + +Documentation for these examples can be found via the Tutorial and Examples +link in the main Qt documentation. + + +Finding the Qt Examples and Demos launcher +========================================== + +On Windows: + +The launcher can be accessed via the Windows Start menu. Select the menu +entry entitled "Qt Examples and Demos" entry in the submenu containing +the Qt tools. + +On Mac OS X: + +For the binary distribution, the qtdemo executable is installed in the +/Developer/Applications/Qt directory. For the source distribution, it is +installed alongside the other Qt tools on the path specified when Qt is +configured. + +On Unix/Linux: + +The qtdemo executable is installed alongside the other Qt tools on the path +specified when Qt is configured. + +On all platforms: + +The source code for the launcher can be found in the demos/qtdemo directory +in the Qt package. This example is built at the same time as the Qt libraries, +tools, examples, and demonstrations. diff --git a/examples/multimedia/audio/audio.pro b/examples/multimedia/audio/audio.pro new file mode 100644 index 0000000..c64bb34 --- /dev/null +++ b/examples/multimedia/audio/audio.pro @@ -0,0 +1,10 @@ +TEMPLATE = subdirs +SUBDIRS = audioinput \ + audiooutput \ + audiodevices + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS audio.pro README +sources.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio +INSTALLS += target sources diff --git a/examples/multimedia/audio/audiodevices/audiodevices.cpp b/examples/multimedia/audio/audiodevices/audiodevices.cpp new file mode 100644 index 0000000..2a3af98 --- /dev/null +++ b/examples/multimedia/audio/audiodevices/audiodevices.cpp @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QDebug> +#include <QAudioDeviceInfo> + +#include "audiodevices.h" + +AudioDevicesBase::AudioDevicesBase( QMainWindow *parent, Qt::WFlags f ) +{ + Q_UNUSED(parent) + Q_UNUSED(f) + setupUi( this ); +} + +AudioDevicesBase::~AudioDevicesBase() {} + + +AudioTest::AudioTest( QMainWindow *parent, Qt::WFlags f ) + : AudioDevicesBase( parent, f ) +{ + mode = QAudio::AudioOutput; + modeBox->addItem("Input"); + modeBox->addItem("Output"); + + connect(testButton,SIGNAL(clicked()),SLOT(test())); + connect(modeBox,SIGNAL(activated(int)),SLOT(modeChanged(int))); + connect(deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int))); + connect(frequencyBox,SIGNAL(activated(int)),SLOT(freqChanged(int))); + connect(channelsBox,SIGNAL(activated(int)),SLOT(channelChanged(int))); + connect(codecsBox,SIGNAL(activated(int)),SLOT(codecChanged(int))); + connect(sampleSizesBox,SIGNAL(activated(int)),SLOT(sampleSizeChanged(int))); + connect(sampleTypesBox,SIGNAL(activated(int)),SLOT(sampleTypeChanged(int))); + connect(endianBox,SIGNAL(activated(int)),SLOT(endianChanged(int))); + + device = 0; + + modeBox->setCurrentIndex(0); + modeChanged(0); + deviceBox->setCurrentIndex(0); + deviceChanged(0); +} + +AudioTest::~AudioTest() +{ +} + +void AudioTest::test() +{ + // tries to set all the settings picked. + + if(device) { + if(device->isFormatSupported(settings)) { + logOutput->append("Success"); + nearestFreq->setText(""); + nearestChannel->setText(""); + nearestCodec->setText(""); + nearestSampleSize->setText(""); + nearestSampleType->setText(""); + nearestEndian->setText(""); + } else { + QAudioFormat nearest = device->nearestFormat(settings); + logOutput->append(tr("Failed")); + nearestFreq->setText(QString("%1").arg(nearest.frequency())); + nearestChannel->setText(QString("%1").arg(nearest.channels())); + nearestCodec->setText(nearest.codec()); + nearestSampleSize->setText(QString("%1").arg(nearest.sampleSize())); + + switch(nearest.sampleType()) { + case QAudioFormat::SignedInt: + nearestSampleType->setText("SignedInt"); + break; + case QAudioFormat::UnSignedInt: + nearestSampleType->setText("UnSignedInt"); + break; + case QAudioFormat::Float: + nearestSampleType->setText("Float"); + break; + case QAudioFormat::Unknown: + nearestSampleType->setText("Unknown"); + } + switch(nearest.byteOrder()) { + case QAudioFormat::LittleEndian: + nearestEndian->setText("LittleEndian"); + break; + case QAudioFormat::BigEndian: + nearestEndian->setText("BigEndian"); + } + } + } + else + logOutput->append("No Device"); +} + +void AudioTest::modeChanged(int idx) +{ + // mode has changed + if(idx == 0) + mode=QAudio::AudioInput; + else + mode=QAudio::AudioOutput; + + deviceBox->clear(); + QList<QAudioDeviceId> devices = QAudioDeviceInfo::deviceList(mode); + for(int i = 0; i < devices.size(); ++i) { + deviceBox->addItem(QAudioDeviceInfo(devices.at(i)).deviceName(), qVariantFromValue(devices.at(i))); + } +} + +void AudioTest::deviceChanged(int idx) +{ + delete device; + device = 0; + + if (deviceBox->count() == 0) + return; + + // device has changed + deviceHandle = deviceBox->itemData(idx).value<QAudioDeviceId>(); + device = new QAudioDeviceInfo(deviceHandle, this); + + frequencyBox->clear(); + QList<int> freqz = device->supportedFrequencies(); + for(int i = 0; i < freqz.size(); ++i) + frequencyBox->addItem(QString("%1").arg(freqz.at(i))); + if(freqz.size()) + settings.setFrequency(freqz.at(0)); + + channelsBox->clear(); + QList<int> chz = device->supportedChannels(); + for(int i = 0; i < chz.size(); ++i) + channelsBox->addItem(QString("%1").arg(chz.at(i))); + if(chz.size()) + settings.setChannels(chz.at(0)); + + codecsBox->clear(); + QStringList codecz = device->supportedCodecs(); + for(int i = 0; i < codecz.size(); ++i) + codecsBox->addItem(QString("%1").arg(codecz.at(i))); + if(codecz.size()) + settings.setCodec(codecz.at(0)); + // Add false to create failed condition! + codecsBox->addItem("audio/mpeg"); + + sampleSizesBox->clear(); + QList<int> sampleSizez = device->supportedSampleSizes(); + for(int i = 0; i < sampleSizez.size(); ++i) + sampleSizesBox->addItem(QString("%1").arg(sampleSizez.at(i))); + if(sampleSizez.size()) + settings.setSampleSize(sampleSizez.at(0)); + + sampleTypesBox->clear(); + QList<QAudioFormat::SampleType> sampleTypez = device->supportedSampleTypes(); + for(int i = 0; i < sampleTypez.size(); ++i) { + switch(sampleTypez.at(i)) { + case QAudioFormat::SignedInt: + sampleTypesBox->addItem("SignedInt"); + break; + case QAudioFormat::UnSignedInt: + sampleTypesBox->addItem("UnSignedInt"); + break; + case QAudioFormat::Float: + sampleTypesBox->addItem("Float"); + break; + case QAudioFormat::Unknown: + sampleTypesBox->addItem("Unknown"); + } + if(sampleTypez.size()) + settings.setSampleType(sampleTypez.at(0)); + } + + endianBox->clear(); + QList<QAudioFormat::Endian> endianz = device->supportedByteOrders(); + for(int i = 0; i < endianz.size(); ++i) { + switch(endianz.at(i)) { + case QAudioFormat::LittleEndian: + endianBox->addItem("Little Endian"); + break; + case QAudioFormat::BigEndian: + endianBox->addItem("Big Endian"); + break; + } + } + if(endianz.size()) + settings.setByteOrder(endianz.at(0)); +} + +void AudioTest::freqChanged(int idx) +{ + // freq has changed + settings.setFrequency(frequencyBox->itemText(idx).toInt()); +} + +void AudioTest::channelChanged(int idx) +{ + settings.setChannels(channelsBox->itemText(idx).toInt()); +} + +void AudioTest::codecChanged(int idx) +{ + settings.setCodec(codecsBox->itemText(idx)); +} + +void AudioTest::sampleSizeChanged(int idx) +{ + settings.setSampleSize(sampleSizesBox->itemText(idx).toInt()); +} + +void AudioTest::sampleTypeChanged(int idx) +{ + switch(sampleTypesBox->itemText(idx).toInt()) { + case QAudioFormat::SignedInt: + settings.setSampleType(QAudioFormat::SignedInt); + break; + case QAudioFormat::UnSignedInt: + settings.setSampleType(QAudioFormat::UnSignedInt); + break; + case QAudioFormat::Float: + settings.setSampleType(QAudioFormat::Float); + } +} + +void AudioTest::endianChanged(int idx) +{ + switch(endianBox->itemText(idx).toInt()) { + case QAudioFormat::LittleEndian: + settings.setByteOrder(QAudioFormat::LittleEndian); + break; + case QAudioFormat::BigEndian: + settings.setByteOrder(QAudioFormat::BigEndian); + } +} + diff --git a/examples/multimedia/audio/audiodevices/audiodevices.h b/examples/multimedia/audio/audiodevices/audiodevices.h new file mode 100644 index 0000000..34a531b --- /dev/null +++ b/examples/multimedia/audio/audiodevices/audiodevices.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QObject> +#include <QMainWindow> +#include <QAudioDeviceInfo> + +#include "ui_audiodevicesbase.h" + +class AudioDevicesBase : public QMainWindow, public Ui::AudioDevicesBase +{ +public: + AudioDevicesBase( QMainWindow *parent = 0, Qt::WFlags f = 0 ); + virtual ~AudioDevicesBase(); +}; + +class AudioTest : public AudioDevicesBase +{ + Q_OBJECT +public: + AudioTest( QMainWindow *parent = 0, Qt::WFlags f = 0 ); + virtual ~AudioTest(); + + QAudioDeviceId deviceHandle; + QAudioDeviceInfo* device; + QAudioFormat settings; + QAudio::Mode mode; + +private slots: + void modeChanged(int idx); + void deviceChanged(int idx); + void freqChanged(int idx); + void channelChanged(int idx); + void codecChanged(int idx); + void sampleSizeChanged(int idx); + void sampleTypeChanged(int idx); + void endianChanged(int idx); + void test(); +}; + diff --git a/examples/multimedia/audio/audiodevices/audiodevices.pro b/examples/multimedia/audio/audiodevices/audiodevices.pro new file mode 100644 index 0000000..adc4890 --- /dev/null +++ b/examples/multimedia/audio/audiodevices/audiodevices.pro @@ -0,0 +1,12 @@ +HEADERS = audiodevices.h +SOURCES = audiodevices.cpp \ + main.cpp +FORMS += audiodevicesbase.ui + +QT += multimedia + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audiodevices +sources.files = $$SOURCES *.h $$RESOURCES $$FORMS audiodevices.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audiodevices +INSTALLS += target sources diff --git a/examples/multimedia/audio/audiodevices/audiodevicesbase.ui b/examples/multimedia/audio/audiodevices/audiodevicesbase.ui new file mode 100644 index 0000000..674f201 --- /dev/null +++ b/examples/multimedia/audio/audiodevices/audiodevicesbase.ui @@ -0,0 +1,255 @@ +<ui version="4.0" > + <class>AudioDevicesBase</class> + <widget class="QMainWindow" name="AudioDevicesBase" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>504</width> + <height>702</height> + </rect> + </property> + <property name="windowTitle" > + <string>AudioDevicesBase</string> + </property> + <widget class="QWidget" name="centralwidget" > + <property name="geometry" > + <rect> + <x>0</x> + <y>28</y> + <width>504</width> + <height>653</height> + </rect> + </property> + <widget class="QWidget" name="layoutWidget" > + <property name="geometry" > + <rect> + <x>40</x> + <y>21</y> + <width>321</width> + <height>506</height> + </rect> + </property> + <layout class="QGridLayout" name="gridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="deviceLabel" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Preferred" > + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Device</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="modeLabel" > + <property name="text" > + <string>Mode</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QComboBox" name="deviceBox" /> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="modeBox" /> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="actualLabel" > + <property name="frameShape" > + <enum>QFrame::Panel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <property name="text" > + <string>Actual Settings</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLabel" name="nearestLabel" > + <property name="frameShape" > + <enum>QFrame::Panel</enum> + </property> + <property name="frameShadow" > + <enum>QFrame::Raised</enum> + </property> + <property name="text" > + <string>Nearest Settings</string> + </property> + <property name="alignment" > + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="actualFreqLabel" > + <property name="text" > + <string>Frequency</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLabel" name="nearestFreqLabel" > + <property name="text" > + <string>Frequency</string> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QComboBox" name="frequencyBox" /> + </item> + <item row="4" column="1" > + <widget class="QLineEdit" name="nearestFreq" /> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="actualChannelsLabel" > + <property name="text" > + <string>Channels</string> + </property> + </widget> + </item> + <item row="5" column="1" > + <widget class="QLabel" name="nearestChannelLabel" > + <property name="text" > + <string>Channel</string> + </property> + </widget> + </item> + <item row="6" column="0" > + <widget class="QComboBox" name="channelsBox" /> + </item> + <item row="6" column="1" > + <widget class="QLineEdit" name="nearestChannel" /> + </item> + <item row="7" column="0" > + <widget class="QLabel" name="actualCodecLabel" > + <property name="text" > + <string>Codecs</string> + </property> + </widget> + </item> + <item row="7" column="1" > + <widget class="QLabel" name="nearestCodecLabel" > + <property name="text" > + <string>Codec</string> + </property> + </widget> + </item> + <item row="8" column="0" > + <widget class="QComboBox" name="codecsBox" /> + </item> + <item row="8" column="1" > + <widget class="QLineEdit" name="nearestCodec" /> + </item> + <item row="9" column="0" > + <widget class="QLabel" name="actualSampleSizeLabel" > + <property name="text" > + <string>SampleSize</string> + </property> + </widget> + </item> + <item row="9" column="1" > + <widget class="QLabel" name="nearestSampleSizeLabel" > + <property name="text" > + <string>SampleSize</string> + </property> + </widget> + </item> + <item row="10" column="0" > + <widget class="QComboBox" name="sampleSizesBox" /> + </item> + <item row="10" column="1" > + <widget class="QLineEdit" name="nearestSampleSize" /> + </item> + <item row="11" column="0" > + <widget class="QLabel" name="actualSampleTypeLabel" > + <property name="text" > + <string>SampleType</string> + </property> + </widget> + </item> + <item row="11" column="1" > + <widget class="QLabel" name="nearestSampleTypeLabel" > + <property name="text" > + <string>SampleType</string> + </property> + </widget> + </item> + <item row="12" column="0" > + <widget class="QComboBox" name="sampleTypesBox" /> + </item> + <item row="12" column="1" > + <widget class="QLineEdit" name="nearestSampleType" /> + </item> + <item row="13" column="0" > + <widget class="QLabel" name="actualEndianLabel" > + <property name="text" > + <string>Endianess</string> + </property> + </widget> + </item> + <item row="13" column="1" > + <widget class="QLabel" name="nearestEndianLabel" > + <property name="text" > + <string>Endianess</string> + </property> + </widget> + </item> + <item row="14" column="0" > + <widget class="QComboBox" name="endianBox" /> + </item> + <item row="14" column="1" > + <widget class="QLineEdit" name="nearestEndian" /> + </item> + <item row="15" column="0" colspan="2" > + <widget class="QTextEdit" name="logOutput" > + <property name="minimumSize" > + <size> + <width>0</width> + <height>40</height> + </size> + </property> + </widget> + </item> + <item row="16" column="0" colspan="2" > + <widget class="QPushButton" name="testButton" > + <property name="text" > + <string>Test</string> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <widget class="QMenuBar" name="menubar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>504</width> + <height>28</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>681</y> + <width>504</width> + <height>21</height> + </rect> + </property> + </widget> + </widget> + <resources/> + <connections/> +</ui> diff --git a/doc/src/hierarchy.qdoc b/examples/multimedia/audio/audiodevices/main.cpp index 4a278dc..12e413e 100644 --- a/doc/src/hierarchy.qdoc +++ b/examples/multimedia/audio/audiodevices/main.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the documentation of the Qt Toolkit. +** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,14 +39,17 @@ ** ****************************************************************************/ -/*! - \page hierarchy.html +#include <QtGui> - \title Class Inheritance Hierarchy - \ingroup classlists +#include "audiodevices.h" - This list shows the C++ class inheritance relations between the - classes in the Qt API. +int main(int argv, char **args) +{ + QApplication app(argv, args); + app.setApplicationName("Audio Device Test"); - \generatelist classhierarchy -*/ + AudioTest audio; + audio.show(); + + return app.exec(); +} diff --git a/examples/multimedia/audio/audioinput/audioinput.cpp b/examples/multimedia/audio/audioinput/audioinput.cpp new file mode 100644 index 0000000..ae7d84c --- /dev/null +++ b/examples/multimedia/audio/audioinput/audioinput.cpp @@ -0,0 +1,376 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <stdlib.h> +#include <math.h> + +#include <QDebug> +#include <QPainter> +#include <QVBoxLayout> + +#include <QAudioDeviceInfo> +#include <QAudioInput> +#include "audioinput.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +Spectrum::Spectrum(QObject* parent, QAudioInput* device, float* out) + :QIODevice( parent ) +{ + input = device; + output = out; + + unsigned int i; + + // Allocate sample buffer and initialize sin and cos lookup tables + fftState = (fft_state *) malloc (sizeof(fft_state)); + + for(i = 0; i < BUFFER_SIZE; i++) { + bitReverse[i] = reverseBits(i); + } + for(i = 0; i < BUFFER_SIZE / 2; i++) { + float j = 2 * M_PI * i / BUFFER_SIZE; + costable[i] = cos(j); + sintable[i] = sin(j); + } +} + +Spectrum::~Spectrum() +{ +} + +void Spectrum::start() +{ + open(QIODevice::WriteOnly); +} + +void Spectrum::stop() +{ + close(); +} + +qint64 Spectrum::readData(char *data, qint64 maxlen) +{ + Q_UNUSED(data) + Q_UNUSED(maxlen) + + return 0; +} + +qint64 Spectrum::writeData(const char *data, qint64 len) +{ + performFFT((sound_sample*)data); + emit update(); + + return len; +} + +int Spectrum::reverseBits(unsigned int initial) { + // BIT-REVERSE-COPY(a,A) + + unsigned int reversed = 0, loop; + for(loop = 0; loop < BUFFER_SIZE_LOG; loop++) { + reversed <<= 1; + reversed += (initial & 1); + initial >>= 1; + } + return reversed; +} + +void Spectrum::performFFT(const sound_sample *input) { + /* Convert to reverse bit order for FFT */ + prepFFT(input, fftState->real, fftState->imag); + + /* Calculate FFT */ + calcFFT(fftState->real, fftState->imag); + + /* Convert FFT to intensities */ + outputFFT(fftState->real, fftState->imag); +} + +void Spectrum::prepFFT(const sound_sample *input, float * re, float * im) { + unsigned int i; + float *realptr = re; + float *imagptr = im; + + /* Get input, in reverse bit order */ + for(i = 0; i < BUFFER_SIZE; i++) { + *realptr++ = input[bitReverse[i]]; + *imagptr++ = 0; + } +} + +void Spectrum::calcFFT(float * re, float * im) { + unsigned int i, j, k; + unsigned int exchanges; + float fact_real, fact_imag; + float tmp_real, tmp_imag; + unsigned int factfact; + + /* Set up some variables to reduce calculation in the loops */ + exchanges = 1; + factfact = BUFFER_SIZE / 2; + + /* divide and conquer method */ + for(i = BUFFER_SIZE_LOG; i != 0; i--) { + for(j = 0; j != exchanges; j++) { + fact_real = costable[j * factfact]; + fact_imag = sintable[j * factfact]; + for(k = j; k < BUFFER_SIZE; k += exchanges << 1) { + int k1 = k + exchanges; + tmp_real = fact_real * re[k1] - fact_imag * im[k1]; + tmp_imag = fact_real * im[k1] + fact_imag * re[k1]; + re[k1] = re[k] - tmp_real; + im[k1] = im[k] - tmp_imag; + re[k] += tmp_real; + im[k] += tmp_imag; + } + } + exchanges <<= 1; + factfact >>= 1; + } +} + +void Spectrum::outputFFT(const float * re, const float * im) { + const float *realptr = re; + const float *imagptr = im; + float *outputptr = output; + + float *endptr = output + BUFFER_SIZE / 2; + + /* Convert FFT to intensities */ + + while(outputptr <= endptr) { + *outputptr = (*realptr * *realptr) + (*imagptr * *imagptr); + outputptr++; realptr++; imagptr++; + } + *output /= 4; + *endptr /= 4; +} + + +RenderArea::RenderArea(QWidget *parent) + : QWidget(parent) +{ + setBackgroundRole(QPalette::Base); + setAutoFillBackground(true); + + samples = 0; + sampleSize = 0; + setMinimumHeight(30); + setMinimumWidth(200); +} + +void RenderArea::paintEvent(QPaintEvent * /* event */) +{ + QPainter painter(this); + + if(sampleSize == 0) + return; + + painter.setPen(Qt::red); + int max = 0; + for(int i=0;i<sampleSize;i++) { + int m = (int)(sqrt(samples[i])/32768); + if(m > max) + max = m; + } + int x1,y1,x2,y2; + + for(int i=0;i<10;i++) { + x1 = painter.viewport().left()+11; + y1 = painter.viewport().top()+10+i; + x2 = painter.viewport().right()-20-max; + y2 = painter.viewport().top()+10+i; + if(x2 < painter.viewport().left()+10) + x2 = painter.viewport().left()+10; + + painter.drawLine(QPoint(x1,y1),QPoint(x2,y2)); + } + + painter.setPen(Qt::black); + painter.drawRect(QRect(painter.viewport().left()+10, painter.viewport().top()+10, + painter.viewport().right()-20, painter.viewport().bottom()-20)); +} + +void RenderArea::spectrum(float* output, int size) +{ + samples = output; + sampleSize = size; + repaint(); +} + + +InputTest::InputTest() +{ + QWidget *window = new QWidget; + QVBoxLayout* layout = new QVBoxLayout; + + canvas = new RenderArea; + layout->addWidget(canvas); + + deviceBox = new QComboBox(this); + QList<QAudioDeviceId> devices = QAudioDeviceInfo::deviceList(QAudio::AudioInput); + for(int i = 0; i < devices.size(); ++i) { + deviceBox->addItem(QAudioDeviceInfo(devices.at(i)).deviceName(), qVariantFromValue(devices.at(i))); + } + connect(deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int))); + layout->addWidget(deviceBox); + + button = new QPushButton(this); + button->setText(tr("Click for Push Mode")); + connect(button,SIGNAL(clicked()),SLOT(toggleMode())); + layout->addWidget(button); + + button2 = new QPushButton(this); + button2->setText(tr("Click To Suspend")); + connect(button2,SIGNAL(clicked()),SLOT(toggleSuspend())); + layout->addWidget(button2); + + window->setLayout(layout); + setCentralWidget(window); + window->show(); + + buffer = new char[BUFFER_SIZE*10]; + output = new float[1024]; + + pullMode = true; + + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setSampleType(QAudioFormat::UnSignedInt); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setCodec("audio/pcm"); + + audioInput = new QAudioInput(format,this); + connect(audioInput,SIGNAL(notify()),SLOT(status())); + connect(audioInput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); + spec = new Spectrum(this,audioInput,output); + connect(spec,SIGNAL(update()),SLOT(refreshDisplay())); + spec->start(); + audioInput->start(spec); +} + +InputTest::~InputTest() {} + +void InputTest::status() +{ + qWarning()<<"bytesReady = "<<audioInput->bytesReady()<<" bytes, clock = "<<audioInput->clock()<<"ms, totalTime = "<<audioInput->totalTime()/1000<<"ms"; +} + +void InputTest::readMore() +{ + if(!audioInput) + return; + qint64 len = audioInput->bytesReady(); + if(len > BUFFER_SIZE*10) + len = BUFFER_SIZE*10; + qint64 l = input->read(buffer,len); + if(l > 0) { + spec->write(buffer,l); + } +} + +void InputTest::toggleMode() +{ + // Change bewteen pull and push modes + audioInput->stop(); + + if(pullMode) { + button->setText(tr("Click for Push Mode")); + input = audioInput->start(0); + connect(input,SIGNAL(readyRead()),SLOT(readMore())); + pullMode = false; + } else { + button->setText(tr("Click for Pull Mode")); + pullMode = true; + audioInput->start(spec); + } +} + +void InputTest::toggleSuspend() +{ + // toggle suspend/resume + if(audioInput->state() == QAudio::SuspendState) { + qWarning()<<"status: Suspended, resume()"; + audioInput->resume(); + button2->setText("Click To Suspend"); + } else if (audioInput->state() == QAudio::ActiveState) { + qWarning()<<"status: Active, suspend()"; + audioInput->suspend(); + button2->setText("Click To Resume"); + } else if (audioInput->state() == QAudio::StopState) { + qWarning()<<"status: Stopped, resume()"; + audioInput->resume(); + button2->setText("Click To Suspend"); + } else if (audioInput->state() == QAudio::IdleState) { + qWarning()<<"status: IdleState"; + } +} + +void InputTest::state(QAudio::State state) +{ + qWarning()<<" state="<<state; +} + +void InputTest::refreshDisplay() +{ + canvas->spectrum(output,256); + canvas->repaint(); +} + +void InputTest::deviceChanged(int idx) +{ + spec->stop(); + audioInput->stop(); + audioInput->disconnect(this); + delete audioInput; + + device = deviceBox->itemData(idx).value<QAudioDeviceId>(); + audioInput = new QAudioInput(device, format, this); + connect(audioInput,SIGNAL(notify()),SLOT(status())); + connect(audioInput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); + spec->start(); + audioInput->start(spec); +} diff --git a/examples/multimedia/audio/audioinput/audioinput.h b/examples/multimedia/audio/audioinput/audioinput.h new file mode 100644 index 0000000..3a6b356 --- /dev/null +++ b/examples/multimedia/audio/audioinput/audioinput.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QPixmap> +#include <QWidget> +#include <QObject> +#include <QMainWindow> +#include <QPushButton> +#include <QComboBox> + +#include <qaudioinput.h> + +#define BUFFER_SIZE_LOG 9 +#define BUFFER_SIZE (1 << BUFFER_SIZE_LOG) + +struct _struct_fft_state { + float real[BUFFER_SIZE]; + float imag[BUFFER_SIZE]; +}; +typedef _struct_fft_state fft_state; +typedef short int sound_sample; + +class Spectrum : public QIODevice +{ + Q_OBJECT +public: + Spectrum(QObject* parent, QAudioInput* device, float* out); + ~Spectrum(); + + void start(); + void stop(); + + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + + QAudioInput* input; + float* output; + fft_state* fftState; + + unsigned int bitReverse[BUFFER_SIZE]; + float sintable[BUFFER_SIZE / 2]; + float costable[BUFFER_SIZE / 2]; + + void prepFFT (const sound_sample *input, float *re, float *im); + void calcFFT (float *re, float *im); + void outputFFT (const float *re, const float *im); + int reverseBits (unsigned int initial); + void performFFT (const sound_sample *input); + +signals: + void update(); +}; + + +class RenderArea : public QWidget +{ + Q_OBJECT + +public: + RenderArea(QWidget *parent = 0); + + void spectrum(float* output, int size); + +protected: + void paintEvent(QPaintEvent *event); + +private: + QPixmap pixmap; + + float* samples; + int sampleSize; +}; + +class InputTest : public QMainWindow +{ + Q_OBJECT +public: + InputTest(); + ~InputTest(); + + QAudioDeviceId device; + QAudioFormat format; + QAudioInput* audioInput; + Spectrum* spec; + QIODevice* input; + RenderArea* canvas; + + bool pullMode; + + QPushButton* button; + QPushButton* button2; + QComboBox* deviceBox; + + char* buffer; + float* output; + +private slots: + void refreshDisplay(); + void status(); + void readMore(); + void toggleMode(); + void toggleSuspend(); + void state(QAudio::State s); + void deviceChanged(int idx); +}; + diff --git a/examples/multimedia/audio/audioinput/audioinput.pro b/examples/multimedia/audio/audioinput/audioinput.pro new file mode 100644 index 0000000..d930750 --- /dev/null +++ b/examples/multimedia/audio/audioinput/audioinput.pro @@ -0,0 +1,12 @@ +HEADERS = audioinput.h +SOURCES = audioinput.cpp \ + main.cpp + +QT += multimedia + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audioinput +sources.files = $$SOURCES *.h $$RESOURCES $$FORMS audioinput.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audioinput +INSTALLS += target sources + diff --git a/doc/src/mainclasses.qdoc b/examples/multimedia/audio/audioinput/main.cpp index 33bb91a..64a9b04 100644 --- a/doc/src/mainclasses.qdoc +++ b/examples/multimedia/audio/audioinput/main.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the documentation of the Qt Toolkit. +** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,13 +39,17 @@ ** ****************************************************************************/ -/*! - \page mainclasses.html - \title Qt's Main Classes - \ingroup classlists +#include <QtGui> - These are the most frequently used Qt classes. For the complete - list see \link classes.html Qt's Classes \endlink. +#include "audioinput.h" - \generatelist mainclasses -*/ +int main(int argv, char **args) +{ + QApplication app(argv, args); + app.setApplicationName("Audio Input Test"); + + InputTest input; + input.show(); + + return app.exec(); +} diff --git a/examples/multimedia/audio/audiooutput/audiooutput.cpp b/examples/multimedia/audio/audiooutput/audiooutput.cpp new file mode 100644 index 0000000..be26f19 --- /dev/null +++ b/examples/multimedia/audio/audiooutput/audiooutput.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QDebug> +#include <QVBoxLayout> + +#include <QAudioOutput> +#include <QAudioDeviceInfo> +#include "audiooutput.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +Generator::Generator(QObject *parent) + :QIODevice( parent ) +{ + finished = false; + buffer = new char[SECONDS*44100*4+1000]; + t=buffer; + len=fillData(t+4,450,SECONDS); /* left channel, 450Hz sine */ + len+=fillData(t+6,452,SECONDS); /* right channel, 452Hz sine */ + putLong(t,len); + putLong(buffer+4,len+8+16+8); + pos = 0; + total = len+8+16+8; +} + +Generator::~Generator() +{ + delete [] buffer; +} + +void Generator::start() +{ + open(QIODevice::ReadOnly); +} + +void Generator::stop() +{ + close(); +} + +int Generator::putShort(char *t, unsigned int value) +{ + *(unsigned char *)(t++)=value&255; + *(unsigned char *)(t)=(value/256)&255; + return 2; +} + +int Generator::putLong(char *t, unsigned int value) +{ + *(unsigned char *)(t++)=value&255; + *(unsigned char *)(t++)=(value/256)&255; + *(unsigned char *)(t++)=(value/(256*256))&255; + *(unsigned char *)(t)=(value/(256*256*256))&255; + return 4; +} + +int Generator::fillData(char *start, int frequency, int seconds) +{ + int i, len=0; + int value; + for(i=0; i<seconds*44100; i++) { + value=(int)(32767.0*sin(2.0*M_PI*((double)(i))*(double)(frequency)/44100.0)); + putShort(start, value); + start += 4; + len+=2; + } + return len; +} + +qint64 Generator::readData(char *data, qint64 maxlen) +{ + int len = maxlen; + if(len > 16384) + len = 16384; + + if(len < (SECONDS*44100*4+1000)-pos) { + // Normal + memcpy(data,t+pos,len); + pos+=len; + return len; + } else { + // Whats left and reset to start + qint64 left = (SECONDS*44100*4+1000)-pos; + memcpy(data,t+pos,left); + pos=0; + return left; + } +} + +qint64 Generator::writeData(const char *data, qint64 len) +{ + Q_UNUSED(data); + Q_UNUSED(len); + + return 0; +} + +AudioTest::AudioTest() +{ + QWidget *window = new QWidget; + QVBoxLayout* layout = new QVBoxLayout; + + deviceBox = new QComboBox(this); + QList<QAudioDeviceId> devices = QAudioDeviceInfo::deviceList(QAudio::AudioOutput); + for(int i = 0; i < devices.size(); ++i) { + deviceBox->addItem(QAudioDeviceInfo(devices.at(i)).deviceName(), qVariantFromValue(devices.at(i))); + } + connect(deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int))); + layout->addWidget(deviceBox); + + button = new QPushButton(this); + button->setText(tr("Click for Push Mode")); + connect(button,SIGNAL(clicked()),SLOT(toggle())); + layout->addWidget(button); + + button2 = new QPushButton(this); + button2->setText(tr("Click To Suspend")); + connect(button2,SIGNAL(clicked()),SLOT(togglePlay())); + layout->addWidget(button2); + + window->setLayout(layout); + setCentralWidget(window); + window->show(); + + buffer = new char[BUFFER_SIZE]; + + gen = new Generator(this); + + pullMode = true; + + timer = new QTimer(this); + connect(timer,SIGNAL(timeout()),SLOT(writeMore())); + + gen->start(); + + settings.setFrequency(44100); + settings.setChannels(2); + settings.setSampleSize(16); + settings.setCodec("audio/pcm"); + settings.setByteOrder(QAudioFormat::LittleEndian); + settings.setSampleType(QAudioFormat::SignedInt); + audioOutput = new QAudioOutput(settings,this); + connect(audioOutput,SIGNAL(notify()),SLOT(status())); + connect(audioOutput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); + + audioOutput->start(gen); +} + +AudioTest::~AudioTest() +{ + delete [] buffer; +} + +void AudioTest::deviceChanged(int idx) +{ + timer->stop(); + gen->stop(); + audioOutput->stop(); + audioOutput->disconnect(this); + delete audioOutput; + + device = deviceBox->itemData(idx).value<QAudioDeviceId>(); + audioOutput = new QAudioOutput(device,settings,this); + connect(audioOutput,SIGNAL(notify()),SLOT(status())); + connect(audioOutput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State))); + gen->start(); + audioOutput->start(gen); +} + +void AudioTest::status() +{ + qWarning()<<"byteFree = "<<audioOutput->bytesFree()<<" bytes, clock = "<<audioOutput->clock()<<"ms, totalTime = "<<audioOutput->totalTime()/1000<<"ms"; +} + +void AudioTest::writeMore() +{ + if(!audioOutput) + return; + + if(audioOutput->state() == QAudio::StopState) + return; + + int l; + int out; + + int chunks = audioOutput->bytesFree()/audioOutput->periodSize(); + while(chunks) { + l = gen->read(buffer,audioOutput->periodSize()); + if(l > 0) + out = output->write(buffer,l); + if(l != audioOutput->periodSize()) + break; + chunks--; + } +} + +void AudioTest::toggle() +{ + // Change between pull and push modes + + timer->stop(); + audioOutput->stop(); + + if (pullMode) { + button->setText("Click for Pull Mode"); + output = audioOutput->start(0); + pullMode = false; + timer->start(20); + } else { + button->setText("Click for Push Mode"); + pullMode = true; + audioOutput->start(gen); + } +} + +void AudioTest::togglePlay() +{ + // toggle suspend/resume + if(audioOutput->state() == QAudio::SuspendState) { + qWarning()<<"status: Suspended, resume()"; + audioOutput->resume(); + button2->setText("Click To Suspend"); + } else if (audioOutput->state() == QAudio::ActiveState) { + qWarning()<<"status: Active, suspend()"; + audioOutput->suspend(); + button2->setText("Click To Resume"); + } else if (audioOutput->state() == QAudio::StopState) { + qWarning()<<"status: Stopped, resume()"; + audioOutput->resume(); + button2->setText("Click To Suspend"); + } else if (audioOutput->state() == QAudio::IdleState) { + qWarning()<<"status: IdleState"; + } +} + +void AudioTest::state(QAudio::State state) +{ + qWarning()<<" state="<<state; +} diff --git a/examples/multimedia/audio/audiooutput/audiooutput.h b/examples/multimedia/audio/audiooutput/audiooutput.h new file mode 100644 index 0000000..1e21b48 --- /dev/null +++ b/examples/multimedia/audio/audiooutput/audiooutput.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <math.h> + +#define BUFFER_SIZE 32768 +#define SECONDS 3 + +#include <QObject> +#include <QMainWindow> +#include <QIODevice> +#include <QTimer> +#include <QPushButton> +#include <QComboBox> + +#include <QAudioOutput> + +class Generator : public QIODevice +{ + Q_OBJECT +public: + Generator(QObject *parent); + ~Generator(); + + void start(); + void stop(); + + char *t; + int len; + int pos; + int total; + char *buffer; + bool finished; + int chunk_size; + + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + +private: + int putShort(char *t, unsigned int value); + int putLong(char *t, unsigned int value); + int fillData(char *start, int frequency, int seconds); +}; + +class AudioTest : public QMainWindow +{ + Q_OBJECT +public: + AudioTest(); + ~AudioTest(); + + QAudioDeviceId device; + Generator* gen; + QAudioOutput* audioOutput; + QIODevice* output; + QTimer* timer; + QAudioFormat settings; + + bool pullMode; + char* buffer; + + QPushButton* button; + QPushButton* button2; + QComboBox* deviceBox; + +private slots: + void status(); + void writeMore(); + void toggle(); + void togglePlay(); + void state(QAudio::State s); + void deviceChanged(int idx); +}; + diff --git a/examples/multimedia/audio/audiooutput/audiooutput.pro b/examples/multimedia/audio/audiooutput/audiooutput.pro new file mode 100644 index 0000000..08f43ce --- /dev/null +++ b/examples/multimedia/audio/audiooutput/audiooutput.pro @@ -0,0 +1,11 @@ +HEADERS = audiooutput.h +SOURCES = audiooutput.cpp \ + main.cpp + +QT += multimedia + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audiooutput +sources.files = $$SOURCES *.h $$RESOURCES $$FORMS audiooutput.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/multimedia/audio/audiooutput +INSTALLS += target sources diff --git a/doc/src/compatclasses.qdoc b/examples/multimedia/audio/audiooutput/main.cpp index cb6d7dd..fc319bd 100644 --- a/doc/src/compatclasses.qdoc +++ b/examples/multimedia/audio/audiooutput/main.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the documentation of the Qt Toolkit. +** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,16 +39,18 @@ ** ****************************************************************************/ -/*! - \page compatclasses.html - \title Qt 3 Compatibility Classes - \ingroup classlists - This is a list of the classes that Qt provides for compatibility - with Qt 3. The vast majority of these are provided by the - Qt3Support module. +#include <QtGui> - \generatelist compatclasses +#include "audiooutput.h" - \sa {Qt's Classes}, {Qt's Modules} -*/ +int main(int argv, char **args) +{ + QApplication app(argv, args); + app.setApplicationName("Audio Output Test"); + + AudioTest audio; + audio.show(); + + return app.exec(); +} diff --git a/examples/multimedia/multimedia.pro b/examples/multimedia/multimedia.pro new file mode 100644 index 0000000..ac78b15 --- /dev/null +++ b/examples/multimedia/multimedia.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs +SUBDIRS = audio + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/multimedia +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS multimedia.pro README +sources.path = $$[QT_INSTALL_EXAMPLES]/multimedia +INSTALLS += target sources diff --git a/examples/phonon/musicplayer/mainwindow.cpp b/examples/phonon/musicplayer/mainwindow.cpp index af2b637..a587b07 100644 --- a/examples/phonon/musicplayer/mainwindow.cpp +++ b/examples/phonon/musicplayer/mainwindow.cpp @@ -157,9 +157,12 @@ void MainWindow::tableClicked(int row, int /* column */) mediaObject->stop(); mediaObject->clearQueue(); + if (row >= sources.size()) + return; + mediaObject->setCurrentSource(sources[row]); - if (wasPlaying) + if (wasPlaying) mediaObject->play(); else mediaObject->stop(); diff --git a/examples/script/customclass/bytearrayclass.cpp b/examples/script/customclass/bytearrayclass.cpp index c3e7480..8fe1a96 100644 --- a/examples/script/customclass/bytearrayclass.cpp +++ b/examples/script/customclass/bytearrayclass.cpp @@ -100,7 +100,7 @@ ByteArrayClass::ByteArrayClass(QScriptEngine *engine) QScriptValue global = engine->globalObject(); proto.setPrototype(global.property("Object").property("prototype")); - ctor = engine->newFunction(construct); + ctor = engine->newFunction(construct, proto); ctor.setData(qScriptValueFromValue(engine, this)); } //! [0] @@ -224,7 +224,10 @@ QScriptValue ByteArrayClass::construct(QScriptContext *ctx, QScriptEngine *) ByteArrayClass *cls = qscriptvalue_cast<ByteArrayClass*>(ctx->callee().data()); if (!cls) return QScriptValue(); - int size = ctx->argument(0).toInt32(); + QScriptValue arg = ctx->argument(0); + if (arg.instanceOf(ctx->callee())) + return cls->newInstance(qscriptvalue_cast<QByteArray>(arg)); + int size = arg.toInt32(); return cls->newInstance(size); } //! [2] @@ -240,7 +243,7 @@ QScriptValue ByteArrayClass::toScriptValue(QScriptEngine *eng, const QByteArray void ByteArrayClass::fromScriptValue(const QScriptValue &obj, QByteArray &ba) { - ba = qscriptvalue_cast<QByteArray>(obj.data()); + ba = qvariant_cast<QByteArray>(obj.data().toVariant()); } diff --git a/examples/script/customclass/main.cpp b/examples/script/customclass/main.cpp index 05aefd5..3b98f5c 100644 --- a/examples/script/customclass/main.cpp +++ b/examples/script/customclass/main.cpp @@ -52,6 +52,7 @@ int main(int argc, char **argv) eng.globalObject().setProperty("ByteArray", baClass->constructor()); qDebug() << "ba = new ByteArray(4):" << eng.evaluate("ba = new ByteArray(4)").toString(); + qDebug() << "ba instanceof ByteArray:" << eng.evaluate("ba instanceof ByteArray").toBool(); qDebug() << "ba.length:" << eng.evaluate("ba.length").toNumber(); qDebug() << "ba[1] = 123; ba[1]:" << eng.evaluate("ba[1] = 123; ba[1]").toNumber(); qDebug() << "ba[7] = 224; ba.length:" << eng.evaluate("ba[7] = 224; ba.length").toNumber(); @@ -65,6 +66,9 @@ int main(int argc, char **argv) qDebug() << "ba.toBase64().toLatin1String():" << eng.evaluate("b64.toLatin1String()").toString(); qDebug() << "ba.valueOf():" << eng.evaluate("ba.valueOf()").toString(); qDebug() << "ba.chop(2); ba.length:" << eng.evaluate("ba.chop(2); ba.length").toNumber(); + qDebug() << "ba2 = new ByteArray(ba):" << eng.evaluate("ba2 = new ByteArray(ba)").toString(); + qDebug() << "ba2.equals(ba):" << eng.evaluate("ba2.equals(ba)").toBool(); + qDebug() << "ba2.equals(new ByteArray()):" << eng.evaluate("ba2.equals(new ByteArray())").toBool(); return 0; } diff --git a/mkspecs/features/qt.prf b/mkspecs/features/qt.prf index 1bac953..a03a313 100644 --- a/mkspecs/features/qt.prf +++ b/mkspecs/features/qt.prf @@ -36,7 +36,7 @@ INCLUDEPATH = $$QMAKE_INCDIR_QT $$INCLUDEPATH #prepending prevents us from picki win32:INCLUDEPATH += $$QMAKE_INCDIR_QT/ActiveQt # As order does matter for static libs, we reorder the QT variable here -TMPLIBS = webkit phonon dbus testlib script scripttools svg qt3support sql xmlpatterns xml egl opengl openvg gui network core +TMPLIBS = multimedia webkit phonon dbus testlib script scripttools svg qt3support sql xmlpatterns xml egl opengl openvg gui network core for(QTLIB, $$list($$TMPLIBS)) { contains(QT, $$QTLIB): QT_ORDERED += $$QTLIB } @@ -152,6 +152,7 @@ for(QTLIB, $$list($$lower($$unique(QT)))) { else:isEqual(QTLIB, dbus):qlib = QtDBus else:isEqual(QTLIB, phonon):qlib = phonon else:isEqual(QTLIB, webkit):qlib = QtWebKit + else:isEqual(QTLIB, multimedia):qlib = QtMultimedia else:message("Unknown QT: $$QTLIB"):qlib = !isEmpty(qlib) { target_qt:isEqual(TARGET, qlib) { diff --git a/mkspecs/unsupported/vxworks-ppc-dcc/qplatformdefs.h b/mkspecs/unsupported/vxworks-ppc-dcc/qplatformdefs.h index 4b0c24c..a1735d0 100644 --- a/mkspecs/unsupported/vxworks-ppc-dcc/qplatformdefs.h +++ b/mkspecs/unsupported/vxworks-ppc-dcc/qplatformdefs.h @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the qmake spec of the Qt Toolkit. ** @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/mkspecs/unsupported/vxworks-ppc-g++/qplatformdefs.h b/mkspecs/unsupported/vxworks-ppc-g++/qplatformdefs.h index 4b0c24c..a1735d0 100644 --- a/mkspecs/unsupported/vxworks-ppc-g++/qplatformdefs.h +++ b/mkspecs/unsupported/vxworks-ppc-g++/qplatformdefs.h @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the qmake spec of the Qt Toolkit. ** @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/mkspecs/unsupported/vxworks-simpentium-dcc/qplatformdefs.h b/mkspecs/unsupported/vxworks-simpentium-dcc/qplatformdefs.h index 4b0c24c..a1735d0 100644 --- a/mkspecs/unsupported/vxworks-simpentium-dcc/qplatformdefs.h +++ b/mkspecs/unsupported/vxworks-simpentium-dcc/qplatformdefs.h @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the qmake spec of the Qt Toolkit. ** @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/mkspecs/unsupported/vxworks-simpentium-g++/qplatformdefs.h b/mkspecs/unsupported/vxworks-simpentium-g++/qplatformdefs.h index 90eb955..b3eefea 100644 --- a/mkspecs/unsupported/vxworks-simpentium-g++/qplatformdefs.h +++ b/mkspecs/unsupported/vxworks-simpentium-g++/qplatformdefs.h @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the qmake spec of the Qt Toolkit. ** @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/3rdparty/phonon/ds9/abstractvideorenderer.cpp b/src/3rdparty/phonon/ds9/abstractvideorenderer.cpp index e932e70..a9d0694 100644 --- a/src/3rdparty/phonon/ds9/abstractvideorenderer.cpp +++ b/src/3rdparty/phonon/ds9/abstractvideorenderer.cpp @@ -99,8 +99,8 @@ namespace Phonon m_dstX = m_dstY = 0; if (ratio > 0) { - if (realWidth / realHeight > ratio && scaleMode == Phonon::VideoWidget::FitInView - || realWidth / realHeight < ratio && scaleMode == Phonon::VideoWidget::ScaleAndCrop) { + if ((realWidth / realHeight > ratio && scaleMode == Phonon::VideoWidget::FitInView) + || (realWidth / realHeight < ratio && scaleMode == Phonon::VideoWidget::ScaleAndCrop)) { //the height is correct, let's change the width m_dstWidth = qRound(realHeight * ratio); m_dstX = qRound((realWidth - realHeight * ratio) / 2.); diff --git a/src/3rdparty/phonon/ds9/fakesource.cpp b/src/3rdparty/phonon/ds9/fakesource.cpp index a4d4640..4dce138 100644 --- a/src/3rdparty/phonon/ds9/fakesource.cpp +++ b/src/3rdparty/phonon/ds9/fakesource.cpp @@ -29,7 +29,7 @@ namespace Phonon namespace DS9 { static WAVEFORMATEX g_defaultWaveFormat = {WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0}; - static VIDEOINFOHEADER2 g_defaultVideoInfo = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, {sizeof(BITMAPINFOHEADER), 1, 1, 1, 0, 0, 0, 0, 0, 0, 0} }; + static VIDEOINFOHEADER2 g_defaultVideoInfo = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {sizeof(BITMAPINFOHEADER), 1, 1, 1, 0, 0, 0, 0, 0, 0, 0} }; static const AM_MEDIA_TYPE g_fakeAudioType = {MEDIATYPE_Audio, MEDIASUBTYPE_PCM, 0, 0, 2, FORMAT_WaveFormatEx, 0, sizeof(WAVEFORMATEX), reinterpret_cast<BYTE*>(&g_defaultWaveFormat)}; static const AM_MEDIA_TYPE g_fakeVideoType = {MEDIATYPE_Video, MEDIASUBTYPE_RGB32, TRUE, FALSE, 0, FORMAT_VideoInfo2, 0, sizeof(VIDEOINFOHEADER2), reinterpret_cast<BYTE*>(&g_defaultVideoInfo)}; diff --git a/src/3rdparty/phonon/ds9/iodevicereader.cpp b/src/3rdparty/phonon/ds9/iodevicereader.cpp index 38c983b..a885a69 100644 --- a/src/3rdparty/phonon/ds9/iodevicereader.cpp +++ b/src/3rdparty/phonon/ds9/iodevicereader.cpp @@ -167,7 +167,7 @@ namespace Phonon oldSize = currentBufferSize(); } - DWORD bytesRead = qMin(currentBufferSize(), int(length)); + int bytesRead = qMin(currentBufferSize(), int(length)); { QWriteLocker locker(&m_lock); qMemCopy(buffer, m_buffer.data(), bytesRead); diff --git a/src/3rdparty/phonon/ds9/mediagraph.cpp b/src/3rdparty/phonon/ds9/mediagraph.cpp index 31a0622..7b10176 100644 --- a/src/3rdparty/phonon/ds9/mediagraph.cpp +++ b/src/3rdparty/phonon/ds9/mediagraph.cpp @@ -68,6 +68,8 @@ namespace Phonon return ret; } + +/* static HRESULT saveToFile(Graph graph, const QString &filepath) { const WCHAR wszStreamName[] = L"ActiveMovieGraph"; @@ -103,7 +105,7 @@ namespace Phonon return hr; } - +*/ MediaGraph::MediaGraph(MediaObject *mo, short index) : m_graph(CLSID_FilterGraph, IID_IGraphBuilder), @@ -537,7 +539,7 @@ namespace Phonon const QList<OutputPin> outputs = BackendNode::pins(filter, PINDIR_OUTPUT); for(int i = 0; i < outputs.count(); ++i) { const OutputPin &pin = outputs.at(i); - if (VFW_E_NOT_CONNECTED == pin->ConnectedTo(inPin.pparam())) { + if (HRESULT(VFW_E_NOT_CONNECTED) == pin->ConnectedTo(inPin.pparam())) { return SUCCEEDED(pin->Connect(newIn, 0)); } } @@ -809,7 +811,7 @@ namespace Phonon for (int i = 0; i < outputs.count(); ++i) { const OutputPin &out = outputs.at(i); InputPin pin; - if (out->ConnectedTo(pin.pparam()) == VFW_E_NOT_CONNECTED) { + if (out->ConnectedTo(pin.pparam()) == HRESULT(VFW_E_NOT_CONNECTED)) { m_decoderPins += out; //unconnected outputs can be decoded outputs } } diff --git a/src/3rdparty/phonon/ds9/mediaobject.cpp b/src/3rdparty/phonon/ds9/mediaobject.cpp index df42011..10782c2 100644 --- a/src/3rdparty/phonon/ds9/mediaobject.cpp +++ b/src/3rdparty/phonon/ds9/mediaobject.cpp @@ -786,15 +786,16 @@ namespace Phonon case Phonon::PausedState: pause(); break; - case Phonon::StoppedState: - stop(); - break; case Phonon::PlayingState: play(); break; case Phonon::ErrorState: setState(Phonon::ErrorState); break; + case Phonon::StoppedState: + default: + stop(); + break; } } } diff --git a/src/3rdparty/phonon/ds9/qbasefilter.cpp b/src/3rdparty/phonon/ds9/qbasefilter.cpp index 95cab92..c950c41 100644 --- a/src/3rdparty/phonon/ds9/qbasefilter.cpp +++ b/src/3rdparty/phonon/ds9/qbasefilter.cpp @@ -92,8 +92,8 @@ namespace Phonon return E_POINTER; } - int nbfetched = 0; - while (nbfetched < int(count) && m_index < m_pins.count()) { + uint nbfetched = 0; + while (nbfetched < count && m_index < m_pins.count()) { IPin *current = m_pins[m_index]; current->AddRef(); ret[nbfetched] = current; @@ -211,7 +211,8 @@ namespace Phonon } else if (iid == IID_IMediaPosition || iid == IID_IMediaSeeking) { if (inputPins().isEmpty()) { - if (*out = getUpStreamInterface(iid)) { + *out = getUpStreamInterface(iid); + if (*out) { return S_OK; //we return here to avoid adding a reference } else { hr = E_NOINTERFACE; diff --git a/src/3rdparty/phonon/ds9/qmeminputpin.cpp b/src/3rdparty/phonon/ds9/qmeminputpin.cpp index dca99db..865b8af 100644 --- a/src/3rdparty/phonon/ds9/qmeminputpin.cpp +++ b/src/3rdparty/phonon/ds9/qmeminputpin.cpp @@ -137,7 +137,8 @@ namespace Phonon return E_POINTER; } - if (*alloc = memoryAllocator(true)) { + *alloc = memoryAllocator(true); + if (*alloc) { return S_OK; } @@ -294,7 +295,7 @@ namespace Phonon LONG length = sample->GetActualDataLength(); HRESULT hr = alloc->Commit(); - if (hr == VFW_E_SIZENOTSET) { + if (hr == HRESULT(VFW_E_SIZENOTSET)) { ALLOCATOR_PROPERTIES prop = getDefaultAllocatorProperties(); prop.cbBuffer = qMax(prop.cbBuffer, length); ALLOCATOR_PROPERTIES actual; @@ -324,7 +325,7 @@ namespace Phonon { LONGLONG start, end; hr = sample->GetMediaTime(&start, &end); - if (hr != VFW_E_MEDIA_TIME_NOT_SET) { + if (hr != HRESULT(VFW_E_MEDIA_TIME_NOT_SET)) { hr = out->SetMediaTime(&start, &end); Q_ASSERT(SUCCEEDED(hr)); } diff --git a/src/3rdparty/phonon/ds9/qpin.cpp b/src/3rdparty/phonon/ds9/qpin.cpp index 68a4ec0..d14876b 100644 --- a/src/3rdparty/phonon/ds9/qpin.cpp +++ b/src/3rdparty/phonon/ds9/qpin.cpp @@ -91,8 +91,8 @@ namespace Phonon return E_INVALIDARG; } - int nbFetched = 0; - while (nbFetched < int(count) && m_index < m_pin->mediaTypes().count()) { + uint nbFetched = 0; + while (nbFetched < count && m_index < m_pin->mediaTypes().count()) { //the caller will deallocate the memory *out = static_cast<AM_MEDIA_TYPE *>(::CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))); const AM_MEDIA_TYPE original = m_pin->mediaTypes().at(m_index); @@ -145,9 +145,9 @@ namespace Phonon QPin::QPin(QBaseFilter *parent, PIN_DIRECTION dir, const QVector<AM_MEDIA_TYPE> &mt) : - m_memAlloc(0), m_parent(parent), m_refCount(1), m_connected(0), + m_parent(parent), m_flushing(false), m_refCount(1), m_connected(0), m_direction(dir), m_mediaTypes(mt), m_connectedType(defaultMediaType), - m_flushing(false) + m_memAlloc(0) { Q_ASSERT(m_parent); m_parent->addPin(this); diff --git a/src/3rdparty/phonon/ds9/videorenderer_soft.cpp b/src/3rdparty/phonon/ds9/videorenderer_soft.cpp index 2112267..cf5ce96 100644 --- a/src/3rdparty/phonon/ds9/videorenderer_soft.cpp +++ b/src/3rdparty/phonon/ds9/videorenderer_soft.cpp @@ -194,8 +194,8 @@ namespace Phonon m_sampleBuffer = ComPointer<IMediaSample>(); #ifndef QT_NO_OPENGL freeGLResources(); -#endif // QT_NO_OPENGL m_textureUploaded = false; +#endif // QT_NO_OPENGL } void endOfStream() @@ -314,7 +314,6 @@ namespace Phonon REFERENCE_TIME m_start; HANDLE m_renderEvent, m_receiveCanWait; // Signals sample to render QSize m_size; - bool m_textureUploaded; //mixer settings qreal m_brightness, @@ -356,6 +355,7 @@ namespace Phonon bool m_checkedPrograms; bool m_usingOpenGL; + bool m_textureUploaded; GLuint m_program[2]; GLuint m_texture[3]; #endif @@ -436,7 +436,7 @@ namespace Phonon QBaseFilter(CLSID_NULL), m_inputPin(new VideoRendererSoftPin(this)), m_renderer(renderer), m_start(0) #ifndef QT_NO_OPENGL - ,m_usingOpenGL(false), m_checkedPrograms(false), m_textureUploaded(false) + , m_checkedPrograms(false), m_usingOpenGL(false), m_textureUploaded(false) #endif { m_renderEvent = ::CreateEvent(0, 0, 0, 0); diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebhistory.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebhistory.cpp index e5eb308..1c1c72a 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebhistory.cpp +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebhistory.cpp @@ -32,7 +32,6 @@ /*! \class QWebHistoryItem - \ingroup explicitly-shared \since 4.4 \brief The QWebHistoryItem class represents one item in the history of a QWebPage @@ -52,13 +51,17 @@ \row \o userData() \o The user specific data that was stored with the history item. \endtable - \note QWebHistoryItem objects are value based and \l{explicitly shared}. + \note QWebHistoryItem objects are value based, but \e{explicitly shared}. Changing + a QWebHistoryItem instance by calling setUserData() will change all copies of that + instance. \sa QWebHistory, QWebPage::history(), QWebHistoryInterface */ /*! - Constructs a history item from \a other. + Constructs a history item from \a other. The new item and \a other + will share their data, and modifying either this item or \a other will + modify both instances. */ QWebHistoryItem::QWebHistoryItem(const QWebHistoryItem &other) : d(other.d) @@ -66,7 +69,9 @@ QWebHistoryItem::QWebHistoryItem(const QWebHistoryItem &other) } /*! - Assigns the \a other history item to this. + Assigns the \a other history item to this. This item and \a other + will share their data, and modifying either this item or \a other will + modify both instances. */ QWebHistoryItem &QWebHistoryItem::operator=(const QWebHistoryItem &other) { @@ -163,6 +168,8 @@ QVariant QWebHistoryItem::userData() const \since 4.5 Stores user specific data \a userData with the history item. + + \note All copies of this item will be modified. \sa userData() */ diff --git a/src/activeqt/container/qaxbase.cpp b/src/activeqt/container/qaxbase.cpp index 4fc9926..d602946 100644 --- a/src/activeqt/container/qaxbase.cpp +++ b/src/activeqt/container/qaxbase.cpp @@ -3204,12 +3204,12 @@ static const char qt_meta_stringdata_QAxBase[] = { }; static QMetaObject qaxobject_staticMetaObject = { - &QObject::staticMetaObject, qt_meta_stringdata_QAxBase, - qt_meta_data_QAxBase, 0 + { &QObject::staticMetaObject, qt_meta_stringdata_QAxBase, + qt_meta_data_QAxBase, 0 } }; static QMetaObject qaxwidget_staticMetaObject = { - &QWidget::staticMetaObject, qt_meta_stringdata_QAxBase, - qt_meta_data_QAxBase, 0 + { &QWidget::staticMetaObject, qt_meta_stringdata_QAxBase, + qt_meta_data_QAxBase, 0 } }; /*! @@ -3692,6 +3692,8 @@ int QAxBase::qt_metacall(QMetaObject::Call call, int id, void **v) case QMetaMethod::Slot: id = internalInvoke(call, id, v); break; + default: + break; } break; case QMetaObject::ReadProperty: @@ -3706,6 +3708,8 @@ int QAxBase::qt_metacall(QMetaObject::Call call, int id, void **v) case QMetaObject::QueryPropertyUser: id -= mo->propertyCount(); break; + default: + break; } Q_ASSERT(id < 0); return id; @@ -3905,7 +3909,7 @@ bool QAxBase::dynamicCallHelper(const char *name, void *inout, QList<QVariant> & else paramType = d->metaobj->paramType(normFunction, i, &out); - if (!parse && d->useMetaObject && var.type() == QVariant::String || var.type() == QVariant::ByteArray) { + if ((!parse && d->useMetaObject && var.type() == QVariant::String) || var.type() == QVariant::ByteArray) { int enumIndex =mo->indexOfEnumerator(paramType); if (enumIndex != -1) { QMetaEnum metaEnum =mo->enumerator(enumIndex); diff --git a/src/activeqt/container/qaxwidget.cpp b/src/activeqt/container/qaxwidget.cpp index ae468ef..e4c9d42 100644 --- a/src/activeqt/container/qaxwidget.cpp +++ b/src/activeqt/container/qaxwidget.cpp @@ -118,6 +118,8 @@ public: QSize minimumSizeHint() const; int qt_metacall(QMetaObject::Call, int isignal, void **argv); + void* qt_metacast(const char *clname); + inline QAxClientSite *clientSite() const { return axhost; @@ -478,7 +480,9 @@ bool axc_FilterProc(void *m) QAxWidget *ax = 0; QAxHostWidget *host = 0; while (!host && hwnd) { - host = qobject_cast<QAxHostWidget*>(QWidget::find(hwnd)); + QWidget *widget = QWidget::find(hwnd); + if (widget && widget->inherits("QAxHostWidget")) + host = qobject_cast<QAxHostWidget*>(widget); hwnd = ::GetParent(hwnd); } if (host) @@ -527,7 +531,7 @@ bool axc_FilterProc(void *m) } QAxClientSite::QAxClientSite(QAxWidget *c) -: ref(1), widget(c), host(0), eventTranslated(true) +: eventTranslated(true), ref(1), widget(c), host(0) { aggregatedObject = widget->createAggregate(); if (aggregatedObject) { @@ -976,10 +980,38 @@ HRESULT WINAPI QAxClientSite::TransformCoords(POINTL* /*pPtlHimetric*/, POINTF* HRESULT WINAPI QAxClientSite::TranslateAccelerator(LPMSG lpMsg, DWORD /*grfModifiers*/) { - eventTranslated = false; if (lpMsg->message == WM_KEYDOWN && !lpMsg->wParam) return S_OK; + + bool ActiveQtDetected = false; + bool fromInProcServer = false; +#ifdef GWLP_USERDATA + LONG_PTR serverType = GetWindowLongPtr(lpMsg->hwnd, GWLP_USERDATA); +#else + LONG serverType = GetWindowLong(lpMsg->hwnd, GWL_USERDATA); +#endif + if (serverType == QAX_INPROC_SERVER) { + ActiveQtDetected = true; + fromInProcServer = true; + } else if (serverType == QAX_OUTPROC_SERVER) { + ActiveQtDetected = true; + fromInProcServer = false; + } + + eventTranslated = false; + if (!ActiveQtDetected || !fromInProcServer) { + // if the request is coming from an out-of-proc server or a non ActiveQt server, + // we send the message to the host window. This will make sure this key event + // comes to Qt for processing. SendMessage(host->winId(), lpMsg->message, lpMsg->wParam, lpMsg->lParam); + if (ActiveQtDetected && !fromInProcServer) { + // ActiveQt based servers will need further processing of the event + // (eg. <SPACE> key for a checkbox), so we return false. + return S_FALSE; + } + } + // ActiveQt based in-processes-servers will handle the event properly, so + // we dont need to send this key event to the host. return S_OK; } @@ -1617,6 +1649,14 @@ int QAxHostWidget::qt_metacall(QMetaObject::Call call, int isignal, void **argv) return -1; } +void* QAxHostWidget::qt_metacast(const char *clname) +{ + if (!clname) return 0; + if (!qstrcmp(clname,"QAxHostWidget")) + return static_cast<void*>(const_cast< QAxHostWidget*>(this)); + return QWidget::qt_metacast(clname); +} + QSize QAxHostWidget::sizeHint() const { return axhost ? axhost->sizeHint() : QWidget::sizeHint(); diff --git a/src/activeqt/control/qaxserverbase.cpp b/src/activeqt/control/qaxserverbase.cpp index d7a8e07..e7ddb47 100644 --- a/src/activeqt/control/qaxserverbase.cpp +++ b/src/activeqt/control/qaxserverbase.cpp @@ -3502,24 +3502,24 @@ Q_GUI_EXPORT int qt_translateKeyCode(int); HRESULT WINAPI QAxServerBase::TranslateAcceleratorW(MSG *pMsg) { if (pMsg->message != WM_KEYDOWN || !isWidget) - return S_FALSE; + return S_FALSE; DWORD dwKeyMod = 0; if (::GetKeyState(VK_SHIFT) < 0) - dwKeyMod |= 1; // KEYMOD_SHIFT + dwKeyMod |= 1; // KEYMOD_SHIFT if (::GetKeyState(VK_CONTROL) < 0) - dwKeyMod |= 2; // KEYMOD_CONTROL + dwKeyMod |= 2; // KEYMOD_CONTROL if (::GetKeyState(VK_MENU) < 0) - dwKeyMod |= 4; // KEYMOD_ALT + dwKeyMod |= 4; // KEYMOD_ALT switch (LOWORD(pMsg->wParam)) { case VK_TAB: - if (isUIActive) { - bool shift = ::GetKeyState(VK_SHIFT) < 0; - bool giveUp = true; + if (isUIActive) { + bool shift = ::GetKeyState(VK_SHIFT) < 0; + bool giveUp = true; QWidget *curFocus = qt.widget->focusWidget(); if (curFocus) { - if (shift) { + if (shift) { if (!curFocus->isWindow()) { QWidget *nextFocus = curFocus->nextInFocusChain(); QWidget *prevFocus = 0; @@ -3537,9 +3537,10 @@ HRESULT WINAPI QAxServerBase::TranslateAcceleratorW(MSG *pMsg) if (!topLevel) { giveUp = false; ((HackWidget*)curFocus)->focusNextPrevChild(false); + curFocus->window()->setAttribute(Qt::WA_KeyboardFocusChange); } } - } else { + } else { QWidget *nextFocus = curFocus; while (1) { nextFocus = nextFocus->nextInFocusChain(); @@ -3548,64 +3549,82 @@ HRESULT WINAPI QAxServerBase::TranslateAcceleratorW(MSG *pMsg) if (nextFocus->focusPolicy() & Qt::TabFocus) { giveUp = false; ((HackWidget*)curFocus)->focusNextPrevChild(true); + curFocus->window()->setAttribute(Qt::WA_KeyboardFocusChange); break; } } - } + } + } + if (giveUp) { + HWND hwnd = ::GetParent(m_hWnd); + ::SetFocus(hwnd); + } else { + return S_OK; } - if (giveUp) { - HWND hwnd = ::GetParent(m_hWnd); - ::SetFocus(hwnd); - } else { - return S_OK; - } - } - break; + } + break; case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN: - if (isUIActive) - return S_FALSE; - break; + if (isUIActive) + return S_FALSE; + break; default: - if (isUIActive && qt.widget->focusWidget()) { + if (isUIActive && qt.widget->focusWidget()) { int state = Qt::NoButton; - if (dwKeyMod & 1) - state |= Qt::ShiftModifier; - if (dwKeyMod & 2) - state |= Qt::ControlModifier; - if (dwKeyMod & 4) - state |= Qt::AltModifier; - - int key = pMsg->wParam; + if (dwKeyMod & 1) + state |= Qt::ShiftModifier; + if (dwKeyMod & 2) + state |= Qt::ControlModifier; + if (dwKeyMod & 4) + state |= Qt::AltModifier; + + int key = pMsg->wParam; if (!(key >= 'A' && key <= 'Z') && !(key >= '0' && key <= '9')) key = qt_translateKeyCode(pMsg->wParam); - QKeyEvent override(QEvent::ShortcutOverride, key, (Qt::KeyboardModifiers)state); - override.ignore(); - QApplication::sendEvent(qt.widget->focusWidget(), &override); - if (override.isAccepted()) - return S_FALSE; - } - break; + QKeyEvent override(QEvent::ShortcutOverride, key, (Qt::KeyboardModifiers)state); + override.ignore(); + QApplication::sendEvent(qt.widget->focusWidget(), &override); + if (override.isAccepted()) + return S_FALSE; + } + break; } if (!m_spClientSite) - return S_FALSE; + return S_FALSE; IOleControlSite *controlSite = 0; m_spClientSite->QueryInterface(IID_IOleControlSite, (void**)&controlSite); if (!controlSite) - return S_FALSE; - + return S_FALSE; + bool resetUserData = false; + // set server type in the user-data of the window. +#ifdef GWLP_USERDATA + LONG_PTR serverType = QAX_INPROC_SERVER; +#else + LONG serverType = QAX_INPROC_SERVER; +#endif + if (qAxOutProcServer) + serverType = QAX_OUTPROC_SERVER; +#ifdef GWLP_USERDATA + LONG_PTR oldData = SetWindowLongPtr(pMsg->hwnd, GWLP_USERDATA, serverType); +#else + LONG oldData = SetWindowLong(pMsg->hwnd, GWL_USERDATA, serverType); +#endif HRESULT hres = controlSite->TranslateAcceleratorW(pMsg, dwKeyMod); - controlSite->Release(); - + // reset the user-data for the window. +#ifdef GWLP_USERDATA + SetWindowLongPtr(pMsg->hwnd, GWLP_USERDATA, oldData); +#else + SetWindowLong(pMsg->hwnd, GWL_USERDATA, oldData); +#endif return hres; } diff --git a/src/activeqt/shared/qaxtypes.cpp b/src/activeqt/shared/qaxtypes.cpp index 49aa99c..63891c4 100644 --- a/src/activeqt/shared/qaxtypes.cpp +++ b/src/activeqt/shared/qaxtypes.cpp @@ -552,7 +552,7 @@ bool QVariantToVARIANT(const QVariant &var, VARIANT &arg, const QByteArray &type int maxColumns = col.count(); if (maxColumns) { is2D = true; - SAFEARRAYBOUND rgsabound[2] = {0}; + SAFEARRAYBOUND rgsabound[2] = { {0} }; rgsabound[0].cElements = count; rgsabound[1].cElements = maxColumns; array = SafeArrayCreate(VT_VARIANT, 2, rgsabound); diff --git a/src/activeqt/shared/qaxtypes.h b/src/activeqt/shared/qaxtypes.h index 4f647a3..e3c7138 100644 --- a/src/activeqt/shared/qaxtypes.h +++ b/src/activeqt/shared/qaxtypes.h @@ -89,6 +89,9 @@ extern QVariant VARIANTToQVariant(const VARIANT &arg, const QByteArray &typeName extern bool QVariantToVoidStar(const QVariant &var, void *data, const QByteArray &typeName, uint type = 0); extern void clearVARIANT(VARIANT *var); +#define QAX_INPROC_SERVER (0x51540001) +#define QAX_OUTPROC_SERVER (0x51540002) + QT_END_NAMESPACE #endif // QT_NO_WIN_ACTIVEQT diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index ced86d2..1d274c9 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -177,17 +177,6 @@ QUnifiedTimer *QUnifiedTimer::instance() return inst; } -void QUnifiedTimer::updateRecentlyStartedAnimations() -{ - if (animationsToStart.isEmpty()) - return; - - animations += animationsToStart; - updateTimer(); //we make sure we start the timer there - - animationsToStart.clear(); -} - void QUnifiedTimer::timerEvent(QTimerEvent *event) { //this is simply the time we last received a tick @@ -195,15 +184,16 @@ void QUnifiedTimer::timerEvent(QTimerEvent *event) if (time.isValid()) lastTick = consistentTiming ? oldLastTick + timingInterval : time.elapsed(); - //we transfer the waiting animations into the "really running" state - updateRecentlyStartedAnimations(); if (event->timerId() == startStopAnimationTimer.timerId()) { startStopAnimationTimer.stop(); + //we transfer the waiting animations into the "really running" state + animations += animationsToStart; + animationsToStart.clear(); if (animations.isEmpty()) { animationTimer.stop(); time = QTime(); - } else { + } else if (!animationTimer.isActive()) { animationTimer.start(timingInterval, this); lastTick = 0; time.start(); @@ -219,27 +209,19 @@ void QUnifiedTimer::timerEvent(QTimerEvent *event) } } -void QUnifiedTimer::updateTimer() -{ - //we delay the call to start and stop for the animation timer so that if you - //stop and start animations in batch you don't stop/start the timer too often. - if (!startStopAnimationTimer.isActive()) - startStopAnimationTimer.start(0, this); // we delay the actual start of the animation -} - void QUnifiedTimer::registerAnimation(QAbstractAnimation *animation) { if (animations.contains(animation) ||animationsToStart.contains(animation)) return; animationsToStart << animation; - updateTimer(); + startStopAnimationTimer.start(0, this); // we delay the check if we should start/stop the global timer } void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) { animations.removeAll(animation); animationsToStart.removeAll(animation); - updateTimer(); + startStopAnimationTimer.start(0, this); // we delay the check if we should start/stop the global timer } diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index 0d8402e..b281aa2 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -135,11 +135,8 @@ public: protected: void timerEvent(QTimerEvent *); - void updateTimer(); private: - void updateRecentlyStartedAnimations(); - QBasicTimer animationTimer, startStopAnimationTimer; QTime time; int lastTick; diff --git a/src/corelib/animation/qparallelanimationgroup.cpp b/src/corelib/animation/qparallelanimationgroup.cpp index 5e4b0d2..8aa04a4 100644 --- a/src/corelib/animation/qparallelanimationgroup.cpp +++ b/src/corelib/animation/qparallelanimationgroup.cpp @@ -214,7 +214,8 @@ void QParallelAnimationGroup::updateState(QAbstractAnimation::State oldState, d->connectUncontrolledAnimations(); for (int i = 0; i < d->animations.size(); ++i) { QAbstractAnimation *animation = d->animations.at(i); - animation->stop(); + if (oldState == Stopped) + animation->stop(); animation->setDirection(d->direction); animation->start(); } diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index 48e5ec1..fdd98c2 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -267,7 +267,7 @@ void QVariantAnimationPrivate::setCurrentValueForProgress(const qreal progress) localProgress); qSwap(currentValue, ret); q->updateCurrentValue(currentValue); - if ((connectedSignals & changedSignalMask) && currentValue != ret) { + if ((connectedSignals[0] & changedSignalMask) && currentValue != ret) { //the value has changed emit q->valueChanged(currentValue); } diff --git a/src/corelib/arch/qatomic_vxworks.h b/src/corelib/arch/qatomic_vxworks.h index 573a44d..b441210 100644 --- a/src/corelib/arch/qatomic_vxworks.h +++ b/src/corelib/arch/qatomic_vxworks.h @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 776a740..63941ef 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -1083,7 +1083,7 @@ bool qSharedBuild() \value WV_XP Windows XP (operating system version 5.1) \value WV_2003 Windows Server 2003, Windows Server 2003 R2, Windows Home Server, Windows XP Professional x64 Edition (operating system version 5.2) \value WV_VISTA Windows Vista, Windows Server 2008 (operating system version 6.0) - \value WV_WINDOWS7 Windows 7 (operating system version 6.1) + \value WV_WINDOWS7 Windows 7, Windows Server 2008 R2 (operating system version 6.1) Alternatively, you may use the following macros which correspond directly to the Windows operating system version number: @@ -1092,7 +1092,7 @@ bool qSharedBuild() \value WV_5_1 Operating system version 5.1, corresponds to Windows XP \value WV_5_2 Operating system version 5.2, corresponds to Windows Server 2003, Windows Server 2003 R2, Windows Home Server, and Windows XP Professional x64 Edition \value WV_6_0 Operating system version 6.0, corresponds to Windows Vista and Windows Server 2008 - \value WV_6_1 Operating system version 6.1, corresponds to Windows 7 + \value WV_6_1 Operating system version 6.1, corresponds to Windows 7 and Windows Server 2008 R2 CE-based versions: diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 8263bae..1711f16 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -279,7 +279,7 @@ namespace QT_NAMESPACE {} # endif #endif -#if defined(Q_OS_MAC64) && !defined(QT_MAC_USE_COCOA) +#if defined(Q_OS_MAC64) && !defined(QT_MAC_USE_COCOA) && !defined(QT_BUILD_QMAKE) #error "You are building a 64-bit application, but using a 32-bit version of Qt. Check your build configuration." #endif @@ -311,11 +311,8 @@ namespace QT_NAMESPACE {} # if !defined(MAC_OS_X_VERSION_10_6) # define MAC_OS_X_VERSION_10_6 MAC_OS_X_VERSION_10_5 + 1 # endif -# if (MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_6) -# warning "Support for this version of Mac OS X is still preliminary" -# endif # if (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_6) -# error "This version of Mac OS X is unsupported" +# warning "This version of Mac OS X is unsupported" # endif #endif @@ -1176,6 +1173,11 @@ class QDataStream; # else # define Q_OPENGL_EXPORT Q_DECL_IMPORT # endif +# if defined(QT_BUILD_MULTIMEDIA_LIB) +# define Q_MULTIMEDIA_EXPORT Q_DECL_EXPORT +# else +# define Q_MULTIMEDIA_EXPORT Q_DECL_IMPORT +# endif # if defined(QT_BUILD_OPENVG_LIB) # define Q_OPENVG_EXPORT Q_DECL_EXPORT # else @@ -1220,6 +1222,7 @@ class QDataStream; # define Q_SVG_EXPORT Q_DECL_IMPORT # define Q_CANVAS_EXPORT Q_DECL_IMPORT # define Q_OPENGL_EXPORT Q_DECL_IMPORT +# define Q_MULTIMEDIA_EXPORT Q_DECL_IMPORT # define Q_OPENVG_EXPORT Q_DECL_IMPORT # define Q_XML_EXPORT Q_DECL_IMPORT # define Q_XMLPATTERNS_EXPORT Q_DECL_IMPORT @@ -1246,6 +1249,7 @@ class QDataStream; # define Q_NETWORK_EXPORT Q_DECL_EXPORT # define Q_SVG_EXPORT Q_DECL_EXPORT # define Q_OPENGL_EXPORT Q_DECL_EXPORT +# define Q_MULTIMEDIA_EXPORT Q_DECL_EXPORT # define Q_OPENVG_EXPORT Q_DECL_EXPORT # define Q_XML_EXPORT Q_DECL_EXPORT # define Q_XMLPATTERNS_EXPORT Q_DECL_EXPORT @@ -1259,6 +1263,7 @@ class QDataStream; # define Q_NETWORK_EXPORT # define Q_SVG_EXPORT # define Q_OPENGL_EXPORT +# define Q_MULTIMEDIA_EXPORT # define Q_XML_EXPORT # define Q_XMLPATTERNS_EXPORT # define Q_SCRIPT_EXPORT @@ -2308,12 +2313,14 @@ QT3_SUPPORT Q_CORE_EXPORT const char *qInstallPathSysconf(); #define QT_MODULE_DBUS 0x08000 #define QT_MODULE_SCRIPTTOOLS 0x10000 #define QT_MODULE_OPENVG 0x20000 +#define QT_MODULE_MULTIMEDIA 0x40000 /* Qt editions */ #define QT_EDITION_CONSOLE (QT_MODULE_CORE \ | QT_MODULE_NETWORK \ | QT_MODULE_SQL \ | QT_MODULE_SCRIPT \ + | QT_MODULE_MULTIMEDIA \ | QT_MODULE_XML \ | QT_MODULE_XMLPATTERNS \ | QT_MODULE_TEST \ @@ -2329,6 +2336,7 @@ QT3_SUPPORT Q_CORE_EXPORT const char *qInstallPathSysconf(); | QT_MODULE_OPENGL \ | QT_MODULE_OPENVG \ | QT_MODULE_SQL \ + | QT_MODULE_MULTIMEDIA \ | QT_MODULE_XML \ | QT_MODULE_XMLPATTERNS \ | QT_MODULE_SCRIPT \ @@ -2377,6 +2385,9 @@ QT_LICENSED_MODULE(OpenVG) #if (QT_EDITION & QT_MODULE_SQL) QT_LICENSED_MODULE(Sql) #endif +#if (QT_EDITION & QT_MODULE_MULTIMEDIA) +QT_LICENSED_MODULE(Multimedia) +#endif #if (QT_EDITION & QT_MODULE_XML) QT_LICENSED_MODULE(Xml) #endif diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 7770fd6..f172d77 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1406,6 +1406,21 @@ public: ImCurrentSelection }; + enum InputMethodHint { + ImhNone = 0x0, + ImhHiddenText = 0x1, + ImhNumbersOnly = 0x2, + ImhUppercaseOnly = 0x4, + ImhLowercaseOnly = 0x8, + ImhNoAutoUppercase = 0x10, + ImhPreferNumbers = 0x20, + ImhPreferUppercase = 0x40, + ImhPreferLowercase = 0x80, + ImhNoPredictiveText = 0x100, + ImhDialableCharactersOnly = 0x200 + }; + Q_DECLARE_FLAGS(InputMethodHints, InputMethodHint) + enum ToolButtonStyle { ToolButtonIconOnly, ToolButtonTextOnly, @@ -1591,6 +1606,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::ItemFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::MatchFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::TextInteractionFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::TouchPointStates) +Q_DECLARE_OPERATORS_FOR_FLAGS(Qt::InputMethodHints) typedef bool (*qInternalCallback)(void **); diff --git a/src/corelib/io/qfilesystemwatcher_fsevents.cpp b/src/corelib/io/qfilesystemwatcher_fsevents.cpp index 3e0aee8..cb276b7 100644 --- a/src/corelib/io/qfilesystemwatcher_fsevents.cpp +++ b/src/corelib/io/qfilesystemwatcher_fsevents.cpp @@ -422,9 +422,12 @@ void QFSEventsFileSystemWatcherEngine::fseventsCallback(ConstFSEventStreamRef , void QFSEventsFileSystemWatcherEngine::stop() { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + QMutexLocker locker(&mutex); stopFSStream(fsStream); - if (threadsRunLoop) + if (threadsRunLoop) { CFRunLoopStop(threadsRunLoop); + waitForStop.wait(&mutex); + } #endif } @@ -461,6 +464,8 @@ void QFSEventsFileSystemWatcherEngine::run() // immediately. CFRunLoopRun(); threadsRunLoop = 0; + QMutexLocker locker(&mutex); + waitForStop.wakeAll(); #endif } diff --git a/src/corelib/io/qfilesystemwatcher_fsevents_p.h b/src/corelib/io/qfilesystemwatcher_fsevents_p.h index 4770867..ffc0c68 100644 --- a/src/corelib/io/qfilesystemwatcher_fsevents_p.h +++ b/src/corelib/io/qfilesystemwatcher_fsevents_p.h @@ -114,6 +114,7 @@ private: CFRunLoopRef threadsRunLoop; QMutex mutex; QWaitCondition waitCondition; + QWaitCondition waitForStop; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 PathHash filePathInfoHash; PathHash dirPathInfoHash; diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index 7177293..3493784 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -32,7 +32,8 @@ HEADERS += \ kernel/qsharedmemory_p.h \ kernel/qsystemsemaphore.h \ kernel/qsystemsemaphore_p.h \ - kernel/qfunctions_p.h + kernel/qfunctions_p.h \ + kernel/qguard_p.h SOURCES += \ kernel/qabstracteventdispatcher.cpp \ @@ -54,7 +55,7 @@ SOURCES += \ kernel/qcoreglobaldata.cpp \ kernel/qsharedmemory.cpp \ kernel/qsystemsemaphore.cpp \ - kernel/qpointer.cpp + kernel/qpointer.cpp win32 { SOURCES += \ diff --git a/src/corelib/kernel/qabstractitemmodel.cpp b/src/corelib/kernel/qabstractitemmodel.cpp index 3d9263e..5d5d4cc 100644 --- a/src/corelib/kernel/qabstractitemmodel.cpp +++ b/src/corelib/kernel/qabstractitemmodel.cpp @@ -467,6 +467,27 @@ QAbstractItemModel *QAbstractItemModelPrivate::staticEmptyModel() return qEmptyModel(); } +namespace { + struct DefaultRoleNames : public QHash<int, QByteArray> + { + 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<int,QByteArray> &QAbstractItemModelPrivate::defaultRoleNames() +{ + return *qDefaultRoleNames(); +} + /*! \internal return true if \a value contains a numerical type @@ -1863,6 +1884,37 @@ QSize QAbstractItemModel::span(const QModelIndex &) const } /*! + \since 4.6 + + Sets the model's role names to \a roleNames. + + This function is provided to allow 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<int,QByteArray> &roleNames) +{ + Q_D(QAbstractItemModel); + d->roleNames = roleNames; +} + +/*! + \since 4.6 + + Returns the model's role names. + + \sa setRoleNames() +*/ +const QHash<int,QByteArray> &QAbstractItemModel::roleNames() const +{ + Q_D(const QAbstractItemModel); + return d->roleNames; +} + +/*! Called to let the model know that it should submit whatever it has cached to the permanent storage. Typically used for row editing. diff --git a/src/corelib/kernel/qabstractitemmodel.h b/src/corelib/kernel/qabstractitemmodel.h index dc7d7bd..a6bbff4 100644 --- a/src/corelib/kernel/qabstractitemmodel.h +++ b/src/corelib/kernel/qabstractitemmodel.h @@ -220,6 +220,8 @@ public: Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const; virtual QSize span(const QModelIndex &index) const; + const QHash<int,QByteArray> &roleNames() const; + #ifdef Q_NO_USING_KEYWORD inline QObject *parent() const { return QObject::parent(); } #else @@ -282,6 +284,8 @@ protected: void changePersistentIndexList(const QModelIndexList &from, const QModelIndexList &to); QModelIndexList persistentIndexList() const; + void setRoleNames(const QHash<int,QByteArray> &roleNames); + private: Q_DECLARE_PRIVATE(QAbstractItemModel) Q_DISABLE_COPY(QAbstractItemModel) diff --git a/src/corelib/kernel/qabstractitemmodel_p.h b/src/corelib/kernel/qabstractitemmodel_p.h index c047f95..6a29723 100644 --- a/src/corelib/kernel/qabstractitemmodel_p.h +++ b/src/corelib/kernel/qabstractitemmodel_p.h @@ -78,7 +78,7 @@ class Q_CORE_EXPORT QAbstractItemModelPrivate : public QObjectPrivate Q_DECLARE_PUBLIC(QAbstractItemModel) public: - QAbstractItemModelPrivate() : QObjectPrivate(), supportedDragActions(-1) {} + QAbstractItemModelPrivate() : QObjectPrivate(), supportedDragActions(-1), roleNames(defaultRoleNames()) {} void removePersistentIndexData(QPersistentModelIndexData *data); void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last); void rowsInserted(const QModelIndex &parent, int first, int last); @@ -144,6 +144,9 @@ public: } persistent; Qt::DropActions supportedDragActions; + + QHash<int,QByteArray> roleNames; + static const QHash<int,QByteArray> &defaultRoleNames(); }; QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcore_unix.cpp b/src/corelib/kernel/qcore_unix.cpp index b57d385..efa9c6d 100644 --- a/src/corelib/kernel/qcore_unix.cpp +++ b/src/corelib/kernel/qcore_unix.cpp @@ -161,76 +161,3 @@ int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, } QT_END_NAMESPACE - -#ifdef Q_OS_LINUX -// Don't wait for libc to supply the calls we need -// Make syscalls directly - -# if defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0204 -// glibc 2.4 has syscall(...) -# include <sys/syscall.h> -# include <asm/unistd.h> -# else -// no syscall(...) -static inline int syscall(...) { errno = ENOSYS; return -1;} -# endif - -# ifndef __NR_dup3 -# if defined(__i386__) -# define __NR_dup3 330 -# define __NR_pipe2 331 -# elif defined(__x86_64__) -# define __NR_dup3 292 -# define __NR_pipe2 293 -# elif defined(__ia64__) -# define __NR_dup3 1316 -# define __NR_pipe2 1317 -# else -// set the syscalls to absurd numbers so that they'll cause ENOSYS errors -# warning "Please port the pipe2/dup3 code to this platform" -# define __NR_dup3 -1 -# define __NR_pipe2 -1 -# endif -# endif - -# if !defined(__NR_socketcall) && !defined(__NR_accept4) -# if defined(__x86_64__) -# define __NR_accept4 288 -# elif defined(__ia64__) -// not assigned yet to IA-64 -# define __NR_accept4 -1 -# else -// set the syscalls to absurd numbers so that they'll cause ENOSYS errors -# warning "Please port the accept4 code to this platform" -# define __NR_accept4 -1 -# endif -# endif - -QT_BEGIN_NAMESPACE -namespace QtLibcSupplement { - int pipe2(int pipes[], int flags) - { - return syscall(__NR_pipe2, pipes, flags); - } - - int dup3(int oldfd, int newfd, int flags) - { - return syscall(__NR_dup3, oldfd, newfd, flags); - } - - int accept4(int s, sockaddr *addr, QT_SOCKLEN_T *addrlen, int flags) - { -# if defined(__NR_socketcall) - // This platform uses socketcall() instead of raw syscalls - // the SYS_ACCEPT4 number is cross-platform: 18 - return syscall(__NR_socketcall, 18, &s); -# else - return syscall(__NR_accept4, s, addr, addrlen, flags); -# endif - - Q_UNUSED(addr); Q_UNUSED(addrlen); Q_UNUSED(flags); // they're actually used - } -} -QT_END_NAMESPACE -#endif // Q_OS_LINUX - diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h index dceb73a..3bdd5ec 100644 --- a/src/corelib/kernel/qcore_unix_p.h +++ b/src/corelib/kernel/qcore_unix_p.h @@ -73,32 +73,16 @@ struct sockaddr; -#if defined(Q_OS_LINUX) && defined(__GLIBC__) && (__GLIBC__ * 0x100 + __GLIBC_MINOR__) >= 0x0204 -// Linux supports thread-safe FD_CLOEXEC +#if defined(Q_OS_LINUX) && defined(O_CLOEXEC) # define QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC 1 - -// add defines for the consts for Linux -# ifndef O_CLOEXEC -# define O_CLOEXEC 02000000 -# endif -# ifndef FD_DUPFD_CLOEXEC -# define F_DUPFD_CLOEXEC 1030 -# endif -# ifndef SOCK_CLOEXEC -# define SOCK_CLOEXEC O_CLOEXEC -# endif -# ifndef SOCK_NONBLOCK -# define SOCK_NONBLOCK O_NONBLOCK -# endif -# ifndef MSG_CMSG_CLOEXEC -# define MSG_CMSG_CLOEXEC 0x40000000 -# endif - QT_BEGIN_NAMESPACE namespace QtLibcSupplement { - Q_CORE_EXPORT int accept4(int, sockaddr *, QT_SOCKLEN_T *, int flags); - Q_CORE_EXPORT int dup3(int oldfd, int newfd, int flags); - Q_CORE_EXPORT int pipe2(int pipes[], int flags); + 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); @@ -190,7 +174,7 @@ static inline int qt_safe_pipe(int pipefd[2], int flags = 0) #endif register int ret; -#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC +#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 @@ -246,7 +230,7 @@ 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 +#if QT_UNIX_SUPPORTS_THREADSAFE_CLOEXEC && defined(O_CLOEXEC) // use dup3 if (flags & FD_CLOEXEC) { EINTR_LOOP(ret, ::dup3(oldfd, newfd, O_CLOEXEC)); @@ -323,6 +307,10 @@ static inline pid_t qt_safe_waitpid(pid_t pid, int *status, int options) #endif // Q_OS_VXWORKS +#if !defined(_POSIX_MONOTONIC_CLOCK) +# define _POSIX_MONOTONIC_CLOCK -1 +#endif + bool qt_gettime_is_monotonic(); timeval qt_gettime(); Q_CORE_EXPORT int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index a682fad9..ff00c1c 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -269,7 +269,7 @@ QT_BEGIN_NAMESPACE \omitvalue FutureCallOut \omitvalue CocoaRequestModal \omitvalue Signal - \omitvalue WinGesture + \omitvalue NativeGesture */ /*! diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index 1d86f47..d941286 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -276,7 +276,7 @@ public: TouchUpdate = 195, TouchEnd = 196, - WinGesture = 197, + NativeGesture = 197, // Internal for platform gesture support // 512 reserved for Qt Jambi's MetaCall event // 513 reserved for Qt Jambi's DeleteOnMainThread event diff --git a/src/corelib/kernel/qeventdispatcher_glib.cpp b/src/corelib/kernel/qeventdispatcher_glib.cpp index 7610631..2bbe560 100644 --- a/src/corelib/kernel/qeventdispatcher_glib.cpp +++ b/src/corelib/kernel/qeventdispatcher_glib.cpp @@ -263,6 +263,7 @@ QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context) (void) new (&timerSource->timerList) QTimerInfoList(); timerSource->processEventsFlags = QEventLoop::AllEvents; g_source_set_can_recurse(&timerSource->source, true); + g_source_set_priority(&timerSource->source, G_PRIORITY_DEFAULT_IDLE); g_source_attach(&timerSource->source, mainContext); } diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h index 9c67c70..5e016d3 100644 --- a/src/corelib/kernel/qeventdispatcher_unix_p.h +++ b/src/corelib/kernel/qeventdispatcher_unix_p.h @@ -69,9 +69,6 @@ #endif QT_BEGIN_NAMESPACE -#if !defined(_POSIX_MONOTONIC_CLOCK) -# define _POSIX_MONOTONIC_CLOCK -1 -#endif // internal timer info struct QTimerInfo { diff --git a/src/corelib/kernel/qfunctions_vxworks.cpp b/src/corelib/kernel/qfunctions_vxworks.cpp index 6d5e7cc..def8f4c 100644 --- a/src/corelib/kernel/qfunctions_vxworks.cpp +++ b/src/corelib/kernel/qfunctions_vxworks.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtCore module of the Qt Toolkit. ** @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/corelib/kernel/qfunctions_vxworks.h b/src/corelib/kernel/qfunctions_vxworks.h index cc98948..e31d495 100644 --- a/src/corelib/kernel/qfunctions_vxworks.h +++ b/src/corelib/kernel/qfunctions_vxworks.h @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/corelib/kernel/qguard_p.h b/src/corelib/kernel/qguard_p.h new file mode 100644 index 0000000..6af01ac --- /dev/null +++ b/src/corelib/kernel/qguard_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGUARD_P_H +#define QGUARD_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/qglobal.h" + +QT_BEGIN_NAMESPACE + +class QObject; +template<class T> +class QGuard +{ + QObject *o; + QGuard<QObject> *next; + QGuard<QObject> **prev; + friend void q_guard_addGuard(QGuard<QObject> *); + friend void q_guard_removeGuard(QGuard<QObject> *); + friend class QObjectPrivate; +public: + inline QGuard(); + inline QGuard(T *); + inline QGuard(const QGuard<T> &); + inline virtual ~QGuard(); + + inline QGuard<T> &operator=(const QGuard<T> &o); + inline QGuard<T> &operator=(T *); + + inline bool isNull() const + { return !o; } + + inline T* operator->() const + { return static_cast<T*>(const_cast<QObject*>(o)); } + inline T& operator*() const + { return *static_cast<T*>(const_cast<QObject*>(o)); } + inline operator T*() const + { return static_cast<T*>(const_cast<QObject*>(o)); } + inline T* data() const + { return static_cast<T*>(const_cast<QObject*>(o)); } + +protected: + virtual void objectDestroyed(T *) {} +}; + +QT_END_NAMESPACE + +#include "private/qobject_p.h" + +QT_BEGIN_NAMESPACE + +inline void q_guard_addGuard(QGuard<QObject> *); +inline void q_guard_removeGuard(QGuard<QObject> *); + +template<class T> +QGuard<T>::QGuard() +: o(0), next(0), prev(0) +{ +} + +template<class T> +QGuard<T>::QGuard(T *g) +: o(g), next(0), prev(0) +{ + if (o) q_guard_addGuard(reinterpret_cast<QGuard<QObject> *>(this)); +} + +template<class T> +QGuard<T>::QGuard(const QGuard<T> &g) +: o(g.o), next(0), prev(0) +{ + if (o) q_guard_addGuard(reinterpret_cast<QGuard<QObject> *>(this)); +} + +template<class T> +QGuard<T>::~QGuard() +{ + if (prev) q_guard_removeGuard(reinterpret_cast<QGuard<QObject> *>(this)); + o = 0; +} + +template<class T> +QGuard<T> &QGuard<T>::operator=(const QGuard<T> &g) +{ + if (g.o != o) { + if (prev) + q_guard_removeGuard(reinterpret_cast<QGuard<QObject> *>(this)); + o = g.o; + if (o) q_guard_addGuard(reinterpret_cast<QGuard<QObject> *>(this)); + } + return *this; +} + +template<class T> +inline QGuard<T> &QGuard<T>::operator=(T *g) +{ + if (g != o) { + if (prev) + q_guard_removeGuard(reinterpret_cast<QGuard<QObject> *>(this)); + o = g; + if (o) q_guard_addGuard(reinterpret_cast<QGuard<QObject> *>(this)); + } + return *this; +} + +QT_END_NAMESPACE + +#endif // QGUARD_P_H diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 08cecaf..d43c5dd 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -148,7 +148,9 @@ enum PropertyFlags { Resettable = 0x00000004, EnumOrFlag = 0x00000008, StdCppSet = 0x00000100, -// Override = 0x00000200, +// Override = 0x00000200, + Constant = 0x00000400, + Final = 0x00000800, Designable = 0x00001000, ResolveDesignable = 0x00002000, Scriptable = 0x00004000, @@ -179,6 +181,10 @@ enum MethodFlags { MethodScriptable = 0x40 }; +enum MetaObjectFlags { + DynamicMetaObject = 0x01 +}; + struct QMetaObjectPrivate { int revision; @@ -188,6 +194,7 @@ struct QMetaObjectPrivate int propertyCount, propertyData; int enumeratorCount, enumeratorData; int constructorCount, constructorData; + int flags; }; static inline const QMetaObjectPrivate *priv(const uint* data) @@ -277,6 +284,17 @@ int QMetaObject::static_metacall(Call cl, int idx, void **argv) const } /*! + \internal +*/ +int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv) +{ + if (QMetaObject *mo = object->d_ptr->metaObject) + return static_cast<QAbstractDynamicMetaObject*>(mo)->metaCall(cl, idx, argv); + else + return object->qt_metacall(cl, idx, argv); +} + +/*! \fn const char *QMetaObject::className() const Returns the class name. @@ -696,6 +714,14 @@ int QMetaObject::indexOfProperty(const char *name) const } m = m->d.superdata; } + + if (i == -1 && priv(this->d.data)->revision >= 3 && (priv(this->d.data)->flags & DynamicMetaObject)){ + QAbstractDynamicMetaObject *me = + const_cast<QAbstractDynamicMetaObject *>(static_cast<const QAbstractDynamicMetaObject *>(this)); + + i = me->createProperty(name, 0); + } + return i; } @@ -1326,6 +1352,18 @@ int QMetaMethod::attributes() const } /*! + \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(); +} + +/*! Returns the access specification of this method (private, protected, or public). @@ -1525,7 +1563,7 @@ bool QMetaMethod::invoke(QObject *object, // recompute the methodIndex by reversing the arithmetic in QMetaObject::property() int methodIndex = ((handle - priv(mobj->d.data)->methodData) / 5) + mobj->methodOffset(); if (connectionType == Qt::DirectConnection) { - return object->qt_metacall(QMetaObject::InvokeMetaMethod, methodIndex, param) < 0; + return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param) < 0; } else { if (returnValue.data()) { qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in " @@ -2040,6 +2078,18 @@ int QMetaProperty::userType() const } /*! + \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. @@ -2151,9 +2201,8 @@ QVariant QMetaProperty::read(const QObject *object) const value = QVariant(t, (void*)0); argv[0] = value.data(); } - const_cast<QObject*>(object)->qt_metacall(QMetaObject::ReadProperty, - idx + mobj->propertyOffset(), - argv); + QMetaObject::metacall(const_cast<QObject*>(object), QMetaObject::ReadProperty, + idx + mobj->propertyOffset(), argv); if (status != -1) return value; @@ -2224,7 +2273,7 @@ bool QMetaProperty::write(QObject *object, const QVariant &value) const argv[0] = &v; else argv[0] = v.data(); - object->qt_metacall(QMetaObject::WriteProperty, idx + mobj->propertyOffset(), argv); + QMetaObject::metacall(object, QMetaObject::WriteProperty, idx + mobj->propertyOffset(), argv); return status; } @@ -2241,7 +2290,7 @@ bool QMetaProperty::reset(QObject *object) const if (!object || !mobj || !isResettable()) return false; void *argv[] = { 0 }; - object->qt_metacall(QMetaObject::ResetProperty, idx + mobj->propertyOffset(), argv); + QMetaObject::metacall(object, QMetaObject::ResetProperty, idx + mobj->propertyOffset(), argv); return true; } @@ -2355,8 +2404,8 @@ bool QMetaProperty::isDesignable(const QObject *object) const bool b = flags & Designable; if (object) { void *argv[] = { &b }; - const_cast<QObject*>(object)->qt_metacall(QMetaObject::QueryPropertyDesignable, - idx + mobj->propertyOffset(), argv); + QMetaObject::metacall(const_cast<QObject*>(object), QMetaObject::QueryPropertyDesignable, + idx + mobj->propertyOffset(), argv); } return b; @@ -2381,8 +2430,8 @@ bool QMetaProperty::isScriptable(const QObject *object) const bool b = flags & Scriptable; if (object) { void *argv[] = { &b }; - const_cast<QObject*>(object)->qt_metacall(QMetaObject::QueryPropertyScriptable, - idx + mobj->propertyOffset(), argv); + QMetaObject::metacall(const_cast<QObject*>(object), QMetaObject::QueryPropertyScriptable, + idx + mobj->propertyOffset(), argv); } return b; } @@ -2405,8 +2454,8 @@ bool QMetaProperty::isStored(const QObject *object) const bool b = flags & Stored; if (object) { void *argv[] = { &b }; - const_cast<QObject*>(object)->qt_metacall(QMetaObject::QueryPropertyStored, - idx + mobj->propertyOffset(), argv); + QMetaObject::metacall(const_cast<QObject*>(object), QMetaObject::QueryPropertyStored, + idx + mobj->propertyOffset(), argv); } return b; } @@ -2432,13 +2481,41 @@ bool QMetaProperty::isUser(const QObject *object) const bool b = flags & User; if (object) { void *argv[] = { &b }; - const_cast<QObject*>(object)->qt_metacall(QMetaObject::QueryPropertyUser, - idx + mobj->propertyOffset(), argv); + QMetaObject::metacall(const_cast<QObject*>(object), QMetaObject::QueryPropertyUser, + idx + mobj->propertyOffset(), argv); } return b; } /*! + 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; +} + +/*! + 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; @@ -2458,8 +2535,8 @@ bool QMetaProperty::isEditable(const QObject *object) const bool b = flags & Editable; if (object) { void *argv[] = { &b }; - const_cast<QObject*>(object)->qt_metacall(QMetaObject::QueryPropertyEditable, - idx + mobj->propertyOffset(), argv); + QMetaObject::metacall(const_cast<QObject*>(object), QMetaObject::QueryPropertyEditable, + idx + mobj->propertyOffset(), argv); } return b; } diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index 000ba6e..419fe06 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -69,6 +69,7 @@ public: MethodType methodType() const; enum Attributes { Compatibility = 0x1, Cloned = 0x2, Scriptable = 0x4 }; int attributes() const; + int methodIndex() const; inline const QMetaObject *enclosingMetaObject() const { return mobj; } @@ -178,6 +179,7 @@ public: const char *typeName() const; QVariant::Type type() const; int userType() const; + int propertyIndex() const; bool isReadable() const; bool isWritable() const; @@ -187,6 +189,8 @@ public: 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; diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 6503ab0..e37b6d3 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -56,6 +56,7 @@ #include <qvarlengtharray.h> #include <qset.h> #include <qsemaphore.h> +#include <qsharedpointer.h> #include <private/qorderedmutexlocker_p.h> #include <private/qmutexpool_p.h> @@ -122,8 +123,11 @@ extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *) } } +QObjectData::~QObjectData() {} +QDeclarativeData::~QDeclarativeData() {} + QObjectPrivate::QObjectPrivate(int version) - : threadData(0), currentSender(0), currentChildBeingDeleted(0), connectionLists(0), senders(0) + : threadData(0), connectionLists(0), senders(0), currentSender(0), currentChildBeingDeleted(0), declarativeData(0), objectGuards(0) { if (version != QObjectPrivateVersion) qFatal("Cannot mix incompatible Qt libraries"); @@ -139,15 +143,18 @@ QObjectPrivate::QObjectPrivate(int version) receiveChildEvents = true; postedEvents = 0; extraData = 0; - connectedSignals = 0; + for (uint i = 0; i < (sizeof connectedSignals / sizeof connectedSignals[0]); ++i) + connectedSignals[i] = 0; inEventHandler = false; inThreadChangeEvent = false; deleteWatch = 0; + metaObject = 0; hasGuards = false; } QObjectPrivate::~QObjectPrivate() { + delete static_cast<QAbstractDynamicMetaObject*>(metaObject); if (deleteWatch) *deleteWatch = 1; #ifndef QT_NO_USERDATA @@ -425,7 +432,22 @@ void QMetaObject::changeGuard(QObject **ptr, QObject *o) */ void QObjectPrivate::clearGuards(QObject *object) { - if (!QObjectPrivate::get(object)->hasGuards) + QObjectPrivate *priv = QObjectPrivate::get(object); + QGuard<QObject> *guard = priv->objectGuards; + while (guard) { + guard->o = 0; + guard = guard->next; + } + while (priv->objectGuards) { + guard = priv->objectGuards; + guard->prev = 0; + if (guard->next) guard->next->prev = &priv->objectGuards; + priv->objectGuards = guard->next; + guard->next = 0; + guard->objectDestroyed(object); + } + + if (!priv->hasGuards) return; GuardHash *hash = guardHash(); if (hash) { @@ -467,7 +489,7 @@ QMetaCallEvent::~QMetaCallEvent() */ int QMetaCallEvent::placeMetaCall(QObject *object) { - return object->qt_metacall(QMetaObject::InvokeMetaMethod, id_, args_); + return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, id_, args_); } /*! @@ -747,7 +769,21 @@ QObject::~QObject() 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; + } + emit destroyed(this); + if (d->declarativeData) + d->declarativeData->destroyed(this); { QMutexLocker locker(signalSlotLock(this)); @@ -2849,10 +2885,16 @@ bool QMetaObject::connect(const QObject *sender, int signal_index, s->d_func()->addConnection(signal_index, c); - if (signal_index < 0) - sender->d_func()->connectedSignals = ~0u; - else if (signal_index < 32) - sender->d_func()->connectedSignals |= (1 << signal_index); + if (signal_index < 0) { + for (uint i = 0; i < (sizeof sender->d_func()->connectedSignals + / sizeof sender->d_func()->connectedSignals[0] ); ++i) + sender->d_func()->connectedSignals[i] = ~0u; + } else if (signal_index < (int)sizeof sender->d_func()->connectedSignals * 8) { + uint n = (signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); + sender->d_func()->connectedSignals[n] |= (1 << (signal_index - n * 8 + * sizeof sender->d_func()->connectedSignals[0])); + } + return true; } @@ -3140,10 +3182,10 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal } #if defined(QT_NO_EXCEPTIONS) - receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); + metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); #else try { - receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); + metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); } catch (...) { locker.relock(); @@ -3192,11 +3234,12 @@ void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal */ void QMetaObject::activate(QObject *sender, int signal_index, void **argv) { - if (signal_index < 32 + if (signal_index < (int)sizeof(sender->d_func()->connectedSignals) * 8 && !qt_signal_spy_callback_set.signal_begin_callback && !qt_signal_spy_callback_set.signal_end_callback) { - uint signal_mask = 1 << signal_index; - if ((sender->d_func()->connectedSignals & signal_mask) == 0) + uint n = (signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); + uint m = 1 << (signal_index - n * 8 * sizeof sender->d_func()->connectedSignals[0]); + if ((sender->d_func()->connectedSignals[n] & m) == 0) // nothing connected to these signals, and no spy return; } @@ -3209,11 +3252,12 @@ void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_sign void **argv) { int signal_index = m->methodOffset() + local_signal_index; - if (signal_index < 32 + if (signal_index < (int)sizeof(sender->d_func()->connectedSignals) * 8 && !qt_signal_spy_callback_set.signal_begin_callback && !qt_signal_spy_callback_set.signal_end_callback) { - uint signal_mask = 1 << signal_index; - if ((sender->d_func()->connectedSignals & signal_mask) == 0) + uint n = (signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); + uint m = 1 << (signal_index - n * 8 * sizeof sender->d_func()->connectedSignals[0]); + if ((sender->d_func()->connectedSignals[n] & m) == 0) // nothing connected to these signals, and no spy return; } @@ -3225,21 +3269,59 @@ void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_sign void QMetaObject::activate(QObject *sender, const QMetaObject *m, int from_local_signal_index, int to_local_signal_index, void **argv) { + Q_ASSERT(from_local_signal_index <= to_local_signal_index); int offset = m->methodOffset(); int from_signal_index = offset + from_local_signal_index; int to_signal_index = offset + to_local_signal_index; - if (to_signal_index < 32 + + if (to_signal_index < (int)sizeof(sender->d_func()->connectedSignals) * 8 && !qt_signal_spy_callback_set.signal_begin_callback && !qt_signal_spy_callback_set.signal_end_callback) { - uint signal_mask = (1 << (to_signal_index + 1)) - 1; - signal_mask ^= (1 << from_signal_index) - 1; - if ((sender->d_func()->connectedSignals & signal_mask) == 0) + + uint n = (from_signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); + uint m = 1 << (from_signal_index - n * 8 * sizeof sender->d_func()->connectedSignals[0]); + uint nt = (to_signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); + uint mt = 1 << (to_signal_index - n * 8 * sizeof sender->d_func()->connectedSignals[0]); + bool connected = false; + quint32 *connectedSignals = sender->d_func()->connectedSignals; + for (uint i = 0; !connected && i < (sizeof sender->d_func()->connectedSignals + / sizeof sender->d_func()->connectedSignals[0]); ++i) { + uint mask = 0; + if (i > n) + mask = ~0u; + else if (i == n) + mask = ~(m -1); + if (i > nt) + mask = 0; + else if (i == nt) + mask &= (mt << 1) - 1; + connected = connectedSignals[i] & mask; + } + if (!connected) // nothing connected to these signals, and no spy return; } activate(sender, from_signal_index, to_signal_index, argv); } +/*! \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. +*/ +bool QMetaObject::isConnected(QObject *sender, int signal_index) { + if (signal_index < (int)sizeof(sender->d_func()->connectedSignals) * 8 + && !qt_signal_spy_callback_set.signal_begin_callback + && !qt_signal_spy_callback_set.signal_end_callback) { + uint n = (signal_index / (8 * sizeof sender->d_func()->connectedSignals[0])); + uint m = 1 << (signal_index - n * 8 * sizeof sender->d_func()->connectedSignals[0]); + if ((sender->d_func()->connectedSignals[n] & m) == 0) + // nothing connected to these signals, and no spy + return false; + } + return true; +} /***************************************************************************** Properties @@ -3829,23 +3911,17 @@ QDebug operator<<(QDebug dbg, const QObject *o) { Synonym for QList<QObject *>. */ -#ifdef QT_JAMBI_BUILD -class QDPtrAccessor : public QObject { -public: - QObjectData *d() const { return d_ptr; } -}; -#endif - void qDeleteInEventHandler(QObject *o) { #ifdef QT_JAMBI_BUILD if (!o) return; - ((QDPtrAccessor *) o)->d()->inEventHandler = false; + 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 index 1fb216b..9169a90 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -109,6 +109,7 @@ public: uint hasGuards : 1; //true iff there is one or more QPointer attached to this object uint unused : 22; int postedEvents; + QMetaObject *metaObject; // assert dynamic }; @@ -379,6 +380,9 @@ inline QList<T> qFindChildren(const QObject *o, const QRegExp &re) #endif // Q_MOC_RUN +template <class T> inline const char * qobject_interface_iid() +{ return 0; } + template <class T> inline T qobject_cast_helper(QObject *object, T) { return static_cast<T>(((T)0)->staticMetaObject.cast(object)); } @@ -395,6 +399,8 @@ inline T qobject_cast(const QObject *object) #ifndef Q_MOC_RUN # define Q_DECLARE_INTERFACE(IFace, IId) \ + template <> inline const char *qobject_interface_iid<IFace *>() \ + { return IId; } \ template <> inline IFace *qobject_cast_helper<IFace *>(QObject *object, IFace *) \ { return (IFace *)(object ? object->qt_metacast(IId) : 0); } \ template <> inline IFace *qobject_cast_helper<IFace *>(const QObject *object, IFace *) \ @@ -458,8 +464,13 @@ inline T qobject_cast(const QObject *object) } +template <class T> 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<IFace *>() \ + { return IId; } \ template <> inline IFace *qobject_cast<IFace *>(QObject *object) \ { return reinterpret_cast<IFace *>((object ? object->qt_metacast(IId) : 0)); } \ template <> inline IFace *qobject_cast<IFace *>(const QObject *object) \ diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 07c397f..056dee3 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -60,12 +60,14 @@ #include "QtCore/qvector.h" #include "QtCore/qreadwritelock.h" #include "QtCore/qvariant.h" +#include "private/qguard_p.h" QT_BEGIN_NAMESPACE class QVariant; class QThreadData; class QObjectConnectionListVector; +namespace QtSharedPointer { class ExternalRefCountData; } /* mirrored in QtTestLib, DON'T CHANGE without prior warning */ struct QSignalSpyCallbackSet @@ -81,56 +83,20 @@ void Q_CORE_EXPORT qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet extern QSignalSpyCallbackSet Q_CORE_EXPORT qt_signal_spy_callback_set; -inline QObjectData::~QObjectData() {} - enum { QObjectPrivateVersion = QT_VERSION }; +class Q_CORE_EXPORT QDeclarativeData +{ +public: + virtual ~QDeclarativeData(); + virtual void destroyed(QObject *) = 0; +}; + class Q_CORE_EXPORT QObjectPrivate : public QObjectData { Q_DECLARE_PUBLIC(QObject) public: - QObjectPrivate(int version = QObjectPrivateVersion); - virtual ~QObjectPrivate(); - -#ifdef QT3_SUPPORT - QList<QObject *> pendingChildInsertedEvents; - void sendPendingChildInsertedEvents(); - void removePendingChildInsertedEvents(QObject *child); -#else - // preserve binary compatibility with code compiled without Qt 3 support - QList<QObject *> unused; -#endif - - // id of the thread that owns the object - QThreadData *threadData; - void moveToThread_helper(); - void setThreadData_helper(QThreadData *currentData, QThreadData *targetData); - void _q_reregisterTimers(void *pointer); - - struct Sender - { - QObject *sender; - int signal; - int ref; - }; - // object currently activating the object - Sender *currentSender; - - QObject *currentChildBeingDeleted; - - bool isSender(const QObject *receiver, const char *signal) const; - QObjectList receiverList(const char *signal) const; - QObjectList senderList() const; - - QList<QPointer<QObject> > eventFilters; - - void setParent_helper(QObject *); - - void deleteChildren(); - - static void clearGuards(QObject *); - struct ExtraData { #ifndef QT_NO_USERDATA @@ -139,12 +105,7 @@ public: QList<QByteArray> propertyNames; QList<QVariant> propertyValues; }; - ExtraData *extraData; - mutable quint32 connectedSignals; - QString objectName; - - // Note: you must hold the signalSlotLock() before accessing the lists below or calling the functions struct Connection { QObject *sender; @@ -159,11 +120,34 @@ public: }; typedef QList<Connection *> ConnectionList; - QObjectConnectionListVector *connectionLists; + 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(); - Connection *senders; //linked list; +#ifdef QT3_SUPPORT + void sendPendingChildInsertedEvents(); + void removePendingChildInsertedEvents(QObject *child); +#endif static Sender *setCurrentSender(QObject *receiver, Sender *sender); @@ -172,14 +156,66 @@ public: Sender *previousSender); static int *setDeleteWatch(QObjectPrivate *d, int *newWatch); static void resetDeleteWatch(QObjectPrivate *d, int *oldWatch, int deleteWatch); - - int *deleteWatch; + static void clearGuards(QObject *); static QObjectPrivate *get(QObject *o) { return o->d_func(); } + +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]; // 64-bit, so doesn't cause padding on 64-bit platforms + +#ifdef QT3_SUPPORT + QList<QObject *> pendingChildInsertedEvents; +#else + // preserve binary compatibility with code compiled without Qt 3 support + // ### why? + QList<QObject *> unused; +#endif + + QList<QPointer<QObject> > eventFilters; + QObject *currentChildBeingDeleted; + + // these objects are all used to indicate that a QObject was deleted + // plus QPointer, which keeps a separate list + QDeclarativeData *declarativeData; + QGuard<QObject> *objectGuards; + QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount; + int *deleteWatch; }; +inline void q_guard_addGuard(QGuard<QObject> *g) +{ + QObjectPrivate *p = QObjectPrivate::get(g->o); + if (p->wasDeleted) { + qWarning("QGuard: cannot add guard to deleted object"); + g->o = 0; + return; + } + + g->next = p->objectGuards; + p->objectGuards = g; + g->prev = &p->objectGuards; + if (g->next) + g->next->prev = &g->next; +} + +inline void q_guard_removeGuard(QGuard<QObject> *g) +{ + if (g->next) g->next->prev = g->prev; + *g->prev = g->next; + g->next = 0; + g->prev = 0; +} + Q_DECLARE_TYPEINFO(QObjectPrivate::Connection, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QObjectPrivate::Sender, Q_MOVABLE_TYPE); @@ -220,6 +256,14 @@ private: 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/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 9187765..1ae46d5 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -334,6 +334,9 @@ struct Q_CORE_EXPORT QMetaObject static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); 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); + + static bool isConnected(QObject *sender, int signal_index); + // internal guarded pointers static void addGuard(QObject **ptr); static void removeGuard(QObject **ptr); @@ -428,6 +431,7 @@ struct Q_CORE_EXPORT QMetaObject }; int static_metacall(Call, int, void **) const; + static int metacall(QObject *, Call, int, void **); #ifdef QT3_SUPPORT QT3_SUPPORT const char *superClassName() const; @@ -439,6 +443,7 @@ struct Q_CORE_EXPORT QMetaObject const uint *data; const void *extradata; } d; + }; struct QMetaObjectExtraData diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp index 0004d3e..3248dcf 100644 --- a/src/corelib/statemachine/qabstracttransition.cpp +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -153,34 +153,12 @@ QAbstractTransition::QAbstractTransition(QState *sourceState) } /*! - Constructs a new QAbstractTransition object with the given \a targets and \a - sourceState. -*/ -QAbstractTransition::QAbstractTransition(const QList<QAbstractState*> &targets, - QState *sourceState) - : QObject(*new QAbstractTransitionPrivate, sourceState) -{ - setTargetStates(targets); -} - -/*! - \internal -*/ -QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, - QState *parent) - : QObject(dd, parent) -{ -} - -/*! \internal */ QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, - const QList<QAbstractState*> &targets, QState *parent) : QObject(dd, parent) { - setTargetStates(targets); } /*! diff --git a/src/corelib/statemachine/qabstracttransition.h b/src/corelib/statemachine/qabstracttransition.h index 9ba1f11..8ff3a6e 100644 --- a/src/corelib/statemachine/qabstracttransition.h +++ b/src/corelib/statemachine/qabstracttransition.h @@ -72,7 +72,6 @@ class Q_CORE_EXPORT QAbstractTransition : public QObject Q_PROPERTY(QList<QAbstractState*> targetStates READ targetStates WRITE setTargetStates) public: QAbstractTransition(QState *sourceState = 0); - QAbstractTransition(const QList<QAbstractState*> &targets, QState *sourceState = 0); virtual ~QAbstractTransition(); QState *sourceState() const; @@ -104,8 +103,6 @@ protected: protected: QAbstractTransition(QAbstractTransitionPrivate &dd, QState *parent); - QAbstractTransition(QAbstractTransitionPrivate &dd, - const QList<QAbstractState*> &targets, QState *parent); private: Q_DISABLE_COPY(QAbstractTransition) diff --git a/src/corelib/statemachine/qeventtransition.cpp b/src/corelib/statemachine/qeventtransition.cpp index 3933981..813c960 100644 --- a/src/corelib/statemachine/qeventtransition.cpp +++ b/src/corelib/statemachine/qeventtransition.cpp @@ -153,22 +153,6 @@ QEventTransition::QEventTransition(QObject *object, QEvent::Type type, } /*! - Constructs a new QEventTransition object associated with events of the given - \a type for the given \a object. The transition has the given \a targets and - \a sourceState. -*/ -QEventTransition::QEventTransition(QObject *object, QEvent::Type type, - const QList<QAbstractState*> &targets, - QState *sourceState) - : QAbstractTransition(*new QEventTransitionPrivate, targets, sourceState) -{ - Q_D(QEventTransition); - d->registered = false; - d->object = object; - d->eventType = type; -} - -/*! \internal */ QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QState *parent) @@ -190,20 +174,6 @@ QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QObject *object, } /*! - \internal -*/ -QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QObject *object, - QEvent::Type type, const QList<QAbstractState*> &targets, - QState *parent) - : QAbstractTransition(dd, targets, parent) -{ - Q_D(QEventTransition); - d->registered = false; - d->object = object; - d->eventType = type; -} - -/*! Destroys this QObject event transition. */ QEventTransition::~QEventTransition() diff --git a/src/corelib/statemachine/qeventtransition.h b/src/corelib/statemachine/qeventtransition.h index b05ffef..0ebca19 100644 --- a/src/corelib/statemachine/qeventtransition.h +++ b/src/corelib/statemachine/qeventtransition.h @@ -62,8 +62,6 @@ class Q_CORE_EXPORT QEventTransition : public QAbstractTransition public: QEventTransition(QState *sourceState = 0); QEventTransition(QObject *object, QEvent::Type type, QState *sourceState = 0); - QEventTransition(QObject *object, QEvent::Type type, - const QList<QAbstractState*> &targets, QState *sourceState = 0); ~QEventTransition(); QObject *eventObject() const; @@ -82,9 +80,6 @@ protected: QEventTransition(QEventTransitionPrivate &dd, QState *parent); QEventTransition(QEventTransitionPrivate &dd, QObject *object, QEvent::Type type, QState *parent); - QEventTransition(QEventTransitionPrivate &dd, QObject *object, - QEvent::Type type, const QList<QAbstractState*> &targets, - QState *parent); private: Q_DISABLE_COPY(QEventTransition) diff --git a/src/corelib/statemachine/qsignalevent.h b/src/corelib/statemachine/qsignalevent.h index c09c5a3..b7ca61f 100644 --- a/src/corelib/statemachine/qsignalevent.h +++ b/src/corelib/statemachine/qsignalevent.h @@ -58,16 +58,16 @@ QT_MODULE(Core) class Q_CORE_EXPORT QSignalEvent : public QEvent { public: - QSignalEvent(const QObject *sender, int signalIndex, + QSignalEvent(QObject *sender, int signalIndex, const QList<QVariant> &arguments); ~QSignalEvent(); - inline const QObject *sender() const { return m_sender; } + inline QObject *sender() const { return m_sender; } inline int signalIndex() const { return m_signalIndex; } inline QList<QVariant> arguments() const { return m_arguments; } private: - const QObject *m_sender; + QObject *m_sender; int m_signalIndex; QList<QVariant> m_arguments; }; diff --git a/src/corelib/statemachine/qsignaltransition.cpp b/src/corelib/statemachine/qsignaltransition.cpp index 389e513..7814699 100644 --- a/src/corelib/statemachine/qsignaltransition.cpp +++ b/src/corelib/statemachine/qsignaltransition.cpp @@ -159,21 +159,6 @@ QSignalTransition::QSignalTransition(QObject *sender, const char *signal, } /*! - Constructs a new signal transition associated with the given \a signal of - the given \a sender. The transition has the given \a targets and \a - sourceState. -*/ -QSignalTransition::QSignalTransition(QObject *sender, const char *signal, - const QList<QAbstractState*> &targets, - QState *sourceState) - : QAbstractTransition(*new QSignalTransitionPrivate, targets, sourceState) -{ - Q_D(QSignalTransition); - d->sender = sender; - d->signal = signal; -} - -/*! Destroys this signal transition. */ QSignalTransition::~QSignalTransition() diff --git a/src/corelib/statemachine/qsignaltransition.h b/src/corelib/statemachine/qsignaltransition.h index 69060ae..415751e 100644 --- a/src/corelib/statemachine/qsignaltransition.h +++ b/src/corelib/statemachine/qsignaltransition.h @@ -62,9 +62,6 @@ public: QSignalTransition(QState *sourceState = 0); QSignalTransition(QObject *sender, const char *signal, QState *sourceState = 0); - QSignalTransition(QObject *sender, const char *signal, - const QList<QAbstractState*> &targets, - QState *sourceState = 0); ~QSignalTransition(); QObject *senderObject() const; diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp index 2042288..09d0be0 100644 --- a/src/corelib/statemachine/qstate.cpp +++ b/src/corelib/statemachine/qstate.cpp @@ -339,7 +339,8 @@ QSignalTransition *QState::addTransition(QObject *sender, const char *signal, return 0; } } - QSignalTransition *trans = new QSignalTransition(sender, signal, QList<QAbstractState*>() << target); + QSignalTransition *trans = new QSignalTransition(sender, signal); + trans->setTargetState(target); addTransition(trans); return trans; } @@ -351,7 +352,8 @@ class UnconditionalTransition : public QAbstractTransition { public: UnconditionalTransition(QAbstractState *target) - : QAbstractTransition(QList<QAbstractState*>() << target) {} + : QAbstractTransition() + { setTargetState(target); } protected: void onTransition(QEvent *) {} bool eventTest(QEvent *) { return true; } diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index a02480b..9cb1d4d 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -205,6 +205,7 @@ This is QStateMachinePrivate::QStateMachinePrivate() { state = NotRunning; + _startState = 0; processing = false; processingScheduled = false; stop = false; @@ -1138,7 +1139,8 @@ class InitialTransition : public QAbstractTransition { public: InitialTransition(QAbstractState *target) - : QAbstractTransition(QList<QAbstractState*>() << target) {} + : QAbstractTransition() + { setTargetState(target); } protected: virtual bool eventTest(QEvent *) { return true; } virtual void onTransition(QEvent *) {} @@ -1146,6 +1148,20 @@ protected: } // namespace +QState *QStateMachinePrivate::startState() +{ + Q_Q(QStateMachine); + if (_startState == 0) + _startState = new StartState(q); + return _startState; +} + +void QStateMachinePrivate::removeStartState() +{ + delete _startState; + _startState = 0; +} + void QStateMachinePrivate::_q_start() { Q_Q(QStateMachine); @@ -1165,11 +1181,19 @@ void QStateMachinePrivate::_q_start() processingScheduled = true; // we call _q_process() below emit q->started(); - StartState *start = new StartState(rootState()); - QAbstractTransition *initialTransition = new InitialTransition(initial); - start->addTransition(initialTransition); - QList<QAbstractTransition*> transitions; - transitions.append(initialTransition); + QState *start = startState(); + Q_ASSERT(start != 0); + + QList<QAbstractTransition*> transitions = QStatePrivate::get(start)->transitions(); + + // If a transition has already been added, then we skip this step, as the + // initial transition in that case has been overridden. + if (transitions.isEmpty()) { + QAbstractTransition *initialTransition = new InitialTransition(initial); + start->addTransition(initialTransition); + transitions.append(initialTransition); + } + QEvent nullEvent(QEvent::None); executeTransitionContent(&nullEvent, transitions); QList<QAbstractState*> enteredStates = enterStates(&nullEvent, transitions); @@ -1177,7 +1201,7 @@ void QStateMachinePrivate::_q_start() applyProperties(transitions, QList<QAbstractState*>() << start, enteredStates); #endif - delete start; + removeStartState(); #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": initial configuration:" << configuration; @@ -1277,6 +1301,68 @@ void QStateMachinePrivate::scheduleProcess() QMetaObject::invokeMethod(q_func(), "_q_process", Qt::QueuedConnection); } +namespace { + +class GoToStateTransition : public QAbstractTransition +{ +public: + GoToStateTransition(QAbstractState *target) + : QAbstractTransition() + { setTargetState(target); } +protected: + void onTransition(QEvent *) { deleteLater(); } + bool eventTest(QEvent *) { return true; } +}; + +} // namespace + +/*! + \internal + + Causes this state machine to unconditionally transition to the given + \a targetState. + + Provides a backdoor for using the state machine "imperatively"; i.e. rather + than defining explicit transitions, you drive the machine's execution by + calling this function. It breaks the whole integrity of the + transition-driven model, but is provided for pragmatic reasons. +*/ +void QStateMachinePrivate::goToState(QAbstractState *targetState) +{ + if (!targetState) { + qWarning("QStateMachine::goToState(): cannot go to null state"); + return; + } + + if (configuration.contains(targetState)) + return; + + QState *sourceState = 0; + if (state == Running) { + QSet<QAbstractState*>::const_iterator it; + for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) { + sourceState = qobject_cast<QState*>(*it); + if (sourceState != 0) + break; + } + } else { + sourceState = startState(); + } + + Q_ASSERT(sourceState != 0); + // Reuse previous GoToStateTransition in case of several calls to + // goToState() in a row. + GoToStateTransition *trans = qFindChild<GoToStateTransition*>(sourceState); + if (!trans) { + trans = new GoToStateTransition(targetState); + sourceState->addTransition(trans); + } else { + trans->setTargetState(targetState); + } + + scheduleProcess(); +} + void QStateMachinePrivate::registerTransitions(QAbstractState *state) { QState *group = qobject_cast<QState*>(state); @@ -1446,7 +1532,7 @@ void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transitio } #endif -void QStateMachinePrivate::handleTransitionSignal(const QObject *sender, int signalIndex, +void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalIndex, void **argv) { Q_ASSERT(connections[sender].at(signalIndex) != 0); @@ -2026,7 +2112,7 @@ QSignalEventGenerator::QSignalEventGenerator(QStateMachine *parent) Constructs a new QSignalEvent object with the given \a sender, \a signalIndex and \a arguments. */ -QSignalEvent::QSignalEvent(const QObject *sender, int signalIndex, +QSignalEvent::QSignalEvent(QObject *sender, int signalIndex, const QList<QVariant> &arguments) : QEvent(QEvent::Signal), m_sender(sender), m_signalIndex(signalIndex), m_arguments(arguments) diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index cae21aa..f0f74d6 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -79,7 +79,7 @@ class QAbstractAnimation; #endif class QStateMachine; -class QStateMachinePrivate : public QStatePrivate +class Q_CORE_EXPORT QStateMachinePrivate : public QStatePrivate { Q_DECLARE_PUBLIC(QStateMachine) public: @@ -116,6 +116,9 @@ public: QState *rootState() const; + QState *startState(); + void removeStartState(); + void microstep(QEvent *event, const QList<QAbstractTransition*> &transitionList); bool isPreempted(const QAbstractState *s, const QSet<QAbstractTransition*> &transitions) const; QSet<QAbstractTransition*> selectTransitions(QEvent *event) const; @@ -138,6 +141,8 @@ public: static bool isDescendantOf(const QAbstractState *s, const QAbstractState *other); static QList<QState*> properAncestors(const QAbstractState *s, const QState *upperBound); + void goToState(QAbstractState *targetState); + void registerTransitions(QAbstractState *state); void registerSignalTransition(QSignalTransition *transition); void unregisterSignalTransition(QSignalTransition *transition); @@ -147,7 +152,7 @@ public: #endif void unregisterTransition(QAbstractTransition *transition); void unregisterAllTransitions(); - void handleTransitionSignal(const QObject *sender, int signalIndex, + void handleTransitionSignal(QObject *sender, int signalIndex, void **args); void scheduleProcess(); @@ -162,6 +167,7 @@ public: #endif State state; + QState *_startState; bool processing; bool processingScheduled; bool stop; @@ -208,7 +214,7 @@ public: f_cloneEvent cloneEvent; }; - static Q_CORE_EXPORT const Handler *handler; + static const Handler *handler; }; QT_END_NAMESPACE diff --git a/src/corelib/tools/qbytedata_p.h b/src/corelib/tools/qbytedata_p.h index cc10ea2..f3724f6 100644 --- a/src/corelib/tools/qbytedata_p.h +++ b/src/corelib/tools/qbytedata_p.h @@ -42,8 +42,18 @@ #ifndef QBYTEDATA_H #define QBYTEDATA_H -#include <qbytearray.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 <qbytearray.h> QT_BEGIN_NAMESPACE diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h index 0020d22..7221925 100644 --- a/src/corelib/tools/qcontiguouscache.h +++ b/src/corelib/tools/qcontiguouscache.h @@ -44,6 +44,7 @@ #include <QtCore/qatomic.h> #include <limits.h> +#include <new> QT_BEGIN_HEADER @@ -76,6 +77,12 @@ struct QContiguousCacheTypedData int start; int offset; uint sharable : 1; + // uint unused : 31; + + // total is 24 bytes (HP-UX aCC: 40 bytes) + // the next entry is already aligned to 8 bytes + // there will be an 8 byte gap here if T requires 16-byte alignment + // (such as long double on 64-bit platforms, __int128, __float128) T array[1]; }; diff --git a/src/corelib/tools/qshareddata.cpp b/src/corelib/tools/qshareddata.cpp index 3cd37a7..6cd220c 100644 --- a/src/corelib/tools/qshareddata.cpp +++ b/src/corelib/tools/qshareddata.cpp @@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE QSharedData is designed to be used with QSharedDataPointer or QExplicitlySharedDataPointer to implement custom \l{implicitly - shared} or \l {explicitly shared} classes. QSharedData provides + shared} or explicitly shared classes. QSharedData provides \l{thread-safe} reference counting. See QSharedDataPointer and QExplicitlySharedDataPointer for details. diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp index 59dfffe..f18dee8 100644 --- a/src/corelib/tools/qsharedpointer.cpp +++ b/src/corelib/tools/qsharedpointer.cpp @@ -128,7 +128,7 @@ To access the pointer that QWeakPointer is tracking, you must first create a QSharedPointer object and verify if the pointer - is null or not. + is null or not. See QWeakPointer::toStrongRef() for more information. \sa QSharedPointer */ @@ -210,6 +210,8 @@ If \tt T is a derived type of the template parameter of this class, QSharedPointer will perform an automatic cast. Otherwise, you will get a compiler error. + + \sa QWeakPointer::toStrongRef() */ /*! @@ -362,6 +364,8 @@ Returns a weak reference object that shares the pointer referenced by this object. + + \sa QWeakPointer::QWeakPointer() */ /*! @@ -478,10 +482,78 @@ */ /*! + \fn T *QWeakPointer::data() const + \since 4.6 + + Returns the value of the pointer being tracked by this QWeakPointer, + \b without ensuring that it cannot get deleted. To have that guarantee, + use toStrongRef(), which returns a QSharedPointer object. If this + function can determine that the pointer has already been deleted, it + returns 0. + + It is ok to obtain the value of the pointer and using that value itself, + like for example in debugging statements: + + \code + qDebug("Tracking %p", weakref.data()); + \endcode + + However, dereferencing the pointer is only allowed if you can guarantee + by external means that the pointer does not get deleted. For example, + if you can be certain that no other thread can delete it, nor the + functions that you may call. + + If that is the case, then the following code is valid: + + \code + // this pointer cannot be used in another thread + // so other threads cannot delete it + QWeakPointer<int> weakref = obtainReference(); + + Object *obj = weakref.data(); + if (obj) { + // if the pointer wasn't deleted yet, we know it can't get + // deleted by our own code here nor the functions we call + otherFunction(obj); + } + \endcode + + Use this function with care. + + \sa isNull(), toStrongRef() +*/ + +/*! \fn QSharedPointer<T> QWeakPointer::toStrongRef() const Promotes this weak reference to a strong one and returns a - QSharedPointer object holding that reference. + QSharedPointer object holding that reference. When promoting to + QSharedPointer, this function verifies if the object has been deleted + already or not. If it hasn't, this function increases the reference + count to the shared object, thus ensuring that it will not get + deleted. + + Since this function can fail to obtain a valid strong reference to the + shared object, you should always verify if the conversion succeeded, + by calling QSharedPointer::isNull() on the returned object. + + For example, the following code promotes a QWeakPointer that was held + to a strong reference and, if it succeeded, it prints the value of the + integer that was held: + + \code + QWeakPointer<int> weakref; + + // ... + + QSharedPointer<int> strong = weakref.toStrongRef(); + if (strong) + qDebug() << "The value is:" << *strong; + else + qDebug() << "The value has already been deleted"; + \endcode + + \sa QSharedPointer::QSharedPointer() */ /*! @@ -792,6 +864,56 @@ #include <qset.h> #include <qmutex.h> +#if !defined(QT_NO_QOBJECT) +#include "../kernel/qobject_p.h" + +/*! + \internal + This function is called for a just-created QObject \a obj, to enable + the use of QSharedPointer and QWeakPointer. + + When QSharedPointer is active in a QObject, the object must not be deleted + directly: the lifetime is managed by the QSharedPointer object. In that case, + the deleteLater() and parent-child relationship in QObject only decrease + the strong reference count, instead of deleting the object. +*/ +void QtSharedPointer::ExternalRefCountData::setQObjectShared(const QObject *obj, bool) +{ + Q_ASSERT(obj); + QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj)); + + if (d->sharedRefcount) + qFatal("QSharedPointer: pointer %p already has reference counting", obj); + d->sharedRefcount = this; + + // QObject decreases the refcount too, so increase it up + weakref.ref(); +} + +QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::getAndRef(const QObject *obj) +{ + Q_ASSERT(obj); + QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj)); + ExternalRefCountData *that = d->sharedRefcount; + if (that) { + that->weakref.ref(); + return that; + } + + // we can create the refcount data because it doesn't exist + ExternalRefCountData *x = new ExternalRefCountData(Qt::Uninitialized); + x->strongref = -1; + x->weakref = 2; // the QWeakPointer that called us plus the QObject itself + if (!d->sharedRefcount.testAndSetRelease(0, x)) { + delete x; + d->sharedRefcount->weakref.ref(); + } + return d->sharedRefcount; +} +#endif + + + #if !defined(QT_NO_MEMBER_TEMPLATES) //# define QT_SHARED_POINTER_BACKTRACE_SUPPORT @@ -897,6 +1019,7 @@ QT_BEGIN_NAMESPACE namespace QtSharedPointer { Q_CORE_EXPORT void internalSafetyCheckAdd(const volatile void *); Q_CORE_EXPORT void internalSafetyCheckRemove(const volatile void *); + Q_AUTOTEST_EXPORT void internalSafetyCheckCleanCheck(); } /*! @@ -961,6 +1084,7 @@ void QtSharedPointer::internalSafetyCheckAdd2(const void *d_ptr, const volatile kp->dPointers.insert(d_ptr, data); kp->dataPointers.insert(ptr, d_ptr); + Q_ASSERT(kp->dPointers.size() == kp->dataPointers.size()); } /*! @@ -985,10 +1109,29 @@ void QtSharedPointer::internalSafetyCheckRemove2(const void *d_ptr) Q_ASSERT(it2 != kp->dataPointers.end()); //qDebug("Removing d=%p value=%p", d_ptr, it->pointer); - + // remove entries kp->dataPointers.erase(it2); kp->dPointers.erase(it); + Q_ASSERT(kp->dPointers.size() == kp->dataPointers.size()); +} + +/*! + \internal + Called by the QSharedPointer autotest +*/ +void QtSharedPointer::internalSafetyCheckCleanCheck() +{ +# ifdef QT_BUILD_INTERNAL + KnownPointers *const kp = knownPointers(); + Q_ASSERT_X(kp, "internalSafetyCheckSelfCheck()", "Called after global statics deletion!"); + + if (kp->dPointers.size() != kp->dataPointers.size()) + qFatal("Internal consistency error: the number of pointers is not equal!"); + + if (!kp->dPointers.isEmpty()) + qFatal("Pointer cleaning failed: %d entries remaining", kp->dPointers.size()); +# endif } QT_END_NAMESPACE diff --git a/src/corelib/tools/qsharedpointer.h b/src/corelib/tools/qsharedpointer.h index abd83ad..2f86ce7 100644 --- a/src/corelib/tools/qsharedpointer.h +++ b/src/corelib/tools/qsharedpointer.h @@ -115,6 +115,7 @@ public: QWeakPointer<T> operator=(const QWeakPointer<T> &other); QWeakPointer<T> operator=(const QSharedPointer<T> &other); + T *data() const; void clear(); QSharedPointer<T> toStrongRef() const; diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index b8f4139..8a34362 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -94,26 +94,27 @@ namespace QtSharedPointer { template <class T> class InternalRefCount; template <class T> class ExternalRefCount; - template <class X, class T> QSharedPointer<X> strongRefFromWeakHelper(const QWeakPointer<T> &, X*); template <class X, class Y> QSharedPointer<X> copyAndSetPointer(X * ptr, const QSharedPointer<Y> &src); // used in debug mode to verify the reuse of pointers Q_CORE_EXPORT void internalSafetyCheckAdd2(const void *, const volatile void *); Q_CORE_EXPORT void internalSafetyCheckRemove2(const void *); - + template <class T, typename Klass, typename RetVal> inline void executeDeleter(T *t, RetVal (Klass:: *memberDeleter)()) { (t->*memberDeleter)(); } template <class T, typename Deleter> inline void executeDeleter(T *t, Deleter d) { d(t); } + template <class T> inline void normalDeleter(T *t) { delete t; } + + // this uses partial template specialization + // the only compilers that didn't support this were MSVC 6.0 and 2002 + template <class T> struct RemovePointer; + template <class T> struct RemovePointer<T *> { typedef T Type; }; + template <class T> struct RemovePointer<QSharedPointer<T> > { typedef T Type; }; + template <class T> struct RemovePointer<QWeakPointer<T> > { typedef T Type; }; - // - // Depending on its template parameter, QSharedPointer derives from either - // QtSharedPointer::InternalRefCount or from QtSharedPointer::ExternalRefCount. - // Both of these classes derive from QtSharedPointer::Basic, which provides common - // operations, - // template <class T> class Basic { @@ -129,21 +130,10 @@ namespace QtSharedPointer { inline T *operator->() const { return data(); } protected: - inline Basic() : value(0) { } + inline Basic(T *ptr = 0) : value(ptr) { } + inline Basic(Qt::Initialization) { } // ~Basic(); - inline void verifyReconstruction(const T *ptr) - { - Q_ASSERT_X(!ptr || value != ptr, "QSharedPointer", - "QSharedPointer violation: you cannot create two QSharedPointer objects " - "from the same pointer"); - - // make use of the "ptr" variable in the no-op statement below - // since this function is in a public header, we don't - // want warnings on "unused variables" to show up everywhere - ptr = 0; - } - inline void internalConstruct(T *ptr) { value = ptr; @@ -160,14 +150,26 @@ namespace QtSharedPointer { struct ExternalRefCountData { - QAtomicInt weakref; - QAtomicInt strongref; + QBasicAtomicInt weakref; + QBasicAtomicInt strongref; - inline ExternalRefCountData() : weakref(1), strongref(1) { } - virtual inline ~ExternalRefCountData() { Q_ASSERT(!weakref); Q_ASSERT(!strongref); } + inline ExternalRefCountData() + { + QBasicAtomicInt proto = Q_BASIC_ATOMIC_INITIALIZER(1); + weakref = strongref = proto; + } + inline ExternalRefCountData(Qt::Initialization) { } + virtual inline ~ExternalRefCountData() { Q_ASSERT(!weakref); Q_ASSERT(strongref <= 0); } virtual inline bool destroy() { return false; } + +#ifndef QT_NO_QOBJECT + Q_CORE_EXPORT static ExternalRefCountData *getAndRef(const QObject *); + Q_CORE_EXPORT void setQObjectShared(const QObject *, bool enable); +#endif + inline void setQObjectShared(...) { } }; + // sizeof(ExternalRefCount) = 12 (32-bit) / 16 (64-bit) template <class T, typename Deleter> struct CustomDeleter @@ -177,6 +179,9 @@ namespace QtSharedPointer { inline CustomDeleter(T *p, Deleter d) : deleter(d), ptr(p) {} }; + // sizeof(CustomDeleter) = sizeof(Deleter) + sizeof(void*) + // for Deleter = function pointer: 8 (32-bit) / 16 (64-bit) + // for Deleter = PMF: 12 (32-bit) / 24 (64-bit) (GCC) struct ExternalRefCountWithDestroyFn: public ExternalRefCountData { @@ -190,6 +195,7 @@ namespace QtSharedPointer { inline bool destroy() { destroyer(this); return true; } inline void operator delete(void *ptr) { ::operator delete(ptr); } }; + // sizeof(ExternalRefCountWithDestroyFn) = 16 (32-bit) / 24 (64-bit) template <class T, typename Deleter> struct ExternalRefCountWithCustomDeleter: public ExternalRefCountWithDestroyFn @@ -203,11 +209,23 @@ namespace QtSharedPointer { { Self *realself = static_cast<Self *>(self); executeDeleter(realself->extra.ptr, realself->extra.deleter); + + // delete the deleter too + realself->extra.~Next(); + } + static void safetyCheckDeleter(ExternalRefCountData *self) + { + internalSafetyCheckRemove2(self); + deleter(self); } static inline Self *create(T *ptr, Deleter userDeleter) { +# ifdef QT_SHAREDPOINTER_TRACK_POINTERS + DestroyerFn destroy = &safetyCheckDeleter; +# else DestroyerFn destroy = &deleter; +# endif Self *d = static_cast<Self *>(::operator new(sizeof(Self))); // initialize the two sub-objects @@ -234,10 +252,19 @@ namespace QtSharedPointer { static_cast<ExternalRefCountWithContiguousData *>(self); that->data.~T(); } + static void safetyCheckDeleter(ExternalRefCountData *self) + { + internalSafetyCheckRemove2(self); + deleter(self); + } static inline ExternalRefCountData *create(T **ptr) { +# ifdef QT_SHAREDPOINTER_TRACK_POINTERS + DestroyerFn destroy = &safetyCheckDeleter; +# else DestroyerFn destroy = &deleter; +# endif ExternalRefCountWithContiguousData *d = static_cast<ExternalRefCountWithContiguousData *>(::operator new(sizeof(ExternalRefCountWithContiguousData))); @@ -258,9 +285,9 @@ namespace QtSharedPointer { template <class T> class ExternalRefCount: public Basic<T> { - typedef ExternalRefCountData Data; - typedef void (*DeleterFunction)(T *); protected: + typedef ExternalRefCountData Data; + inline void ref() const { d->weakref.ref(); d->strongref.ref(); } inline bool deref() { @@ -272,42 +299,51 @@ namespace QtSharedPointer { inline void internalConstruct(T *ptr) { - Basic<T>::internalConstruct(ptr); - Q_ASSERT(!d); +#ifdef QT_SHAREDPOINTER_TRACK_POINTERS + internalConstruct<void (*)(T *)>(ptr, normalDeleter); +#else if (ptr) d = new Data; -#ifdef QT_SHAREDPOINTER_TRACK_POINTERS - if (ptr) internalSafetyCheckAdd2(d, ptr); + else + d = 0; + internalFinishConstruction(ptr); #endif } template <typename Deleter> inline void internalConstruct(T *ptr, Deleter deleter) { - Basic<T>::internalConstruct(ptr); - Q_ASSERT(!d); if (ptr) d = ExternalRefCountWithCustomDeleter<T, Deleter>::create(ptr, deleter); -#ifdef QT_SHAREDPOINTER_TRACK_POINTERS - if (ptr) internalSafetyCheckAdd2(d, ptr); -#endif + else + d = 0; + internalFinishConstruction(ptr); } inline void internalCreate() { T *ptr; d = ExternalRefCountWithContiguousData<T>::create(&ptr); + Basic<T>::internalConstruct(ptr); + } + inline void internalFinishConstruction(T *ptr) + { Basic<T>::internalConstruct(ptr); + if (ptr) d->setQObjectShared(ptr, true); #ifdef QT_SHAREDPOINTER_TRACK_POINTERS if (ptr) internalSafetyCheckAdd2(d, ptr); #endif } inline ExternalRefCount() : d(0) { } - inline ~ExternalRefCount() { if (d && !deref()) delete d; } + inline ExternalRefCount(Qt::Initialization i) : Basic<T>(i) { } inline ExternalRefCount(const ExternalRefCount<T> &other) : Basic<T>(other), d(other.d) { if (d) ref(); } + template <class X> + inline ExternalRefCount(const ExternalRefCount<X> &other) : Basic<T>(other.value), d(other.d) + { if (d) ref(); } + inline ~ExternalRefCount() { if (d && !deref()) delete d; } template <class X> inline void internalCopy(const ExternalRefCount<X> &other) @@ -317,32 +353,35 @@ namespace QtSharedPointer { inline void internalDestroy() { -#ifdef QT_SHAREDPOINTER_TRACK_POINTERS - internalSafetyCheckRemove2(d); -#endif if (!d->destroy()) delete this->value; } - private: #if defined(Q_NO_TEMPLATE_FRIENDS) public: #else template <class X> friend class ExternalRefCount; template <class X> friend class QWeakPointer; template <class X, class Y> friend QSharedPointer<X> copyAndSetPointer(X * ptr, const QSharedPointer<Y> &src); - template <class X, class Y> friend QSharedPointer<X> QtSharedPointer::strongRefFromWeakHelper(const QWeakPointer<Y> &src, X *); #endif inline void internalSet(Data *o, T *actual) { - if (d == o) return; - if (o && !o->strongref) - o = 0; if (o) { - verifyReconstruction(actual); - o->weakref.ref(); - o->strongref.ref(); + // increase the strongref, but never up from zero + // or less (-1 is used by QWeakPointer on untracked QObject) + register int tmp = o->strongref; + while (tmp > 0) { + // try to increment from "tmp" to "tmp + 1" + if (o->strongref.testAndSetRelaxed(tmp, tmp + 1)) + break; // succeeded + tmp = o->strongref; // failed, try again + } + + if (tmp > 0) + o->weakref.ref(); + else + o = 0; } if (d && !deref()) delete d; @@ -350,9 +389,6 @@ namespace QtSharedPointer { this->value = d && d->strongref ? actual : 0; } -#if defined(QT_BUILD_INTERNAL) - public: -#endif Data *d; private: @@ -368,7 +404,8 @@ public: inline QSharedPointer() { } // inline ~QSharedPointer() { } - inline explicit QSharedPointer(T *ptr) { internalConstruct(ptr); } + inline explicit QSharedPointer(T *ptr) : BaseClass(Qt::Uninitialized) + { internalConstruct(ptr); } template <typename Deleter> inline QSharedPointer(T *ptr, Deleter d) { internalConstruct(ptr, d); } @@ -380,13 +417,9 @@ public: return *this; } - inline QSharedPointer(const QWeakPointer<T> &other) - { *this = QtSharedPointer::strongRefFromWeakHelper(other, static_cast<T*>(0)); } - inline QSharedPointer<T> &operator=(const QWeakPointer<T> &other) - { *this = QtSharedPointer::strongRefFromWeakHelper(other, static_cast<T*>(0)); return *this; } - template <class X> - inline QSharedPointer(const QSharedPointer<X> &other) { *this = other; } + inline QSharedPointer(const QSharedPointer<X> &other) : BaseClass(other) + { } template <class X> inline QSharedPointer<T> &operator=(const QSharedPointer<X> &other) @@ -397,12 +430,12 @@ public: } template <class X> - inline QSharedPointer(const QWeakPointer<X> &other) - { *this = QtSharedPointer::strongRefFromWeakHelper(other, static_cast<T *>(0)); } + inline QSharedPointer(const QWeakPointer<X> &other) : BaseClass(Qt::Uninitialized) + { this->d = 0; *this = other; } template <class X> inline QSharedPointer<T> &operator=(const QWeakPointer<X> &other) - { *this = strongRefFromWeakHelper(other, static_cast<T *>(0)); return *this; } + { internalSet(other.d, other.value); return *this; } template <class X> QSharedPointer<X> staticCast() const @@ -434,14 +467,18 @@ public: QWeakPointer<T> toWeakRef() const; +protected: + inline QSharedPointer(Qt::Initialization i) : BaseClass(i) {} + public: static inline QSharedPointer<T> create() { - QSharedPointer<T> result; + QSharedPointer<T> result(Qt::Uninitialized); result.internalCreate(); // now initialize the data new (result.data()) T(); + result.internalFinishConstruction(result.data()); return result; } }; @@ -456,9 +493,19 @@ public: inline bool isNull() const { return d == 0 || d->strongref == 0 || value == 0; } inline operator RestrictedBool() const { return isNull() ? 0 : &QWeakPointer::value; } inline bool operator !() const { return isNull(); } + inline T *data() const { return d == 0 || d->strongref == 0 ? 0 : value; } inline QWeakPointer() : d(0), value(0) { } inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; } + + // special constructor that is enabled only if X derives from QObject + template <class X> + inline QWeakPointer(X *ptr) : d(ptr ? d->getAndRef(ptr) : 0), value(ptr) + { } + template <class X> + inline QWeakPointer &operator=(X *ptr) + { return *this = QWeakPointer(ptr); } + inline QWeakPointer(const QWeakPointer<T> &o) : d(o.d), value(o.value) { if (d) d->weakref.ref(); } inline QWeakPointer<T> &operator=(const QWeakPointer<T> &o) @@ -510,7 +557,7 @@ public: template <class X> inline bool operator==(const QSharedPointer<X> &o) const - { return d == o.d && value == static_cast<const T *>(o.data()); } + { return d == o.d; } template <class X> inline bool operator!=(const QSharedPointer<X> &o) const @@ -525,7 +572,7 @@ private: #if defined(Q_NO_TEMPLATE_FRIENDS) public: #else - template <class X, class Y> friend QSharedPointer<X> QtSharedPointer::strongRefFromWeakHelper(const QWeakPointer<Y> &src, X *); + template <class X> friend class QSharedPointer; #endif inline void internalSet(Data *o, T *actual) @@ -602,14 +649,6 @@ namespace QtSharedPointer { result.internalSet(src.d, ptr); return result; } - template <class X, class T> - Q_INLINE_TEMPLATE QSharedPointer<X> strongRefFromWeakHelper - (const QT_PREPEND_NAMESPACE(QWeakPointer<T>) &src, X *) - { - QSharedPointer<X> result; - result.internalSet(src.d, src.value); - return result; - } } // cast operators @@ -669,14 +708,6 @@ Q_INLINE_TEMPLATE QSharedPointer<X> qSharedPointerObjectCast(const QWeakPointer< return qSharedPointerObjectCast<X>(src.toStrongRef()); } -# ifndef QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION -namespace QtSharedPointer { - template <class T> struct RemovePointer; - template <class T> struct RemovePointer<T *> { typedef T Type; }; - template <class T> struct RemovePointer<QSharedPointer<T> > { typedef T Type; }; - template <class T> struct RemovePointer<QWeakPointer<T> > { typedef T Type; }; -} - template <class X, class T> inline QSharedPointer<typename QtSharedPointer::RemovePointer<X>::Type> qobject_cast(const QSharedPointer<T> &src) @@ -689,7 +720,6 @@ qobject_cast(const QWeakPointer<T> &src) { return qSharedPointerObjectCast<typename QtSharedPointer::RemovePointer<X>::Type, T>(src); } -# endif #endif diff --git a/src/gui/accessible/accessible.pri b/src/gui/accessible/accessible.pri index 76b6687..ad2fb4c 100644 --- a/src/gui/accessible/accessible.pri +++ b/src/gui/accessible/accessible.pri @@ -14,7 +14,8 @@ contains(QT_CONFIG, accessibility) { mac:!embedded { HEADERS += accessible/qaccessible_mac_p.h - OBJECTIVE_SOURCES += accessible/qaccessible_mac.mm + OBJECTIVE_SOURCES += accessible/qaccessible_mac.mm \ + accessible/qaccessible_mac_cocoa.mm } else:win32 { SOURCES += accessible/qaccessible_win.cpp } else { diff --git a/src/gui/accessible/qaccessible_mac_cocoa.mm b/src/gui/accessible/qaccessible_mac_cocoa.mm index e69de29..1cb2ffa 100644 --- a/src/gui/accessible/qaccessible_mac_cocoa.mm +++ b/src/gui/accessible/qaccessible_mac_cocoa.mm @@ -0,0 +1,234 @@ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaccessible.h" +#include "qaccessible_mac_p.h" +#include "qdebug.h" +#include "qtabwidget.h" + +#include <private/qt_mac_p.h> +#include <private/qcocoaview_mac_p.h> +#include <private/qwidget_p.h> + + +#ifndef QT_NO_ACCESSIBILITY + +#ifdef QT_MAC_USE_COCOA + +QT_BEGIN_NAMESPACE + +//#define MAC_ACCESSIBILTY_DEVELOPER_MODE + +#ifdef MAC_ACCESSIBILTY_DEVELOPER_MODE +#define MAC_ACCESSIBILTY_DEBUG qDebug +#else +#define MAC_ACCESSIBILTY_DEBUG if (0) qDebug +#endif + +typedef QMap<QAccessible::Role, NSString *> QMacAccessibiltyRoleMap; +Q_GLOBAL_STATIC(QMacAccessibiltyRoleMap, qMacAccessibiltyRoleMap); + +static QAInterface interfaceForView(QT_MANGLE_NAMESPACE(QCocoaView) *view) +{ + return QAInterface(QAccessible::queryAccessibleInterface([view qt_qwidget])); +} + +/* + Set up mappings from Qt accessibilty roles to Mac accessibilty roles. +*/ +static void populateRoleMap() +{ + QMacAccessibiltyRoleMap &roleMap = *qMacAccessibiltyRoleMap(); + roleMap[QAccessible::MenuItem] = NSAccessibilityMenuItemRole; + roleMap[QAccessible::MenuBar] = NSAccessibilityMenuBarRole; + roleMap[QAccessible::ScrollBar] = NSAccessibilityScrollBarRole; + roleMap[QAccessible::Grip] = NSAccessibilityGrowAreaRole; + roleMap[QAccessible::Window] = NSAccessibilityWindowRole; + roleMap[QAccessible::Dialog] = NSAccessibilityWindowRole; + roleMap[QAccessible::AlertMessage] = NSAccessibilityWindowRole; + roleMap[QAccessible::ToolTip] = NSAccessibilityWindowRole; + roleMap[QAccessible::HelpBalloon] = NSAccessibilityWindowRole; + roleMap[QAccessible::PopupMenu] = NSAccessibilityMenuRole; + roleMap[QAccessible::Application] = NSAccessibilityApplicationRole; + roleMap[QAccessible::Pane] = NSAccessibilityGroupRole; + roleMap[QAccessible::Grouping] = NSAccessibilityGroupRole; + roleMap[QAccessible::Separator] = NSAccessibilitySplitterRole; + roleMap[QAccessible::ToolBar] = NSAccessibilityToolbarRole; + roleMap[QAccessible::PageTab] = NSAccessibilityRadioButtonRole; + roleMap[QAccessible::ButtonMenu] = NSAccessibilityMenuButtonRole; + roleMap[QAccessible::ButtonDropDown] = NSAccessibilityPopUpButtonRole; + roleMap[QAccessible::SpinBox] = NSAccessibilityIncrementorRole; + roleMap[QAccessible::Slider] = NSAccessibilitySliderRole; + roleMap[QAccessible::ProgressBar] = NSAccessibilityProgressIndicatorRole; + roleMap[QAccessible::ComboBox] = NSAccessibilityPopUpButtonRole; + roleMap[QAccessible::RadioButton] = NSAccessibilityRadioButtonRole; + roleMap[QAccessible::CheckBox] = NSAccessibilityCheckBoxRole; + roleMap[QAccessible::StaticText] = NSAccessibilityStaticTextRole; + roleMap[QAccessible::Table] = NSAccessibilityTableRole; + roleMap[QAccessible::StatusBar] = NSAccessibilityStaticTextRole; + roleMap[QAccessible::Column] = NSAccessibilityColumnRole; + roleMap[QAccessible::ColumnHeader] = NSAccessibilityColumnRole; + roleMap[QAccessible::Row] = NSAccessibilityRowRole; + roleMap[QAccessible::RowHeader] = NSAccessibilityRowRole; + roleMap[QAccessible::Cell] = NSAccessibilityTextFieldRole; + roleMap[QAccessible::PushButton] = NSAccessibilityButtonRole; + roleMap[QAccessible::EditableText] = NSAccessibilityTextFieldRole; + roleMap[QAccessible::Link] = NSAccessibilityTextFieldRole; + roleMap[QAccessible::Indicator] = NSAccessibilityValueIndicatorRole; + roleMap[QAccessible::Splitter] = NSAccessibilitySplitGroupRole; + roleMap[QAccessible::List] = NSAccessibilityListRole; + roleMap[QAccessible::ListItem] = NSAccessibilityStaticTextRole; + roleMap[QAccessible::Cell] = NSAccessibilityStaticTextRole; +} + +/* + Returns a Mac accessibility role for the given interface, or + NSAccessibilityUnknownRole if no role mapping is found. +*/ +static NSString *macRoleForInterface(QAInterface interface) +{ + const QAccessible::Role qtRole = interface.role(); + QMacAccessibiltyRoleMap &roleMap = *qMacAccessibiltyRoleMap(); + + if (roleMap.isEmpty()) + populateRoleMap(); + + MAC_ACCESSIBILTY_DEBUG() << "role for" << interface.object() << "interface role" << hex << qtRole; + + if (roleMap.contains(qtRole)) { + MAC_ACCESSIBILTY_DEBUG() << "return" << roleMap[qtRole]; + return roleMap[qtRole]; + } + + MAC_ACCESSIBILTY_DEBUG() << "return NSAccessibilityUnknownRole"; + return NSAccessibilityUnknownRole; +} + +/* + Is the interface a QTabBar embedded in a QTabWidget? + (as opposed to a stand-alone tab bar) +*/ +static bool isEmbeddedTabBar(const QAInterface &interface) +{ + QObject *object = interface.object(); + if (interface.role() == QAccessible::PageTabList && object) + return (qobject_cast<QTabWidget *>(object->parent())); + + return false; +} + +static bool isInterfaceIgnored(QAInterface interface) +{ + // Mac accessibility does not have an attribute that corresponds to the + // Invisible/Offscreen state. Use the ignore facility to disable them. + const QAccessible::State state = interface.state(); + if (state & QAccessible::Invisible || + state & QAccessible::Offscreen ) + return false; + + // Hide QTabBars that has a QTabWidget parent (the QTabWidget handles the accessibility) + if (isEmbeddedTabBar(interface)) + return false; + + if (QObject * const object = interface.object()) { + const QString className = QLatin1String(object->metaObject()->className()); + + // Prevent VoiceOver from focusing on tool tips by ignoring those + // interfaces. Shifting VoiceOver focus to the tool tip is confusing + // and the contents of the tool tip is avalible through the description + // attribute anyway. + if (className == QLatin1String("QTipLabel")) + return false; + } + + // Hide interfaces with an unknown role. When developing it's often useful to disable + // this check to see all interfaces in the hierarchy. +#ifndef MAC_ACCESSIBILTY_DEVELOPER_MODE + return [macRoleForInterface(interface) isEqualToString: NSAccessibilityUnknownRole]; +#else + return NO; +#endif +} + +QT_END_NAMESPACE + +@implementation QT_MANGLE_NAMESPACE(QCocoaView) (Accessibility) + +- (BOOL)accessibilityIsIgnored +{ + QAInterface interface = interfaceForView(self); + return isInterfaceIgnored(interface); +} + +- (NSArray *)accessibilityAttributeNames +{ + QAInterface interface = interfaceForView(self); + + static NSArray *attributes = nil; + if (attributes == nil) { + attributes = [super accessibilityAttributeNames]; + + } + return attributes; +} + +- (id)accessibilityAttributeValue:(NSString *)attribute +{ + MAC_ACCESSIBILTY_DEBUG() << "accessibilityAttributeValue" << self << QCFString::toQString(reinterpret_cast<CFStringRef>(attribute)); + + QAInterface interface = interfaceForView(self); + + // Switch on the attribute name and call the appropriate handler function. + // Pass the call on to the NSView class for attributes we don't handle. + if ([attribute isEqualToString:@"AXRole"]) { + return macRoleForInterface(interface); + } else { + return [super accessibilityAttributeValue:attribute]; + } +} + +@end + +#endif // QT_MAC_USE_COCOA + +#endif // QT_NO_ACCESSIBILITY + diff --git a/src/gui/dialogs/qcolordialog.cpp b/src/gui/dialogs/qcolordialog.cpp index 42d3a9a..aee592c 100644 --- a/src/gui/dialogs/qcolordialog.cpp +++ b/src/gui/dialogs/qcolordialog.cpp @@ -1583,8 +1583,7 @@ void QColorDialog::setCurrentColor(const QColor &color) #ifdef Q_WS_MAC d->setCurrentQColor(color); - if (d->delegate) - QColorDialogPrivate::setColor(d->delegate, color); + d->setCocoaPanelColor(color); #endif } @@ -1725,19 +1724,16 @@ void QColorDialog::setVisible(bool visible) #if defined(Q_WS_MAC) if (visible) { - if (!d->delegate && QColorDialogPrivate::sharedColorPanelAvailable && - !(testAttribute(Qt::WA_DontShowOnScreen) || (d->opts & DontUseNativeDialog))){ - d->delegate = QColorDialogPrivate::openCocoaColorPanel( - currentColor(), parentWidget(), windowTitle(), options(), d); + if (d->delegate || (QColorDialogPrivate::sharedColorPanelAvailable && + !(testAttribute(Qt::WA_DontShowOnScreen) || (d->opts & DontUseNativeDialog)))){ + d->openCocoaColorPanel(currentColor(), parentWidget(), windowTitle(), options()); QColorDialogPrivate::sharedColorPanelAvailable = false; setAttribute(Qt::WA_DontShowOnScreen); } setWindowFlags(windowModality() == Qt::WindowModal ? Qt::Sheet : DefaultWindowFlags); } else { if (d->delegate) { - QColorDialogPrivate::closeCocoaColorPanel(d->delegate); - d->delegate = 0; - QColorDialogPrivate::sharedColorPanelAvailable = true; + d->closeCocoaColorPanel(); setAttribute(Qt::WA_DontShowOnScreen, false); } } @@ -1840,6 +1836,14 @@ QRgb QColorDialog::getRgba(QRgb initial, bool *ok, QWidget *parent) QColorDialog::~QColorDialog() { + Q_D(QColorDialog); +#if defined(Q_WS_MAC) + if (d->delegate) { + d->releaseCocoaColorPanelDelegate(); + QColorDialogPrivate::sharedColorPanelAvailable = true; + } +#endif + #ifndef QT_NO_SETTINGS if (!customSet) { QSettings settings(QSettings::UserScope, QLatin1String("Trolltech")); diff --git a/src/gui/dialogs/qcolordialog_mac.mm b/src/gui/dialogs/qcolordialog_mac.mm index 1936de5..6cdb7ee 100644 --- a/src/gui/dialogs/qcolordialog_mac.mm +++ b/src/gui/dialogs/qcolordialog_mac.mm @@ -76,6 +76,8 @@ QT_USE_NAMESPACE CGFloat mMinWidth; // currently unused CGFloat mExtraHeight; // currently unused BOOL mHackedPanel; + NSInteger mResultCode; + BOOL mDialogIsExecuting; } - (id)initWithColorPanel:(NSColorPanel *)panel stolenContentView:(NSView *)stolenContentView @@ -90,7 +92,8 @@ QT_USE_NAMESPACE - (NSColorPanel *)colorPanel; - (QColor)qtColor; - (void)finishOffWithCode:(NSInteger)result; -- (void)cleanUpAfterMyself; +- (void)showColorPanel; +- (void)exec; @end @implementation QCocoaColorPanelDelegate @@ -110,6 +113,8 @@ QT_USE_NAMESPACE mMinWidth = 0.0; mExtraHeight = 0.0; mHackedPanel = (okButton != 0); + mResultCode = NSCancelButton; + mDialogIsExecuting = false; if (mHackedPanel) { [self relayout]; @@ -121,19 +126,31 @@ QT_USE_NAMESPACE [cancelButton setTarget:self]; } - if (mPriv) - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(colorChanged:) - name:NSColorPanelColorDidChangeNotification - object:mColorPanel]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(colorChanged:) + name:NSColorPanelColorDidChangeNotification + object:mColorPanel]; + mQtColor = new QColor(); return self; } - (void)dealloc { - if (mPriv) - [[NSNotificationCenter defaultCenter] removeObserver:self]; + QMacCocoaAutoReleasePool pool; + if (mHackedPanel) { + NSView *ourContentView = [mColorPanel contentView]; + + // return stolen stuff to its rightful owner + [mStolenContentView removeFromSuperview]; + [mColorPanel setContentView:mStolenContentView]; + + [mOkButton release]; + [mCancelButton release]; + [ourContentView release]; + } + [mColorPanel setDelegate:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; delete mQtColor; [super dealloc]; } @@ -160,8 +177,7 @@ QT_USE_NAMESPACE - (void)colorChanged:(NSNotification *)notification; { Q_UNUSED(notification); - if (mPriv) - [self updateQtColor]; + [self updateQtColor]; } - (void)relayout @@ -258,8 +274,7 @@ QT_USE_NAMESPACE } } - if (mPriv) - mPriv->setCurrentQColor(*mQtColor); + mPriv->setCurrentQColor(*mQtColor); } - (NSColorPanel *)colorPanel @@ -274,36 +289,42 @@ QT_USE_NAMESPACE - (void)finishOffWithCode:(NSInteger)code { - if (mPriv) { - // Finish the QColorDialog as well. But since a call to accept or reject will - // close down the QEventLoop found in QDialog, we need to make sure that the - // current thread has exited the native dialogs modal session/run loop first. - // We ensure this by posting the call: + mResultCode = code; + if (mDialogIsExecuting) { + // We stop the current modal event loop. The control + // will then return inside -(void)exec below. + // It's important that the modal event loop is stopped before + // we accept/reject QColorDialog, since QColorDialog has its + // own event loop that needs to be stopped last. [NSApp stopModalWithCode:code]; - if (code == NSOKButton) - QMetaObject::invokeMethod(mPriv->colorDialog(), "accept", Qt::QueuedConnection); - else - QMetaObject::invokeMethod(mPriv->colorDialog(), "reject", Qt::QueuedConnection); } else { - [NSApp stopModalWithCode:code]; + // Since we are not in a modal event loop, we can safely close + // down QColorDialog + if (mResultCode == NSCancelButton) + mPriv->colorDialog()->reject(); + else + mPriv->colorDialog()->accept(); } } -- (void)cleanUpAfterMyself +- (void)showColorPanel { - if (mHackedPanel) { - NSView *ourContentView = [mColorPanel contentView]; - - // return stolen stuff to its rightful owner - [mStolenContentView removeFromSuperview]; - [mColorPanel setContentView:mStolenContentView]; + mDialogIsExecuting = false; + [mColorPanel makeKeyAndOrderFront:mColorPanel]; +} - [mOkButton release]; - [mCancelButton release]; - [ourContentView release]; - } - [mColorPanel setDelegate:nil]; +- (void)exec +{ + QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active); + QMacCocoaAutoReleasePool pool; + mDialogIsExecuting = true; + [NSApp runModalForWindow:mColorPanel]; + if (mResultCode == NSCancelButton) + mPriv->colorDialog()->reject(); + else + mPriv->colorDialog()->accept(); } + @end QT_BEGIN_NAMESPACE @@ -312,91 +333,90 @@ extern void macStartInterceptNSPanelCtor(); extern void macStopInterceptNSPanelCtor(); extern NSButton *macCreateButton(const char *text, NSView *superview); -void *QColorDialogPrivate::openCocoaColorPanel(const QColor &initial, - QWidget *parent, const QString &title, QColorDialog::ColorDialogOptions options, - QColorDialogPrivate *priv) +void QColorDialogPrivate::openCocoaColorPanel(const QColor &initial, + QWidget *parent, const QString &title, QColorDialog::ColorDialogOptions options) { Q_UNUSED(parent); // we would use the parent if only NSColorPanel could be a sheet QMacCocoaAutoReleasePool pool; - /* - The standard Cocoa color panel has no OK or Cancel button and - is created as a utility window, whereas we want something like - the Carbon color panel. We need to take the following steps: + if (!delegate) { + /* + The standard Cocoa color panel has no OK or Cancel button and + is created as a utility window, whereas we want something like + the Carbon color panel. We need to take the following steps: + + 1. Intercept the color panel constructor to turn off the + NSUtilityWindowMask flag. This is done by temporarily + replacing initWithContentRect:styleMask:backing:defer: + in NSPanel by our own method. - 1. Intercept the color panel constructor to turn off the - NSUtilityWindowMask flag. This is done by temporarily - replacing initWithContentRect:styleMask:backing:defer: - in NSPanel by our own method. + 2. Modify the color panel so that its content view is part + of a new content view that contains it as well as two + buttons (OK and Cancel). - 2. Modify the color panel so that its content view is part - of a new content view that contains it as well as two - buttons (OK and Cancel). + 3. Lay out the original content view and the buttons when + the color panel is shown and whenever it is resized. - 3. Lay out the original content view and the buttons when - the color panel is shown and whenever it is resized. + 4. Clean up after ourselves. + */ - 4. Clean up after ourselves. - */ + bool hackColorPanel = !(options & QColorDialog::NoButtons); - bool hackColorPanel = !(options & QColorDialog::NoButtons); + if (hackColorPanel) + macStartInterceptNSPanelCtor(); + NSColorPanel *colorPanel = [NSColorPanel sharedColorPanel]; + if (hackColorPanel) + macStopInterceptNSPanelCtor(); - if (hackColorPanel) - macStartInterceptNSPanelCtor(); - NSColorPanel *colorPanel = [NSColorPanel sharedColorPanel]; - if (hackColorPanel) - macStopInterceptNSPanelCtor(); + [colorPanel setHidesOnDeactivate:false]; - [colorPanel setHidesOnDeactivate:false]; + // set up the Cocoa color panel + [colorPanel setShowsAlpha:options & QColorDialog::ShowAlphaChannel]; + [colorPanel setTitle:(NSString*)(CFStringRef)QCFString(title)]; - // set up the Cocoa color panel - [colorPanel setShowsAlpha:options & QColorDialog::ShowAlphaChannel]; - [colorPanel setTitle:(NSString*)(CFStringRef)QCFString(title)]; + NSView *stolenContentView = 0; + NSButton *okButton = 0; + NSButton *cancelButton = 0; - NSView *stolenContentView = 0; - NSButton *okButton = 0; - NSButton *cancelButton = 0; + if (hackColorPanel) { + // steal the color panel's contents view + stolenContentView = [colorPanel contentView]; + [stolenContentView retain]; + [colorPanel setContentView:0]; - if (hackColorPanel) { - // steal the color panel's contents view - stolenContentView = [colorPanel contentView]; - [stolenContentView retain]; - [colorPanel setContentView:0]; + // create a new content view and add the stolen one as a subview + NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; + NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect]; + [ourContentView addSubview:stolenContentView]; - // create a new content view and add the stolen one as a subview - NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; - NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect]; - [ourContentView addSubview:stolenContentView]; + // create OK and Cancel buttons and add these as subviews + okButton = macCreateButton("&OK", ourContentView); + cancelButton = macCreateButton("Cancel", ourContentView); - // create OK and Cancel buttons and add these as subviews - okButton = macCreateButton("&OK", ourContentView); - cancelButton = macCreateButton("Cancel", ourContentView); + [colorPanel setContentView:ourContentView]; + [colorPanel setDefaultButtonCell:[okButton cell]]; + } - [colorPanel setContentView:ourContentView]; - [colorPanel setDefaultButtonCell:[okButton cell]]; + delegate = [[QCocoaColorPanelDelegate alloc] initWithColorPanel:colorPanel + stolenContentView:stolenContentView + okButton:okButton + cancelButton:cancelButton + priv:this]; + [colorPanel setDelegate:static_cast<QCocoaColorPanelDelegate *>(delegate)]; } - // create a delegate and set it - QCocoaColorPanelDelegate *delegate = - [[QCocoaColorPanelDelegate alloc] initWithColorPanel:colorPanel - stolenContentView:stolenContentView - okButton:okButton - cancelButton:cancelButton - priv:priv]; - [colorPanel setDelegate:delegate]; - setColor(delegate, initial); - [colorPanel makeKeyAndOrderFront:colorPanel]; - - return delegate; + setCocoaPanelColor(initial); + [static_cast<QCocoaColorPanelDelegate *>(delegate) showColorPanel]; } -void QColorDialogPrivate::closeCocoaColorPanel(void *delegate) +void QColorDialogPrivate::closeCocoaColorPanel() { - QMacCocoaAutoReleasePool pool; - QCocoaColorPanelDelegate *theDelegate = static_cast<QCocoaColorPanelDelegate *>(delegate); - [[theDelegate colorPanel] close]; - [theDelegate cleanUpAfterMyself]; - [theDelegate autorelease]; + [[static_cast<QCocoaColorPanelDelegate *>(delegate) colorPanel] close]; +} + +void QColorDialogPrivate::releaseCocoaColorPanelDelegate() +{ + [static_cast<QCocoaColorPanelDelegate *>(delegate) release]; } void QColorDialogPrivate::mac_nativeDialogModalHelp() @@ -416,13 +436,10 @@ void QColorDialogPrivate::mac_nativeDialogModalHelp() void QColorDialogPrivate::_q_macRunNativeAppModalPanel() { - QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active); - QMacCocoaAutoReleasePool pool; - QCocoaColorPanelDelegate *delegateCasted = static_cast<QCocoaColorPanelDelegate *>(delegate); - [NSApp runModalForWindow:[delegateCasted colorPanel]]; + [static_cast<QCocoaColorPanelDelegate *>(delegate) exec]; } -void QColorDialogPrivate::setColor(void *delegate, const QColor &color) +void QColorDialogPrivate::setCocoaPanelColor(const QColor &color) { QMacCocoaAutoReleasePool pool; QCocoaColorPanelDelegate *theDelegate = static_cast<QCocoaColorPanelDelegate *>(delegate); diff --git a/src/gui/dialogs/qcolordialog_p.h b/src/gui/dialogs/qcolordialog_p.h index ea66d4a..00d40b6 100644 --- a/src/gui/dialogs/qcolordialog_p.h +++ b/src/gui/dialogs/qcolordialog_p.h @@ -116,14 +116,11 @@ public: QByteArray memberToDisconnectOnClose; #ifdef Q_WS_MAC - static void *openCocoaColorPanel(const QColor &initial, - QWidget *parent, const QString &title, - QColorDialog::ColorDialogOptions options, - QColorDialogPrivate *priv = 0); - static void closeCocoaColorPanel(void *delegate); - static QColor execCocoaColorPanel(const QColor &initial, QWidget *parent, - const QString &title, QColorDialog::ColorDialogOptions options); - static void setColor(void *delegate, const QColor &color); + void openCocoaColorPanel(const QColor &initial, + QWidget *parent, const QString &title, QColorDialog::ColorDialogOptions options); + void closeCocoaColorPanel(); + void releaseCocoaColorPanelDelegate(); + void setCocoaPanelColor(const QColor &color); inline void done(int result) { q_func()->done(result); } inline QColorDialog *colorDialog() { return q_func(); } diff --git a/src/gui/dialogs/qfiledialog.cpp b/src/gui/dialogs/qfiledialog.cpp index 8b4e1b1..f000033 100644 --- a/src/gui/dialogs/qfiledialog.cpp +++ b/src/gui/dialogs/qfiledialog.cpp @@ -394,6 +394,9 @@ QList<QUrl> QFileDialog::sidebarUrls() const static const qint32 QFileDialogMagic = 0xbe; +const char *qt_file_dialog_filter_reg_exp = +"^(.*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; + /*! \since 4.3 Saves the state of the dialog's layout, history and current directory. @@ -984,8 +987,13 @@ void QFileDialog::setNameFilters(const QStringList &filters) if (testOption(HideNameFilterDetails)) { QStringList strippedFilters; + QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp)); for (int i = 0; i < cleanedFilters.count(); ++i) { - strippedFilters.append(cleanedFilters[i].mid(0, cleanedFilters[i].indexOf(QLatin1String(" (")))); + QString filterName; + int index = r.indexIn(cleanedFilters[i]); + if (index >= 0) + filterName = r.cap(1); + strippedFilters.append(filterName.simplified()); } d->qFileDialogUi->fileTypeCombo->addItems(strippedFilters); } else { @@ -2837,9 +2845,6 @@ void QFileDialogPrivate::_q_goToDirectory(const QString &path) } } -const char *qt_file_dialog_filter_reg_exp = - "^([^()]*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; - // Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)" QStringList qt_clean_filter_list(const QString &filter) { diff --git a/src/gui/dialogs/qfiledialog_win.cpp b/src/gui/dialogs/qfiledialog_win.cpp index 9bf82c3..02469b2 100644 --- a/src/gui/dialogs/qfiledialog_win.cpp +++ b/src/gui/dialogs/qfiledialog_win.cpp @@ -59,7 +59,9 @@ #endif #include <shlobj.h> - +#if !defined(Q_WS_WINCE) +#include <shobjidl.h> +#endif #include <objbase.h> #if defined(__IFileDialog_INTERFACE_DEFINED__) \ @@ -441,6 +443,8 @@ static bool qt_win_set_IFileDialogOptions(IFileDialog *pfd, QString subfilter = *it; if (!subfilter.isEmpty()) { offsets<<currentOffset; + //Here the COMMON_ITEM_DIALOG API always add the details for the filter (e.g. *.txt) + //so we don't need to handle the flag HideNameFilterDetails. winfilters += subfilter; // The name of the filter. winfilters += QChar(); currentOffset += subfilter.size()+1; diff --git a/src/gui/dialogs/qfilesystemmodel.cpp b/src/gui/dialogs/qfilesystemmodel.cpp index 5a5d845..b8e12b4 100644 --- a/src/gui/dialogs/qfilesystemmodel.cpp +++ b/src/gui/dialogs/qfilesystemmodel.cpp @@ -872,6 +872,7 @@ QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, pixmap.setAlphaChannel(pixmap.createAlphaMask()); return pixmap; } + break; case Qt::TextAlignmentRole: return Qt::AlignLeft; } diff --git a/src/gui/egl/qegl.cpp b/src/gui/egl/qegl.cpp index 89d9d1b..ebdac9a 100644 --- a/src/gui/egl/qegl.cpp +++ b/src/gui/egl/qegl.cpp @@ -400,4 +400,15 @@ void QEglContext::dumpAllConfigs() delete [] configs; } +QString QEglContext::extensions() +{ + const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); + return QString(QLatin1String(exts)); +} + +bool QEglContext::hasExtension(const char* extensionName) +{ + return extensions().contains(QLatin1String(extensionName)); +} + QT_END_NAMESPACE diff --git a/src/gui/egl/qegl_p.h b/src/gui/egl/qegl_p.h index ddf7d27..3ae1489 100644 --- a/src/gui/egl/qegl_p.h +++ b/src/gui/egl/qegl_p.h @@ -122,6 +122,9 @@ public: void dumpAllConfigs(); + QString extensions(); + bool hasExtension(const char* extensionName); + private: QEgl::API apiType; EGLDisplay dpy; diff --git a/src/gui/egl/qegl_x11.cpp b/src/gui/egl/qegl_x11.cpp index be89efe..6772592 100644 --- a/src/gui/egl/qegl_x11.cpp +++ b/src/gui/egl/qegl_x11.cpp @@ -39,15 +39,18 @@ ** ****************************************************************************/ +#include <QtCore/qdebug.h> + +#include <private/qt_x11_p.h> +#include <QtGui/qx11info_x11.h> +#include <private/qpixmapdata_p.h> +#include <private/qpixmap_x11_p.h> + #include <QtGui/qpaintdevice.h> #include <QtGui/qpixmap.h> #include <QtGui/qwidget.h> -#include <QtCore/qdebug.h> #include "qegl_p.h" -#include <QtGui/qx11info_x11.h> -#include <X11/Xlib.h> -#include <X11/Xutil.h> QT_BEGIN_NAMESPACE @@ -125,7 +128,17 @@ void QEglProperties::setVisualFormat(const QX11Info *xinfo) setValue(EGL_RED_SIZE, countBits(visual->red_mask)); setValue(EGL_GREEN_SIZE, countBits(visual->green_mask)); setValue(EGL_BLUE_SIZE, countBits(visual->blue_mask)); - setValue(EGL_ALPHA_SIZE, 0); // XXX + + EGLint alphaBits = 0; +#if !defined(QT_NO_XRENDER) + XRenderPictFormat *format; + format = XRenderFindVisualFormat(xinfo->display(), visual); + if (format && (format->type == PictTypeDirect) && format->direct.alphaMask) { + alphaBits = countBits(format->direct.alphaMask); + qDebug("QEglProperties::setVisualFormat() - visual's alphaMask is %d", alphaBits); + } +#endif + setValue(EGL_ALPHA_SIZE, alphaBits); } extern const QX11Info *qt_x11Info(const QPaintDevice *pd); diff --git a/src/gui/egl/qeglproperties.cpp b/src/gui/egl/qeglproperties.cpp index e0ae8a6..358ebcc 100644 --- a/src/gui/egl/qeglproperties.cpp +++ b/src/gui/egl/qeglproperties.cpp @@ -46,12 +46,26 @@ QT_BEGIN_NAMESPACE #include <QtCore/qdebug.h> #include <QtCore/qstringlist.h> +#include "qegl_p.h" + + // Initialize a property block. QEglProperties::QEglProperties() { props.append(EGL_NONE); } +QEglProperties::QEglProperties(EGLConfig cfg) +{ + props.append(EGL_NONE); + for (int name = 0x3020; name <= 0x304F; ++name) { + EGLint value; + if (name != EGL_NONE && eglGetConfigAttrib(QEglContext::defaultDisplay(0), cfg, name, &value)) + setValue(name, value); + } + eglGetError(); // Clear the error state. +} + // Fetch the current value associated with a property. int QEglProperties::value(int name) const { @@ -215,12 +229,21 @@ bool QEglProperties::reduceConfiguration() removeValue(EGL_SAMPLES); return true; } - if (removeValue(EGL_ALPHA_SIZE)) + if (removeValue(EGL_ALPHA_SIZE)) { +#if defined(EGL_BIND_TO_TEXTURE_RGBA) && defined(EGL_BIND_TO_TEXTURE_RGB) + if (removeValue(EGL_BIND_TO_TEXTURE_RGBA)) + setValue(EGL_BIND_TO_TEXTURE_RGB, TRUE); +#endif return true; + } if (removeValue(EGL_STENCIL_SIZE)) return true; if (removeValue(EGL_DEPTH_SIZE)) return true; +#if defined(EGL_BIND_TO_TEXTURE_RGB) + if (removeValue(EGL_BIND_TO_TEXTURE_RGB)) + return true; +#endif return false; } diff --git a/src/gui/egl/qeglproperties_p.h b/src/gui/egl/qeglproperties_p.h index 81af4cd..bcdc657 100644 --- a/src/gui/egl/qeglproperties_p.h +++ b/src/gui/egl/qeglproperties_p.h @@ -107,6 +107,7 @@ class Q_GUI_EXPORT QEglProperties { public: QEglProperties(); + QEglProperties(EGLConfig); QEglProperties(const QEglProperties& other) : props(other.props) {} ~QEglProperties() {} diff --git a/src/gui/embedded/qkbdqnx_qws.cpp b/src/gui/embedded/qkbdqnx_qws.cpp index 06163c7..089b868 100644 --- a/src/gui/embedded/qkbdqnx_qws.cpp +++ b/src/gui/embedded/qkbdqnx_qws.cpp @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/embedded/qkbdqnx_qws.h b/src/gui/embedded/qkbdqnx_qws.h index c046c8d..fa3ae56 100644 --- a/src/gui/embedded/qkbdqnx_qws.h +++ b/src/gui/embedded/qkbdqnx_qws.h @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/embedded/qmouseqnx_qws.cpp b/src/gui/embedded/qmouseqnx_qws.cpp index 98f8f06..59cd5be 100644 --- a/src/gui/embedded/qmouseqnx_qws.cpp +++ b/src/gui/embedded/qmouseqnx_qws.cpp @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/embedded/qmouseqnx_qws.h b/src/gui/embedded/qmouseqnx_qws.h index a61562e..f8cad4a 100644 --- a/src/gui/embedded/qmouseqnx_qws.h +++ b/src/gui/embedded/qmouseqnx_qws.h @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/embedded/qscreenqnx_qws.cpp b/src/gui/embedded/qscreenqnx_qws.cpp index 7101bc7..c79ee59 100644 --- a/src/gui/embedded/qscreenqnx_qws.cpp +++ b/src/gui/embedded/qscreenqnx_qws.cpp @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/embedded/qscreenqnx_qws.h b/src/gui/embedded/qscreenqnx_qws.h index 837c061..30312fe 100644 --- a/src/gui/embedded/qscreenqnx_qws.h +++ b/src/gui/embedded/qscreenqnx_qws.h @@ -6,11 +6,11 @@ ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ -** Commercial Usage -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. +** 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 5aae93e..beaf42b 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -135,38 +135,39 @@ \section1 Transformation - QGraphicsItem supports affine transformations in addition to its base - position, pos(). To change the item's transformation, you can pass - a transformation matrix to setTransform() - - Item transformations accumulate from parent to child, so if both a - parent and child item are rotated 90 degrees, the child's total - transformation will be 180 degrees. Similarly, if the item's - parent is scaled to 2x its original size, its children will also - be twice as large. An item's transformation does not affect its - own local geometry; all geometry functions (e.g., contains(), - update(), and all the mapping functions) still operate in local - coordinates. For convenience, QGraphicsItem provides the functions - sceneTransform(), which returns the item's total transformation + QGraphicsItem supports projective transformations in addition to its base + position, pos(). There are several ways to change an item's transformation. + For simple transformations, you can call either of the convenience + functions setRotation() or setScale(), or you can pass any transformation + matrix to setTransform(). For advanced transformation control you also have + the option of setting several combined transformations by calling + setTransformations(). + + Item transformations accumulate from parent to child, so if both a parent + and child item are rotated 90 degrees, the child's total transformation + will be 180 degrees. Similarly, if the item's parent is scaled to 2x its + original size, its children will also be twice as large. An item's + transformation does not affect its own local geometry; all geometry + functions (e.g., contains(), update(), and all the mapping functions) still + operate in local coordinates. For convenience, QGraphicsItem provides the + functions sceneTransform(), which returns the item's total transformation matrix (including its position and all parents' positions and - transformations), and scenePos(), which returns its position in - scene coordinates. To reset an item's matrix, call - resetTransform(). + transformations), and scenePos(), which returns its position in scene + coordinates. To reset an item's matrix, call resetTransform(). - Another way to apply transformation to an item is to use the , or - set the different transformation properties (transformOrigin, - x/y/zRotation, x/yScale, horizontal/verticalShear). Those - properties come in addition to the base transformation + Certain transformation operations produce a different outcome depending on + the order in which they are applied. For example, if you scale an + transform, and then rotate it, you may get a different result than if the + transform was rotated first. However, the order you set the transformation + properties on QGraphicsItem does not affect the resulting transformation; + QGraphicsItem always applies the properties in a fixed, defined order: - The order you set the transformation properties does not affect - the resulting transformation The resulting transformation is - always computed in the following order - - \code - [Origin] [Base] [RotateX] [RotateY] [RotateZ] [Shear] [Scale] [-Origin] - \endcode - - Where [Base] is the stransformation set by setTransform + \list + \o The item's base transform is applied (transform()) + \o The item's transformations list is applied in order (transformations()) + \o The item is rotated relative to its transform origin point (rotation(), transformOriginPoint()) + \o The item is scaled relative to its transform origin point (scale(), transformOriginPoint()) + \endlist \section1 Painting @@ -674,19 +675,22 @@ void QGraphicsItemPrivate::updateAncestorFlag(QGraphicsItem::GraphicsItemFlag ch return; } - // Inherit the enabled-state from our parents. - if ((parent && ((parent->d_ptr->ancestorFlags & flag) - || (int(parent->d_ptr->flags & childFlag) == childFlag) + if (parent) { + // Inherit the enabled-state from our parents. + if ((parent->d_ptr->ancestorFlags & flag) + || (int(parent->d_ptr->flags & childFlag) == childFlag) || (childFlag == -1 && parent->d_ptr->handlesChildEvents) - || (childFlag == -2 && parent->d_ptr->filtersDescendantEvents)))) { - enabled = true; - ancestorFlags |= flag; - } - - // Top-level root items don't have any ancestors, so there are no - // ancestor flags either. - if (!parent) + || (childFlag == -2 && parent->d_ptr->filtersDescendantEvents)) { + enabled = true; + ancestorFlags |= flag; + } else { + ancestorFlags &= ~flag; + } + } else { + // Top-level root items don't have any ancestors, so there are no + // ancestor flags either. ancestorFlags = 0; + } } else { // Don't set or propagate the ancestor flag if it's already correct. if (((ancestorFlags & flag) && enabled) || (!(ancestorFlags & flag) && !enabled)) @@ -2969,11 +2973,10 @@ QMatrix QGraphicsItem::matrix() const Returns this item's transformation matrix. - Either the one set by setTransform, or the resulting transformation from - all the transfmation properties - - If no matrix or transformation property has been set, the - identity matrix is returned. + The transformation matrix is combined with the item's rotation(), scale() + and transformations() into a combined transformations for the item. + + The default transformation matrix is an identity matrix. \sa setTransform(), sceneTransform() */ @@ -2987,13 +2990,13 @@ QTransform QGraphicsItem::transform() const /*! \since 4.6 - Returns the rotation around the Z axis. - - The default is 0 + Returns the clockwise rotation, in degrees, around the Z axis. The default + value is 0 (i.e., the item is not rotated). - \warning The value doesn't take in account any rotation set with the setTransform() method. + The rotation is combined with the item's scale(), transform() and + transformations() to map the item's coordinate system to the parent item. - \sa setRotation(), {Transformations} + \sa setRotation(), transformOriginPoint(), {Transformations} */ qreal QGraphicsItem::rotation() const { @@ -3005,9 +3008,19 @@ qreal QGraphicsItem::rotation() const /*! \since 4.6 - Sets the rotation around the Z axis to \a angle degrees. + Sets the clockwise rotation \a angle, in degrees, around the Z axis. The + default value is 0 (i.e., the item is not rotated). Assigning a negative + value will rotate the item counter-clockwise. Normally the rotation angle + is in the range (-360, 360), but it's also possible to assign values + outside of this range (e.g., a rotation of 370 degrees is the same as a + rotation of 10 degrees). - \warning The value doesn't take in account any rotation set with the setTransform() method. + The item is rotated around its transform origin point, which by default + is (0, 0). You can select a different transformation origin by calling + setTransformOriginPoint(). + + The rotation is combined with the item's scale(), transform() and + transformations() to map the item's coordinate system to the parent item. \sa rotation(), setTransformOriginPoint(), {Transformations} */ @@ -3027,13 +3040,13 @@ void QGraphicsItem::setRotation(qreal angle) /*! \since 4.6 - Returns the scale factor of the item. - - The default is 1 + Returns the scale factor of the item. The default scale factor is 1.0 + (i.e., the item is not scaled). - \warning The value doesn't take in account any scaling set with the setTransform() method. + The scale is combined with the item's rotation(), transform() and + transformations() to map the item's coordinate system to the parent item. - \sa setScale(), {Transformations} + \sa setScale(), rotation(), {Transformations} */ qreal QGraphicsItem::scale() const { @@ -3045,9 +3058,17 @@ qreal QGraphicsItem::scale() const /*! \since 4.6 - Sets the scale factor of the item to \a factor. + Sets the scale \a factor of the item. The default scale factor is 1.0 + (i.e., the item is not scaled). A scale factor of 0.0 will collapse the + item to a single point. If you provide a negative scale factor, the + item will be flipped and mirrored (i.e., rotated 180 degrees). - \warning The value doesn't take in account any scaling set with the setTransform() method. + The item is scaled around its transform origin point, which by default + is (0, 0). You can select a different transformation origin by calling + setTransformOriginPoint(). + + The scale is combined with the item's rotation(), transform() and + transformations() to map the item's coordinate system to the parent item. \sa scale(), setTransformOriginPoint(), {Transformations} */ @@ -3068,9 +3089,17 @@ void QGraphicsItem::setScale(qreal factor) /*! \since 4.6 - returns list of graphics transformations on the item. + Returns a list of graphics transforms that currently apply to this item. - \sa scale(), setTransformOriginPoint(), {Transformations} + QGraphicsTransform is for applying and controlling a chain of individual + transformation operations on an item. It's particularily useful in + animations, where each transform operation needs to be interpolated + independently, or differently. + + The transformations are combined with the item's rotation(), scale() and + transform() to map the item's coordinate system to the parent item. + + \sa scale(), rotation(), transformOriginPoint(), {Transformations} */ QList<QGraphicsTransform *> QGraphicsItem::transformations() const { @@ -3082,7 +3111,20 @@ QList<QGraphicsTransform *> QGraphicsItem::transformations() const /*! \since 4.6 - Sets a list of graphics transformations on the item to \a transformations. + Sets a list of graphics \a transformations (QGraphicsTransform) that + currently apply to this item. + + If all you want is to rotate or scale an item, you should call setRotation() + or setScale() instead. If you want to set an arbitrary transformation on + an item, you can call setTransform(). + + QGraphicsTransform is for applying and controlling a chain of individual + transformation operations on an item. It's particularily useful in + animations, where each transform operation needs to be interpolated + independently, or differently. + + The transformations are combined with the item's rotation(), scale() and + transform() to map the item's coordinate system to the parent item. \sa scale(), setTransformOriginPoint(), {Transformations} */ @@ -3098,7 +3140,9 @@ void QGraphicsItem::setTransformations(const QList<QGraphicsTransform *> &transf d_ptr->dirtySceneTransform = 1; } - +/*! + \internal +*/ void QGraphicsItemPrivate::appendGraphicsTransform(QGraphicsTransform *t) { if (!transformData) @@ -3432,7 +3476,9 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) to map an item coordiate to a scene coordinate, or mapFromScene() to map from scene coordinates to item coordinates. - \warning using this function doesnt affect the value of the transformation properties + The transformation matrix is combined with the item's rotation(), scale() + and transformations() into a combined transformation that maps the item's + coordinate system to its parent. \sa transform(), setRotation(), setScale(), setTransformOriginPoint(), {The Graphics View Coordinate System}, {Transformations} */ @@ -3491,7 +3537,14 @@ void QGraphicsItem::resetTransform() /*! \obsolete - Use setZRotation() instead + + Use + + \code + setRotation(rotation() + angle); + \endcode + + instead. Rotates the current item transformation \a angle degrees clockwise around its origin. To translate around an arbitrary point (x, y), you need to @@ -3501,8 +3554,6 @@ void QGraphicsItem::resetTransform() \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 6 - \warning using this functionhas no effect on the zRotation value - \sa setTransform(), transform(), scale(), shear(), translate() */ void QGraphicsItem::rotate(qreal angle) @@ -3512,7 +3563,14 @@ void QGraphicsItem::rotate(qreal angle) /*! \obsolete - Use setScale() instead + + Use + + \code + setTransform(QTransform::fromScale(sx, sy), true); + \endcode + + instead. Scales the current item transformation by (\a sx, \a sy) around its origin. To scale from an arbitrary point (x, y), you need to combine @@ -3522,8 +3580,6 @@ void QGraphicsItem::rotate(qreal angle) \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 7 - \warning using this function has no effect on the xScale or yScale value - \sa setTransform(), transform() */ void QGraphicsItem::scale(qreal sx, qreal sy) @@ -3533,11 +3589,16 @@ void QGraphicsItem::scale(qreal sx, qreal sy) /*! \obsolete - Use setShear instead. - Shears the current item transformation by (\a sh, \a sv). + Use - \warning using this function has no effect on the horizontalShear or verticalShear value + \code + setTransform(QTransform().shear(sh, sv), true); + \endcode + + instead. + + Shears the current item transformation by (\a sh, \a sv). \sa setTransform(), transform() */ @@ -3548,7 +3609,13 @@ void QGraphicsItem::shear(qreal sh, qreal sv) /*! \obsolete - Use setPos() or setTransformOriginPoint() instead. + + Use setPos() or setTransformOriginPoint() instead. For identical + behavior, use + + \code + setTransform(QTransform::fromTranslate(dx, dy), true); + \endcode Translates the current item transformation by (\a dx, \a dy). @@ -6320,7 +6387,7 @@ void QGraphicsItem::inputMethodEvent(QInputMethodEvent *event) surrounding text and reconversions. \a query specifies which property is queried. - \sa inputMethodEvent() + \sa inputMethodEvent(), QInputMethodEvent, QInputContext */ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const { @@ -6336,6 +6403,39 @@ QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const } /*! + Returns the current input method hints of this item. + + Input method hints are only relevant for input items. + The hints are used by the input method to indicate how it should operate. + For example, if the Qt::ImhNumbersOnly flag is set, the input method may change + its visual components to reflect that only numbers can be entered. + + The effect may vary between input method implementations. + + \since 4.6 + + \sa setInputMethodHints(), inputMethodQuery(), QInputContext +*/ +Qt::InputMethodHints QGraphicsItem::inputMethodHints() const +{ + Q_D(const QGraphicsItem); + return d->imHints; +} + +/*! + Sets the current input method hints of this item to \a hints. + + \since 4.6 + + \sa inputMethodHints(), inputMethodQuery(), QInputContext +*/ +void QGraphicsItem::setInputMethodHints(Qt::InputMethodHints hints) +{ + Q_D(QGraphicsItem); + d->imHints = hints; +} + +/*! This virtual function is called by QGraphicsItem to notify custom items that some part of the item's state changes. By reimplementing this function, your can react to a change, and in some cases, (depending on \a @@ -6462,7 +6562,7 @@ void QGraphicsItem::prepareGeometryChange() // if someone is connected to the changed signal or the scene has no views. // Note that this has to be done *after* markDirty to ensure that // _q_processDirtyItems is called before _q_emitUpdated. - if ((scenePrivate->connectedSignals & scenePrivate->changedSignalMask) + if ((scenePrivate->connectedSignals[0] & scenePrivate->changedSignalMask) || scenePrivate->views.isEmpty()) { if (d_ptr->hasTranslateOnlySceneTransform()) { d_ptr->scene->update(boundingRect().translated(d_ptr->sceneTransform.dx(), diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index b94fb97..f142b0f 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -375,6 +375,9 @@ public: QVariant data(int key) const; void setData(int key, const QVariant &value); + Qt::InputMethodHints inputMethodHints() const; + void setInputMethodHints(Qt::InputMethodHints hints); + enum { Type = 1, UserType = 65536 diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index b8d98c1..805b554 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -126,6 +126,7 @@ public: depth(0), focusProxy(0), subFocusItem(0), + imHints(Qt::ImhNone), acceptedMouseButtons(0x1f), visible(1), explicitlyHidden(0), @@ -419,8 +420,9 @@ public: int depth; QGraphicsItem *focusProxy; QGraphicsItem *subFocusItem; + Qt::InputMethodHints imHints; - // Packed 32 bytes + // Packed 32 bits quint32 acceptedMouseButtons : 5; quint32 visible : 1; quint32 explicitlyHidden : 1; @@ -468,7 +470,8 @@ public: QGraphicsItem *q_ptr; }; -struct QGraphicsItemPrivate::TransformData { +struct QGraphicsItemPrivate::TransformData +{ QTransform transform; qreal scale; qreal rotation; @@ -481,7 +484,7 @@ struct QGraphicsItemPrivate::TransformData { scale(1.0), rotation(0.0), xOrigin(0.0), yOrigin(0.0), onlyTransform(true) - {} + { } QTransform computedFullTransform(QTransform *postmultiplyTransform = 0) const { diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index a846cf6..da4a347 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -344,7 +344,7 @@ void QGraphicsScenePrivate::_q_emitUpdated() // the optimization that items send updates directly to the views, but it // needs to happen in order to keep compatibility with the behavior from // Qt 4.4 and backward. - if (connectedSignals & changedSignalMask) { + if (connectedSignals[0] & changedSignalMask) { for (int i = 0; i < views.size(); ++i) { QGraphicsView *view = views.at(i); if (!view->d_func()->connectedToScene) { @@ -2899,7 +2899,7 @@ void QGraphicsScene::update(const QRectF &rect) // Check if anyone's connected; if not, we can send updates directly to // the views. Otherwise or if there are no views, use old behavior. - bool directUpdates = !(d->connectedSignals & d->changedSignalMask) && !d->views.isEmpty(); + bool directUpdates = !(d->connectedSignals[0] & d->changedSignalMask) && !d->views.isEmpty(); if (rect.isNull()) { d->updateAll = true; d->updatedRects.clear(); @@ -4477,7 +4477,7 @@ void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, b if (removingItemFromScene) { // Note that this function can be called from the item's destructor, so // do NOT call any virtual functions on it within this block. - if ((connectedSignals & changedSignalMask) || views.isEmpty()) { + if ((connectedSignals[0] & changedSignalMask) || views.isEmpty()) { // This block of code is kept for compatibility. Since 4.5, by default // QGraphicsView does not connect the signal and we use the below // method of delivering updates. @@ -4623,7 +4623,7 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool // Process item. if (item->d_ptr->dirty || item->d_ptr->paintedViewBoundingRectsNeedRepaint) { - const bool useCompatUpdate = views.isEmpty() || (connectedSignals & changedSignalMask); + const bool useCompatUpdate = views.isEmpty() || (connectedSignals[0] & changedSignalMask); const QRectF itemBoundingRect = adjustedItemBoundingRect(item); if (useCompatUpdate && !itemIsUntransformable && qFuzzyIsNull(item->boundingRegionGranularity())) { diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp index 3cb33d1..07d23b7 100644 --- a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp +++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp @@ -525,7 +525,7 @@ QGraphicsSceneBspTreeIndex::~QGraphicsSceneBspTreeIndex() } /*! - \reimp + \internal Clear the all the BSP index. */ void QGraphicsSceneBspTreeIndex::clear() @@ -566,7 +566,7 @@ void QGraphicsSceneBspTreeIndex::removeItem(QGraphicsItem *item) } /*! - \reimp + \internal Update the BSP when the \a item 's bounding rect has changed. */ void QGraphicsSceneBspTreeIndex::prepareBoundingRectChange(const QGraphicsItem *item) @@ -682,7 +682,7 @@ void QGraphicsSceneBspTreeIndex::setBspTreeDepth(int depth) } /*! - \reimp + \internal This method react to the \a rect change of the scene and reset the BSP tree index. @@ -695,11 +695,10 @@ void QGraphicsSceneBspTreeIndex::updateSceneRect(const QRectF &rect) } /*! - \reimp + \internal This method react to the \a change of the \a item and use the \a value to update the BSP tree if necessary. - */ void QGraphicsSceneBspTreeIndex::itemChange(const QGraphicsItem *item, QGraphicsItem::GraphicsItemChange change, const QVariant &value) { diff --git a/src/gui/graphicsview/qgraphicstransform.cpp b/src/gui/graphicsview/qgraphicstransform.cpp index b55d78e..778cd94 100644 --- a/src/gui/graphicsview/qgraphicstransform.cpp +++ b/src/gui/graphicsview/qgraphicstransform.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtDeclarative module of the Qt Toolkit. ** @@ -34,11 +34,48 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ +/*! + \class QGraphicsTransform + \brief The QGraphicsTransform class is an abstract base class for building + advanced transformations on QGraphicsItems. + \since 4.6 + + As an alternative to QGraphicsItem::transform, QGraphicsTransform lets you + create and control advanced transformations that can be configured + independently using specialized properties. + + QGraphicsItem allows you to assign any number of QGraphicsTransform + instances to one QGraphicsItem. Each QGraphicsTransform is applied in + order, one at a time, to the QGraphicsItem it's assigned to. + + QGraphicsTransform is particularily useful for animations. Whereas + QGraphicsItem::setTransform() lets you assign any transform directly to an + item, there is no direct way to interpolate between two different + transformations (e.g., when transitioning between two states, each for + which the item has a different arbitrary transform assigned). Using + QGraphicsTransform you can interpolate the property values of each + independent transformation. The resulting operation is then combined into a + single transform which is applied to QGraphicsItem. + + If you want to create your own configurable transformation, you can create + a subclass of QGraphicsTransform (or any or the existing subclasses), and + reimplement the pure virtual applyTo() function, which takes a pointer to a + QTransform. Each operation you would like to apply should be exposed as + properties (e.g., customTransform->setVerticalShear(2.5)). Inside you + reimplementation of applyTo(), you can modify the provided transform + respectively. + + QGraphicsTransform can be used together with QGraphicsItem::setTransform(), + QGraphicsItem::setRotation(), and QGraphicsItem::setScale(). + + \sa QGraphicsItem::transform(), QGraphicsScale, QGraphicsRotation, QGraphicsRotation3D +*/ + #include "qgraphicstransform.h" #include "qgraphicsitem_p.h" #include "qgraphicstransform_p.h" @@ -51,7 +88,6 @@ QT_BEGIN_NAMESPACE - void QGraphicsTransformPrivate::setItem(QGraphicsItem *i) { if (item == i) @@ -77,15 +113,38 @@ void QGraphicsTransformPrivate::updateItem(QGraphicsItem *item) item->d_ptr->dirtySceneTransform = 1; } -void QGraphicsTransform::update() +/*! + Constructs a new QGraphicsTransform with the given \a parent. +*/ +QGraphicsTransform::QGraphicsTransform(QObject *parent) + : QObject(*new QGraphicsTransformPrivate, parent) +{ +} + +/*! + Destroys the graphics transform. +*/ +QGraphicsTransform::~QGraphicsTransform() { Q_D(QGraphicsTransform); - if (d->item) - d->updateItem(d->item); + d->setItem(0); +} + +/*! + \internal +*/ +QGraphicsTransform::QGraphicsTransform(QGraphicsTransformPrivate &p, QObject *parent) + : QObject(p, parent) +{ } /*! - returns this object as a QTransform. + Applies this transformation to an identity transform, and returns the + resulting transform. + + This is equivalent to passing an identity transform to applyTo(). + + \sa applyTo() */ QTransform QGraphicsTransform::transform() const { @@ -95,76 +154,65 @@ QTransform QGraphicsTransform::transform() const } /*! - \class QGraphicsTransform - \brief The QGraphicsTransform class is an abstract base class for tranformations on QGraphicsItems. - \since 4.6 - - The classes that inherit QGraphicsTransform express different types of transformations - that can be applied to graphics items. + \fn void QGraphicsTransform::applyTo(QTransform *transform) const - A list of these transformations can be applied to any graphics item. These - transformations are then easily modifyable and usable from e.g. within animations. + This pure virtual method has to be reimplemented in derived classes. - QGraphicsTransform is an abstract base class that is implemented by QGraphicsScale, - QGraphicsRotation and QGraphicsRotation3D. Subclasses have to implement the applyTo method. + It applies this transformation to \a transform. - \sa QGraphicsItem::transform(), QGraphicsScale, QGraphicsRotation, QGraphicsRotation3D + \sa QGraphicsItem::transform() */ /*! - Constructs a new QGraphicsTransform with \a parent. -*/ -QGraphicsTransform::QGraphicsTransform(QObject *parent) : - QObject(*new QGraphicsTransformPrivate, parent) -{ -} + Notifies that this transform operation has changed its parameters in such a + way that applyTo() will return a different result than before. -/*! - Destructs the graphics transform. + When implementing you own custom graphics transform, you must call this + function every time you change a parameter, to let QGraphicsItem know that + its transformation needs to be updated. + + \sa applyTo() */ -QGraphicsTransform::~QGraphicsTransform() +void QGraphicsTransform::update() { Q_D(QGraphicsTransform); - d->setItem(0); + if (d->item) + d->updateItem(d->item); } /*! - \internal -*/ -QGraphicsTransform::QGraphicsTransform(QGraphicsTransformPrivate &p, QObject *parent) - : QObject(p, parent) -{ -} - -/*! \fn void QGraphicsTransform::applyTo(QTransform *transform) const + \class QGraphicsScale + \brief The QGraphicsScale class provides a scale transformation. + \since 4.6 - This pure virtual method has to be reimplemented in derived classes. + QGraphicsScene provides certain parameters to help control how the scale + should be applied. - It applies this transformation to \a transform. -*/ + The origin is the point that the item is scaled from (i.e., it stays fixed + relative to the parent as the rest of the item grows). By default the + origin is QPointF(0, 0). + The two parameters xScale and yScale describe the scale factors to apply in + horizontal and vertical direction. They can take on any value, including 0 + (to collapse the item to a point) or negativate value. A negative xScale + value will mirror the item horizontally. A negative yScale value will flip + the item vertically. -/*! - \class QGraphicsScale - \brief The QGraphicsScale class provides a way to scale a graphics item in 2 dimensions. - \since 4.6 - - QGraphicsScale contains an \a origin around which the scaling happens, and two - scale factors, xScale and yScale, the x and one for the y axis. + \sa QGraphicsTransform, QGraphicsItem::setScale(), QTransform::scale() */ class QGraphicsScalePrivate : public QGraphicsTransformPrivate { public: QGraphicsScalePrivate() - : xScale(1), yScale(1) {} + : xScale(1), yScale(1) {} QPointF origin; qreal xScale; qreal yScale; }; /*! - Constructs a new graphics scale object with \a parent. + Constructs an empty QGraphicsScale object with the given \a parent. */ QGraphicsScale::QGraphicsScale(QObject *parent) : QGraphicsTransform(*new QGraphicsScalePrivate, parent) @@ -172,24 +220,26 @@ QGraphicsScale::QGraphicsScale(QObject *parent) } /*! - Destroys the object + Destroys the graphics scale. */ QGraphicsScale::~QGraphicsScale() { } /*! - \property QGraphicsScale::origin - The origin of the scale. All scaling will be done relative to this point. + \property QGraphicsScale::origin + \brief The QGraphicsScene class provides the origin of the scale. + + All scaling will be done relative to this point (i.e., this point + will stay fixed, relative to the parent, when the item is scaled). - The \a origin is in other words the fixed point for the transformation. + \sa xScale, yScale */ QPointF QGraphicsScale::origin() const { Q_D(const QGraphicsScale); return d->origin; } - void QGraphicsScale::setOrigin(const QPointF &point) { Q_D(QGraphicsScale); @@ -199,26 +249,21 @@ void QGraphicsScale::setOrigin(const QPointF &point) } /*! - \fn QGraphicsScale::originChanged() - - This signal is emitted whenever the origin of the object - changes. -*/ - -/*! - \property QGraphicsScale::xScale + \property QGraphicsScale::xScale + \brief the horizontal scale factor. - The scale factor in x direction. The x direction is - in the graphics items logical coordinates. + The scale factor can be any real number; the default value is 1.0. If you + set the factor to 0.0, the item will be collapsed to a single point. If you + provide a negative value, the item will be mirrored horizontally around its + origin. - \sa yScale + \sa yScale, origin */ qreal QGraphicsScale::xScale() const { Q_D(const QGraphicsScale); return d->xScale; } - void QGraphicsScale::setXScale(qreal scale) { Q_D(QGraphicsScale); @@ -230,19 +275,21 @@ void QGraphicsScale::setXScale(qreal scale) } /*! - \property QGraphicsScale::yScale + \property QGraphicsScale::yScale + \brief the vertical scale factor. - The scale factor in y direction. The y direction is - in the graphics items logical coordinates. + The scale factor can be any real number; the default value is 1.0. If you + set the factor to 0.0, the item will be collapsed to a single point. If you + provide a negative value, the item will be flipped vertically around its + origin. - \sa xScale + \sa xScale, origin */ qreal QGraphicsScale::yScale() const { Q_D(const QGraphicsScale); return d->yScale; } - void QGraphicsScale::setYScale(qreal scale) { Q_D(QGraphicsScale); @@ -254,14 +301,7 @@ void QGraphicsScale::setYScale(qreal scale) } /*! - \fn QGraphicsScale::scaleChanged() - - This signal is emitted whenever the xScale or yScale of the object - changes. -*/ - -/*! - \reimp + \reimp */ void QGraphicsScale::applyTo(QTransform *transform) const { @@ -272,12 +312,41 @@ void QGraphicsScale::applyTo(QTransform *transform) const } /*! - \class QGraphicsRotation - \brief The QGraphicsRotation class provides a way to rotate a graphics item in 2 dimensions. - \since 4.6 + \fn QGraphicsScale::originChanged() + + QGraphicsScale emits this signal when its origin changes. + + \sa QGraphicsScale::origin +*/ + +/*! + \fn QGraphicsScale::scaleChanged() + + This signal is emitted whenever the xScale or yScale of the object + changes. - QGraphicsRotation contains an \a origin around which the rotation happens, and one - angle in degrees describing the amount of the rotation. + \sa QGraphicsScale::xScale, QGraphicsScale::yScale +*/ + +/*! + \class QGraphicsRotation + \brief The QGraphicsRotation class provides a rotation transformation. + \since 4.6 + + QGraphicsRotation provides certain parameters to help control how the + rotation should be applied. + + The origin is the point that the item is rotated around (i.e., it stays + fixed relative to the parent as the rest of the item is rotated). By + default the origin is QPointF(0, 0). + + The angle property provides the number of degrees to rotate the item + clockwise around the origin. This value also be negative, indicating a + counter-clockwise rotation. For animation purposes it may also be useful to + provide rotation angles exceeding (-360, 360) degrees, for instance to + animate how an item rotates several times. + + \sa QGraphicsTransform, QGraphicsItem::setRotation(), QTransform::rotate() */ class QGraphicsRotationPrivate : public QGraphicsTransformPrivate @@ -291,7 +360,7 @@ public: }; /*! - Constructs a new graphics rotation with \a parent. + Constructs a new QGraphicsRotation with the given \a parent. */ QGraphicsRotation::QGraphicsRotation(QObject *parent) : QGraphicsTransform(*new QGraphicsRotationPrivate, parent) @@ -299,7 +368,7 @@ QGraphicsRotation::QGraphicsRotation(QObject *parent) } /*! - \internal + \internal */ QGraphicsRotation::QGraphicsRotation(QGraphicsRotationPrivate &p, QObject *parent) : QGraphicsTransform(p, parent) @@ -307,24 +376,26 @@ QGraphicsRotation::QGraphicsRotation(QGraphicsRotationPrivate &p, QObject *paren } /*! - Destructs the object + Destroys the graphics rotation. */ QGraphicsRotation::~QGraphicsRotation() { } /*! - \property QGraphicsRotation::origin - The origin around which this object will rotate the graphics item. + \property QGraphicsRotation::origin + \brief the origin of the rotation. - The \a origin is in other words the fixed point for the transformation. + All rotations will be done relative to this point (i.e., this point + will stay fixed, relative to the parent, when the item is rotated). + + \sa angle */ QPointF QGraphicsRotation::origin() const { Q_D(const QGraphicsRotation); return d->origin; } - void QGraphicsRotation::setOrigin(const QPointF &point) { Q_D(QGraphicsRotation); @@ -334,22 +405,22 @@ void QGraphicsRotation::setOrigin(const QPointF &point) } /*! - \fn QGraphicsRotation::originChanged() + \property QGraphicsRotation::angle + \brief the angle for clockwise rotation, in degrees. - This signal is emitted whenever the origin of the object - changes. -*/ + The angle can be any real number; the default value is 0.0. A value of 180 + will rotate 180 degrees, clockwise. If you provide a negative number, the + item will be rotated counter-clockwise. Normally the rotation angle will be + in the range (-360, 360), but you can also provide numbers outside of this + range (e.g., a angle of 370 degrees gives the same result as 10 degrees). -/*! - \property QGraphicsRotation::angle - The angle, in degrees, of the rotation. + \sa origin */ qreal QGraphicsRotation::angle() const { Q_D(const QGraphicsRotation); return d->angle; } - void QGraphicsRotation::setAngle(qreal angle) { Q_D(QGraphicsRotation); @@ -361,33 +432,50 @@ void QGraphicsRotation::setAngle(qreal angle) } /*! - \fn void QGraphicsRotation::angleChanged() - - This signal is emitted whenever the angle of the object - changes. -*/ - -/*! - \reimp + \reimp */ void QGraphicsRotation::applyTo(QTransform *t) const { Q_D(const QGraphicsRotation); - if(d->angle) { + if (d->angle) { t->translate(d->origin.x(), d->origin.y()); t->rotate(d->angle); t->translate(-d->origin.x(), -d->origin.y()); } } +/*! + \fn QGraphicsRotation::originChanged() + + This signal is emitted whenever the origin has changed. + + \sa QGraphicsRotation::origin +*/ /*! - \class QGraphicsRotation3D - \brief The QGraphicsRotation3D class provides a way to rotate a graphics item in 3 dimensions. - \since 4.6 + \fn void QGraphicsRotation::angleChanged() - In addition to the origin and angle of a simple QGraphicsRotation, QGraphicsRotation3D contains - also an axis that describes around which axis in space the rotation is supposed to happen. + This signal is emitted whenever the angle has changed. + + \sa QGraphicsRotation::angle +*/ + +/*! + \class QGraphicsRotation3D + \brief The QGraphicsRotation3D class provides rotation in 3 dimensions. + \since 4.6 + + QGraphicsRotation3D extends QGraphicsRotation with the ability to rotate + around a given axis. + + You can provide the desired axis by assigning a QVector3D to the axis + property. The angle property, which is provided by QGraphicsRotation, now + describes the number of degrees to rotate around this axis. + + By default the axis is (0, 0, 1), giving QGraphicsRotation3D the same + default behavior as QGraphicsRotation (i.e., rotation around the Z axis). + + \sa QGraphicsTransform, QGraphicsItem::setRotation(), QTransform::rotate() */ class QGraphicsRotation3DPrivate : public QGraphicsRotationPrivate @@ -399,7 +487,7 @@ public: }; /*! - Constructs a new 3D rotation with \a parent. + Constructs a new QGraphicsRotation3D with the given \a parent. */ QGraphicsRotation3D::QGraphicsRotation3D(QObject *parent) : QGraphicsRotation(*new QGraphicsRotation3DPrivate, parent) @@ -407,7 +495,7 @@ QGraphicsRotation3D::QGraphicsRotation3D(QObject *parent) } /*! - Destroys the object + Destroys the 3D graphics rotation. */ QGraphicsRotation3D::~QGraphicsRotation3D() { @@ -415,15 +503,22 @@ QGraphicsRotation3D::~QGraphicsRotation3D() /*! \property QGraphicsRotation3D::axis + \brief a rotation axis, specified by a vector in 3D space. + + This can be any axis in 3D space. By default the axis is (0, 0, 1), + which is aligned with the Z axis and provides the same behavior + for the rotation angle as QGraphicsRotation. If you provide another + axis, QGraphicsRotation3D will provide a transformation that rotates + around this axis. For example, if you would like to rotate an item + around its X axis, you could pass (1, 0, 0) as the axis. - A rotation axis is specified by a vector in 3D space. + \sa QTransform, QGraphicsRotation::angle */ QVector3D QGraphicsRotation3D::axis() { Q_D(QGraphicsRotation3D); return d->axis; } - void QGraphicsRotation3D::setAxis(const QVector3D &axis) { Q_D(QGraphicsRotation3D); @@ -431,17 +526,10 @@ void QGraphicsRotation3D::setAxis(const QVector3D &axis) update(); } -/*! - \fn void QGraphicsRotation3D::axisChanged() - - This signal is emitted whenever the axis of the object - changes. -*/ - -const qreal inv_dist_to_plane = 1. / 1024.; +static const qreal inv_dist_to_plane = 1. / 1024.; /*! - \reimp + \reimp */ void QGraphicsRotation3D::applyTo(QTransform *t) const { @@ -474,6 +562,12 @@ void QGraphicsRotation3D::applyTo(QTransform *t) const t->translate(-d->origin.x(), -d->origin.y()); } +/*! + \fn void QGraphicsRotation3D::axisChanged() + + This signal is emitted whenever the axis of the object changes. +*/ + #include "moc_qgraphicstransform.cpp" QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicstransform.h b/src/gui/graphicsview/qgraphicstransform.h index b5d8d84..d7c07d0 100644 --- a/src/gui/graphicsview/qgraphicstransform.h +++ b/src/gui/graphicsview/qgraphicstransform.h @@ -70,6 +70,7 @@ protected Q_SLOTS: protected: QGraphicsTransform(QGraphicsTransformPrivate &p, QObject *parent); + private: friend class QGraphicsItem; friend class QGraphicsItemPrivate; diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index bf348af..b9c36dc 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -25,7 +25,9 @@ HEADERS += \ image/qpixmapcache_p.h \ image/qpixmapdata_p.h \ image/qpixmapdatafactory_p.h \ - image/qpixmapfilter_p.h + image/qpixmapfilter_p.h \ + image/qimagepixmapcleanuphooks_p.h \ + SOURCES += \ image/qbitmap.cpp \ @@ -47,6 +49,8 @@ SOURCES += \ image/qmovie.cpp \ image/qpixmap_raster.cpp \ image/qnativeimage.cpp \ + image/qimagepixmapcleanuphooks.cpp \ + win32 { SOURCES += image/qpixmap_win.cpp diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index e8ca7a9..bb77fb5 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -49,6 +49,7 @@ #include "qimagewriter.h" #include "qstringlist.h" #include "qvariant.h" +#include "qimagepixmapcleanuphooks_p.h" #include <ctype.h> #include <stdlib.h> #include <limits.h> @@ -106,14 +107,6 @@ static inline bool checkPixelSize(const QImage::Format format) } -// ### Qt 5: remove -typedef void (*_qt_image_cleanup_hook)(int); -Q_GUI_EXPORT _qt_image_cleanup_hook qt_image_cleanup_hook = 0; - -// ### Qt 5: rename -typedef void (*_qt_image_cleanup_hook_64)(qint64); -Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64 = 0; - static QImage rotated90(const QImage &src); static QImage rotated180(const QImage &src); static QImage rotated270(const QImage &src); @@ -257,8 +250,8 @@ QImageData * QImageData::create(const QSize &size, QImage::Format format, int nu QImageData::~QImageData() { - if (is_cached && qt_image_cleanup_hook_64) - qt_image_cleanup_hook_64((((qint64) ser_no) << 32) | ((qint64) detach_no)); + if (is_cached) + QImagePixmapCleanupHooks::executeImageHooks((((qint64) ser_no) << 32) | ((qint64) detach_no)); delete paintEngine; if (data && own_data) free(data); @@ -1342,8 +1335,8 @@ QImage::operator QVariant() const void QImage::detach() { if (d) { - if (d->is_cached && qt_image_cleanup_hook_64 && d->ref == 1) - qt_image_cleanup_hook_64(cacheKey()); + if (d->is_cached && d->ref == 1) + QImagePixmapCleanupHooks::executeImageHooks(cacheKey()); if (d->ref != 1 || d->ro_data) *this = copy(); diff --git a/src/gui/image/qimagepixmapcleanuphooks.cpp b/src/gui/image/qimagepixmapcleanuphooks.cpp new file mode 100644 index 0000000..7d1c5fb --- /dev/null +++ b/src/gui/image/qimagepixmapcleanuphooks.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qimagepixmapcleanuphooks_p.h" +#include "qpixmapdata_p.h" + + +// Legacy, single instance hooks: ### Qt 5: remove +typedef void (*_qt_pixmap_cleanup_hook)(int); +typedef void (*_qt_pixmap_cleanup_hook_64)(qint64); +typedef void (*_qt_image_cleanup_hook)(int); +Q_GUI_EXPORT _qt_pixmap_cleanup_hook qt_pixmap_cleanup_hook = 0; +Q_GUI_EXPORT _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64 = 0; +Q_GUI_EXPORT _qt_image_cleanup_hook qt_image_cleanup_hook = 0; +Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64 = 0; + + +QImagePixmapCleanupHooks* qt_image_and_pixmap_cleanup_hooks = 0; + + +QImagePixmapCleanupHooks::QImagePixmapCleanupHooks() +{ + qt_image_and_pixmap_cleanup_hooks = this; +} + +QImagePixmapCleanupHooks *QImagePixmapCleanupHooks::instance() +{ + if (!qt_image_and_pixmap_cleanup_hooks) + qt_image_and_pixmap_cleanup_hooks = new QImagePixmapCleanupHooks; + return qt_image_and_pixmap_cleanup_hooks; +} + +void QImagePixmapCleanupHooks::addPixmapHook(_qt_pixmap_cleanup_hook_pm hook) +{ + pixmapHooks.append(hook); +} + +void QImagePixmapCleanupHooks::addImageHook(_qt_image_cleanup_hook_64 hook) +{ + imageHooks.append(hook); +} + +void QImagePixmapCleanupHooks::removePixmapHook(_qt_pixmap_cleanup_hook_pm hook) +{ + pixmapHooks.removeAll(hook); +} + +void QImagePixmapCleanupHooks::removeImageHook(_qt_image_cleanup_hook_64 hook) +{ + imageHooks.removeAll(hook); +} + + +void QImagePixmapCleanupHooks::executePixmapHooks(QPixmap* pm) +{ + for (int i = 0; i < qt_image_and_pixmap_cleanup_hooks->pixmapHooks.count(); ++i) + qt_image_and_pixmap_cleanup_hooks->pixmapHooks[i](pm); + + if (qt_pixmap_cleanup_hook_64) + qt_pixmap_cleanup_hook_64(pm->cacheKey()); +} + + +void QImagePixmapCleanupHooks::executeImageHooks(qint64 key) +{ + for (int i = 0; i < qt_image_and_pixmap_cleanup_hooks->imageHooks.count(); ++i) + qt_image_and_pixmap_cleanup_hooks->imageHooks[i](key); + + if (qt_image_cleanup_hook_64) + qt_image_cleanup_hook_64(key); +} + diff --git a/src/gui/image/qimagepixmapcleanuphooks_p.h b/src/gui/image/qimagepixmapcleanuphooks_p.h new file mode 100644 index 0000000..e765e69 --- /dev/null +++ b/src/gui/image/qimagepixmapcleanuphooks_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QIMAGEPIXMAP_CLEANUPHOOKS_P_H +#define QIMAGEPIXMAP_CLEANUPHOOKS_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 <QtGui/qpixmap.h> + +QT_BEGIN_NAMESPACE + +typedef void (*_qt_image_cleanup_hook_64)(qint64); +typedef void (*_qt_pixmap_cleanup_hook_pm)(QPixmap*); + +class QImagePixmapCleanupHooks; +extern QImagePixmapCleanupHooks* qt_image_and_pixmap_cleanup_hooks; + +class Q_GUI_EXPORT QImagePixmapCleanupHooks +{ +public: + QImagePixmapCleanupHooks(); + + static QImagePixmapCleanupHooks *instance(); + + void addPixmapHook(_qt_pixmap_cleanup_hook_pm); + void addImageHook(_qt_image_cleanup_hook_64); + + void removePixmapHook(_qt_pixmap_cleanup_hook_pm); + void removeImageHook(_qt_image_cleanup_hook_64); + + static void executePixmapHooks(QPixmap*); + static void executeImageHooks(qint64 key); + +private: + QList<_qt_image_cleanup_hook_64> imageHooks; + QList<_qt_pixmap_cleanup_hook_pm> pixmapHooks; +}; + +QT_END_NAMESPACE + +#endif // QIMAGEPIXMAP_CLEANUPHOOKS_P_H diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 18829f4..82835d5 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -43,6 +43,7 @@ #include "qpixmap.h" #include "qpixmapdata_p.h" +#include "qimagepixmapcleanuphooks_p.h" #include "qbitmap.h" #include "qcolormap.h" @@ -81,14 +82,6 @@ QT_BEGIN_NAMESPACE // ### Qt 5: remove -typedef void (*_qt_pixmap_cleanup_hook)(int); -Q_GUI_EXPORT _qt_pixmap_cleanup_hook qt_pixmap_cleanup_hook = 0; - -// ### Qt 5: rename -typedef void (*_qt_pixmap_cleanup_hook_64)(qint64); -Q_GUI_EXPORT _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64 = 0; - -// ### Qt 5: remove Q_GUI_EXPORT qint64 qt_pixmap_id(const QPixmap &pixmap) { return pixmap.cacheKey(); @@ -656,7 +649,7 @@ void QPixmap::resize_helper(const QSize &s) QX11PixmapData *x11Data = data->classId() == QPixmapData::X11Class ? static_cast<QX11PixmapData*>(data) : 0; if (x11Data) { pm.x11SetScreen(x11Data->xinfo.screen()); - uninit = x11Data->uninit; + uninit = x11Data->flags & QX11PixmapData::Uninitialized; } #elif defined(Q_WS_MAC) QMacPixmapData *macData = data->classId() == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data) : 0; @@ -1357,8 +1350,8 @@ bool QPixmap::isDetached() const void QPixmap::deref() { if (data && !data->ref.deref()) { // Destroy image if last ref - if (data->is_cached && qt_pixmap_cleanup_hook_64) - qt_pixmap_cleanup_hook_64(cacheKey()); + if (data->is_cached) + QImagePixmapCleanupHooks::executePixmapHooks(this); delete data; data = 0; } @@ -1897,9 +1890,6 @@ int QPixmap::defaultDepth() #endif } -typedef void (*_qt_pixmap_cleanup_hook_64)(qint64); -extern _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64; - /*! Detaches the pixmap from shared pixmap data. @@ -1925,8 +1915,8 @@ void QPixmap::detach() rasterData->image.detach(); } - if (data->is_cached && qt_pixmap_cleanup_hook_64 && data->ref == 1) - qt_pixmap_cleanup_hook_64(cacheKey()); + if (data->is_cached && data->ref == 1) + QImagePixmapCleanupHooks::executePixmapHooks(this); #if defined(Q_WS_MAC) QMacPixmapData *macData = id == QPixmapData::MacClass ? static_cast<QMacPixmapData*>(data) : 0; @@ -1946,7 +1936,7 @@ void QPixmap::detach() #if defined(Q_WS_X11) if (data->classId() == QPixmapData::X11Class) { QX11PixmapData *d = static_cast<QX11PixmapData*>(data); - d->uninit = false; + d->flags &= ~QX11PixmapData::Uninitialized; // reset the cache data if (d->hd2) { diff --git a/src/gui/image/qpixmap_mac.cpp b/src/gui/image/qpixmap_mac.cpp index 45392f1..5568898 100644 --- a/src/gui/image/qpixmap_mac.cpp +++ b/src/gui/image/qpixmap_mac.cpp @@ -1178,6 +1178,7 @@ QPixmap QPixmap::fromMacCGImageRef(CGImageRef image) const size_t w = CGImageGetWidth(image), h = CGImageGetHeight(image); QPixmap ret(w, h); + ret.fill(Qt::transparent); CGRect rect = CGRectMake(0, 0, w, h); CGContextRef ctx = qt_mac_cg_context(&ret); qt_mac_drawCGImage(ctx, &rect, image); diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp index 86cf515..be3d070 100644 --- a/src/gui/image/qpixmap_x11.cpp +++ b/src/gui/image/qpixmap_x11.cpp @@ -313,7 +313,7 @@ int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0; QX11PixmapData::QX11PixmapData(PixelType type) : QPixmapData(type, X11Class), hd(0), - uninit(true), read_only(false), x11_mask(0), picture(0), mask_picture(0), hd2(0), + flags(Uninitialized), x11_mask(0), picture(0), mask_picture(0), hd2(0), gl_surface(0), share_mode(QPixmap::ImplicitlyShared), pengine(0) { } @@ -1217,7 +1217,7 @@ void QX11PixmapData::release() XFreePixmap(xinfo.display(), hd2); hd2 = 0; } - if (!read_only) + if (!(flags & Readonly)) XFreePixmap(xinfo.display(), hd); hd = 0; } @@ -1876,7 +1876,7 @@ QPixmap QX11PixmapData::transformed(const QTransform &transform, } else { // color pixmap QPixmap pm; QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(pm.data); - x11Data->uninit = false; + x11Data->flags &= ~QX11PixmapData::Uninitialized; x11Data->xinfo = xinfo; x11Data->d = d; x11Data->w = w; @@ -2018,7 +2018,7 @@ QPixmap QPixmap::grabWindow(WId window, int x, int y, int w, int h) QPixmap pm(data); - data->uninit = false; + data->flags &= ~QX11PixmapData::Uninitialized; pm.x11SetScreen(scr); GC gc = XCreateGC(dpy, pm.handle(), 0, 0); @@ -2060,7 +2060,7 @@ QPaintEngine* QX11PixmapData::paintEngine() const { QX11PixmapData *that = const_cast<QX11PixmapData*>(this); - if (read_only && share_mode == QPixmap::ImplicitlyShared) { + if ((flags & Readonly) && share_mode == QPixmap::ImplicitlyShared) { // if someone wants to draw onto us, copy the shared contents // and turn it into a fully fledged QPixmap ::Pixmap hd_copy = XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()), @@ -2082,7 +2082,7 @@ QPaintEngine* QX11PixmapData::paintEngine() const XFreeGC(X11->display, gc); } that->hd = hd_copy; - that->read_only = false; + that->flags &= ~QX11PixmapData::Readonly; } if (!that->pengine) @@ -2133,7 +2133,7 @@ void QX11PixmapData::copy(const QPixmapData *data, const QRect &rect) setSerialNumber(++qt_pixmap_serial); - uninit = false; + flags &= ~Uninitialized; xinfo = x11Data->xinfo; d = x11Data->d; w = rect.width(); @@ -2201,7 +2201,7 @@ void QX11PixmapData::convertToARGB32(bool preserveContents) return; // Q_ASSERT(count == 1); - if (read_only && share_mode == QPixmap::ExplicitlyShared) + if ((flags & Readonly) && share_mode == QPixmap::ExplicitlyShared) return; Pixmap pm = XCreatePixmap(X11->display, RootWindow(X11->display, xinfo.screen()), @@ -2211,10 +2211,10 @@ void QX11PixmapData::convertToARGB32(bool preserveContents) if (picture) { if (preserveContents) XRenderComposite(X11->display, PictOpSrc, picture, 0, p, 0, 0, 0, 0, 0, 0, w, h); - if (!read_only) + if (!(flags & Readonly)) XRenderFreePicture(X11->display, picture); } - if (hd && !read_only) + if (hd && !(flags & Readonly)) XFreePixmap(X11->display, hd); if (x11_mask) { XFreePixmap(X11->display, x11_mask); @@ -2252,9 +2252,8 @@ QPixmap QPixmap::fromX11Pixmap(Qt::HANDLE pixmap, QPixmap::ShareMode mode) QX11PixmapData *data = new QX11PixmapData(depth == 1 ? QPixmapData::BitmapType : QPixmapData::PixmapType); data->setSerialNumber(++qt_pixmap_serial); - data->read_only = true; + data->flags = QX11PixmapData::Readonly; data->share_mode = mode; - data->uninit = false; data->w = width; data->h = height; data->is_null = (width <= 0 || height <= 0); diff --git a/src/gui/image/qpixmap_x11_p.h b/src/gui/image/qpixmap_x11_p.h index 3de9a0f..835fea4 100644 --- a/src/gui/image/qpixmap_x11_p.h +++ b/src/gui/image/qpixmap_x11_p.h @@ -99,6 +99,7 @@ private: friend class QX11PaintEngine; friend class QX11WindowSurface; friend class QRasterWindowSurface; + friend class QGLContextPrivate; // Needs to access xinfo, gl_surface & flags void release(); @@ -108,14 +109,21 @@ private: Qt::HANDLE hd; - uint uninit : 1; - uint read_only : 1; + enum Flag { + NoFlags = 0x0, + Uninitialized = 0x1, + Readonly = 0x2, + InvertedWhenBoundToTexture = 0x4, + GlSurfaceCreatedWithAlpha = 0x8 + }; + uint flags; QX11Info xinfo; Qt::HANDLE x11_mask; Qt::HANDLE picture; Qt::HANDLE mask_picture; Qt::HANDLE hd2; // sorted in the default display depth + Qt::HANDLE gl_surface; #ifndef QT_NO_XRENDER void convertToARGB32(bool preserveContents = true); #endif diff --git a/src/gui/image/qpixmapdata_p.h b/src/gui/image/qpixmapdata_p.h index 29dafaf..32b419e 100644 --- a/src/gui/image/qpixmapdata_p.h +++ b/src/gui/image/qpixmapdata_p.h @@ -116,6 +116,7 @@ private: friend class QPixmap; friend class QGLContextPrivate; friend class QX11PixmapData; + friend class QGLTextureCache; //Needs to check the reference count QAtomicInt ref; int detach_no; diff --git a/src/gui/itemviews/qabstractitemview.cpp b/src/gui/itemviews/qabstractitemview.cpp index 8b6b5cb..94569ec 100644 --- a/src/gui/itemviews/qabstractitemview.cpp +++ b/src/gui/itemviews/qabstractitemview.cpp @@ -960,6 +960,7 @@ void QAbstractItemView::reset() d->currentIndexSet = false; setState(NoState); setRootIndex(QModelIndex()); + d->selectionModel->reset(); } /*! diff --git a/src/gui/itemviews/qitemdelegate.cpp b/src/gui/itemviews/qitemdelegate.cpp index 336ca79..bd6dc62 100644 --- a/src/gui/itemviews/qitemdelegate.cpp +++ b/src/gui/itemviews/qitemdelegate.cpp @@ -1226,21 +1226,19 @@ bool QItemDelegate::eventFilter(QObject *object, QEvent *event) } else if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) { //the Hide event will take care of he editors that are in fact complete dialogs if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) { - if (editor->isAncestorOf(QApplication::focusWidget())) - return false; // don't worry about focus changes internally in the editor - + QWidget *w = QApplication::focusWidget(); + while (w) { // don't worry about focus changes internally in the editor + if (w == editor) + return false; + w = w->parentWidget(); + } #ifndef QT_NO_DRAGANDDROP // The window may lose focus during an drag operation. // i.e when dragging involves the taskbar on Windows. if (QDragManager::self() && QDragManager::self()->object != 0) return false; #endif - // Opening a modal dialog will start a new eventloop - // that will process the deleteLater event. - if (QApplication::activeModalWidget() - && !QApplication::activeModalWidget()->isAncestorOf(editor) - && qobject_cast<QDialog*>(QApplication::activeModalWidget())) - return false; + emit commitData(editor); emit closeEditor(editor, NoHint); } @@ -1298,8 +1296,14 @@ bool QItemDelegate::editorEvent(QEvent *event, return false; } - Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked + Qt::CheckState state; + if ( flags & Qt::ItemIsTristate ) { + state = static_cast<Qt::CheckState>( (value.toInt() + 1) % 3 ); + } else { + state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + } + return model->setData(index, state, Qt::CheckStateRole); } diff --git a/src/gui/itemviews/qstyleditemdelegate.cpp b/src/gui/itemviews/qstyleditemdelegate.cpp index bd8fdac..34667ee 100644 --- a/src/gui/itemviews/qstyleditemdelegate.cpp +++ b/src/gui/itemviews/qstyleditemdelegate.cpp @@ -674,22 +674,19 @@ bool QStyledItemDelegate::eventFilter(QObject *object, QEvent *event) } else if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) { //the Hide event will take care of he editors that are in fact complete dialogs if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) { - if (editor->isAncestorOf(QApplication::focusWidget())) - return false; // don't worry about focus changes internally in the editor - + QWidget *w = QApplication::focusWidget(); + while (w) { // don't worry about focus changes internally in the editor + if (w == editor) + return false; + w = w->parentWidget(); + } #ifndef QT_NO_DRAGANDDROP // The window may lose focus during an drag operation. // i.e when dragging involves the taskbar on Windows. if (QDragManager::self() && QDragManager::self()->object != 0) return false; #endif - // Opening a modal dialog will start a new eventloop - // that will process the deleteLater event. - QWidget *activeModalWidget = QApplication::activeModalWidget(); - if (activeModalWidget - && !activeModalWidget->isAncestorOf(editor) - && qobject_cast<QDialog*>(activeModalWidget)) - return false; + emit commitData(editor); emit closeEditor(editor, NoHint); } @@ -749,8 +746,13 @@ bool QStyledItemDelegate::editorEvent(QEvent *event, return false; } - Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked + Qt::CheckState state; + if ( flags & Qt::ItemIsTristate ) { + state = static_cast<Qt::CheckState>( (value.toInt() + 1) % 3 ); + } else { + state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked ? Qt::Unchecked : Qt::Checked); + } return model->setData(index, state, Qt::CheckStateRole); } diff --git a/src/gui/kernel/qaction.h b/src/gui/kernel/qaction.h index 133fab4..cb681c4 100644 --- a/src/gui/kernel/qaction.h +++ b/src/gui/kernel/qaction.h @@ -68,24 +68,24 @@ class Q_GUI_EXPORT QAction : public QObject Q_ENUMS(MenuRole) Q_ENUMS(Priority) - Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable) + Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable NOTIFY changed) Q_PROPERTY(bool checked READ isChecked WRITE setChecked DESIGNABLE isCheckable NOTIFY toggled) - Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) - Q_PROPERTY(QIcon icon READ icon WRITE setIcon) - Q_PROPERTY(QString text READ text WRITE setText) - Q_PROPERTY(QString iconText READ iconText WRITE setIconText) - Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip) - Q_PROPERTY(QString statusTip READ statusTip WRITE setStatusTip) - Q_PROPERTY(QString whatsThis READ whatsThis WRITE setWhatsThis) - Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY changed) + Q_PROPERTY(QIcon icon READ icon WRITE setIcon NOTIFY changed) + Q_PROPERTY(QString text READ text WRITE setText NOTIFY changed) + Q_PROPERTY(QString iconText READ iconText WRITE setIconText NOTIFY changed) + Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY changed) + Q_PROPERTY(QString statusTip READ statusTip WRITE setStatusTip NOTIFY changed) + Q_PROPERTY(QString whatsThis READ whatsThis WRITE setWhatsThis NOTIFY changed) + Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY changed) #ifndef QT_NO_SHORTCUT - Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut) - Q_PROPERTY(Qt::ShortcutContext shortcutContext READ shortcutContext WRITE setShortcutContext) - Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat) + Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut NOTIFY changed) + Q_PROPERTY(Qt::ShortcutContext shortcutContext READ shortcutContext WRITE setShortcutContext NOTIFY changed) + Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat NOTIFY changed) #endif - Q_PROPERTY(bool visible READ isVisible WRITE setVisible) - Q_PROPERTY(MenuRole menuRole READ menuRole WRITE setMenuRole) - Q_PROPERTY(bool iconVisibleInMenu READ isIconVisibleInMenu WRITE setIconVisibleInMenu) + Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY changed) + Q_PROPERTY(MenuRole menuRole READ menuRole WRITE setMenuRole NOTIFY changed) + Q_PROPERTY(bool iconVisibleInMenu READ isIconVisibleInMenu WRITE setIconVisibleInMenu NOTIFY changed) Q_PROPERTY(Priority priority READ priority WRITE setPriority) public: diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 5181689..c24ff49 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -179,11 +179,11 @@ QApplicationPrivate::~QApplicationPrivate() QApplication contains the main event loop, where all events from the window system and other sources are processed and dispatched. It also handles the - application's initialization and finalization, and provides session - management. In addition, it handles most system-wide and application-wide - settings. + application's initialization, finalization, and provides session + management. In addition, QApplication handles most of the system-wide and + application-wide settings. - For any GUI application using Qt, there is precisely one QApplication + For any GUI application using Qt, there is precisely \bold one QApplication object, no matter whether the application has 0, 1, 2 or more windows at any given time. For non-GUI Qt applications, use QCoreApplication instead, as it does not depend on the \l QtGui library. @@ -239,9 +239,9 @@ QApplicationPrivate::~QApplicationPrivate() saveState() for details. \endlist - The QApplication object does so much initialization. Hence, it \e{must} be + Since the QApplication object does so much initialization, it \e{must} be created before any other objects related to the user interface are created. - Since QApplication also deals with common command line arguments, it is + QApplication also deals with common command line arguments. Hence, it is usually a good idea to create it \e before any interpretation or modification of \c argv is done in the application itself. @@ -673,9 +673,9 @@ QApplication::QApplication(int &argc, char **argv, int _internal) On X11, the window system is initialized if \a GUIenabled is true. If \a GUIenabled is false, the application does not connect to the X server. - On Windows and Macintosh, currently the window system is always - initialized, regardless of the value of GUIenabled. This may change in - future versions of Qt. + On Windows and Mac OS, currently the window system is always initialized, + regardless of the value of GUIenabled. This may change in future versions + of Qt. The following example shows how to create an application that uses a graphical interface when available. @@ -1194,21 +1194,22 @@ bool QApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventLis \since 4.4 \brief defines a threshold for auto maximizing widgets - The auto maximize threshold is only available as part of Qt for Windows CE. + \bold{The auto maximize threshold is only available as part of Qt for + Windows CE.} This property defines a threshold for the size of a window as a percentage of the screen size. If the minimum size hint of a window exceeds the - threshold, calling show() will then cause the window to be maximized + threshold, calling show() will cause the window to be maximized automatically. - Setting the threshold to be 100 or greater means that it will cause it to - always be maximized. Setting it to be 50 means that the widget is maximized - if the vertical minimum size hint is at least 50% of the vertical screen - size. + Setting the threshold to 100 or greater means that the widget will always + be maximized. Alternatively, setting the threshold to 50 means that the + widget will be maximized only if the vertical minimum size hint is at least + 50% of the vertical screen size. - If -1 is specified then this will disable the feature. + Setting the threshold to -1 disables the feature. - On Windows CE the default is -1 (i.e. it is disabled). + On Windows CE the default is -1 (i.e., it is disabled). On Windows Mobile the default is 40. */ @@ -1217,9 +1218,9 @@ bool QApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventLis \since 4.5 \brief toggles automatic SIP (software input panel) visibility - The auto SIP property is only available as part of Qt for Windows CE. + \bold{The auto SIP property is only available as part of Qt for Windows CE.} - Set this property to true to automatically display the SIP when entering + Set this property to \c true to automatically display the SIP when entering widgets that accept keyboard input. This property only affects widgets with the WA_InputMethodEnabled attribute set. */ @@ -1517,7 +1518,7 @@ int QApplication::colorSpec() strategy. Use this option if your application uses buttons, menus, texts and pixmaps with few colors. With this option, the application uses system global colors. This works fine for most - applications under X11, but on Windows machines it may cause + applications under X11, but on the Windows platform, it may cause dithering of non-standard colors. \o QApplication::CustomColor. Use this option if your application needs a small number of custom colors. On X11, this option is the @@ -3513,12 +3514,12 @@ void QApplication::changeOverrideCursor(const QCursor &cursor) 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 - QApplication::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 QApplication::exec() call. + application's \c{main()} function. This is because, on some platforms the + QApplication::exec() call may not return. For example, on the Windows + platform, when the user logs off, the system terminates the process after Qt + closes all top-level windows. Hence, there is \e{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 QApplication::exec() call. \sa quitOnLastWindowClosed, quit(), exit(), processEvents(), QCoreApplication::exec() @@ -4051,7 +4052,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) touchEvent->setAccepted(eventAccepted); break; } - case QEvent::WinGesture: + case QEvent::NativeGesture: { // only propagate the first gesture event (after the GID_BEGIN) QWidget *w = static_cast<QWidget *>(receiver); @@ -4762,7 +4763,7 @@ bool QApplication::keypadNavigationEnabled() On Mac OS X, this works more at the application level and will cause the application icon to bounce in the dock. - On Windows this causes the window's taskbar entry to flash for a time. If + On Windows, this causes the window's taskbar entry to flash for a time. If \a msec is zero, the flashing will stop and the taskbar entry will turn a different color (currently orange). @@ -4779,24 +4780,22 @@ bool QApplication::keypadNavigationEnabled() caret display. Usually the text cursor is displayed for half the cursor flash time, then hidden for the same amount of time, but this may vary. - The default value on X11 is 1000 milliseconds. On Windows, the control - panel value is used. Widgets should not cache this value since it may be - changed at any time by the user changing the global desktop settings. + The default value on X11 is 1000 milliseconds. On Windows, the + \gui{Control Panel} value is used and setting this property sets the cursor + flash time for all applications. - \note On Microsoft Windows, setting this property sets the cursor flash - time for all applications. + We recommend that widgets do not cache this value as it may change at any + time if the user changes the global desktop settings. */ /*! \property QApplication::doubleClickInterval - \brief the time limit in milliseconds that distinguishes a double click from two - consecutive mouse clicks - - The default value on X11 is 400 milliseconds. On Windows and Mac OS X, the - operating system's value is used. + \brief the time limit in milliseconds that distinguishes a double click + from two consecutive mouse clicks - On Microsoft Windows, calling this function sets the double click interval - for all applications. + The default value on X11 is 400 milliseconds. On Windows and Mac OS, the + operating system's value is used. However, on Windows, calling this + function sets the double click interval for all applications. */ /*! @@ -4805,7 +4804,7 @@ bool QApplication::keypadNavigationEnabled() from two consecutive key presses \since 4.2 - The default value on X11 is 400 milliseconds. On Windows and Mac OS X, the + The default value on X11 is 400 milliseconds. On Windows and Mac OS, the operating system's value is used. */ @@ -4870,7 +4869,7 @@ bool QApplication::keypadNavigationEnabled() You need not have a main widget; connecting lastWindowClosed() to quit() is an alternative. - For X11, this function also resizes and moves the main widget according + On X11, this function also resizes and moves the main widget according to the \e -geometry command-line option, so you should set the default geometry (using \l QWidget::setGeometry()) before calling setMainWidget(). @@ -4899,9 +4898,10 @@ bool QApplication::keypadNavigationEnabled() Application cursors are stored on an internal stack. setOverrideCursor() pushes the cursor onto the stack, and restoreOverrideCursor() pops the active cursor off the stack. changeOverrideCursor() changes the curently - active application override cursor. Every setOverrideCursor() must - eventually be followed by a corresponding restoreOverrideCursor(), - otherwise the stack will never be emptied. + active application override cursor. + + Every setOverrideCursor() must eventually be followed by a corresponding + restoreOverrideCursor(), otherwise the stack will never be emptied. Example: \snippet doc/src/snippets/code/src_gui_kernel_qapplication_x11.cpp 0 @@ -5082,19 +5082,19 @@ bool QApplicationPrivate::shouldSetFocus(QWidget *w, Qt::FocusPolicy policy) */ /*! \fn QDecoration* QApplication::qwsSetDecoration(const QString &decoration) - \overload + \overload - Requests a QDecoration object for \a decoration from the QDecorationFactory. + Requests a QDecoration object for \a decoration from the + QDecorationFactory. - The string must be one of the QDecorationFactory::keys(). Keys are - case insensitive. + The string must be one of the QDecorationFactory::keys(). Keys are case + insensitive. - A later call to the QApplication constructor will override the - requested style when a "-style" option is passed in as a commandline - parameter. + A later call to the QApplication constructor will override the requested + style when a "-style" option is passed in as a commandline parameter. - Returns 0 if an unknown \a decoration is passed, otherwise the QStyle object - returned is set as the application's GUI style. + Returns 0 if an unknown \a decoration is passed, otherwise the QStyle object + returned is set as the application's GUI style. */ /*! diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm index beccfb0..ac132aa 100644 --- a/src/gui/kernel/qapplication_mac.mm +++ b/src/gui/kernel/qapplication_mac.mm @@ -1480,6 +1480,7 @@ QWidget *QApplicationPrivate::tryModalHelper_sys(QWidget *top) return top; } +#ifndef QT_MAC_USE_COCOA static bool qt_try_modal(QWidget *widget, EventRef event) { QWidget * top = 0; @@ -1513,6 +1514,7 @@ static bool qt_try_modal(QWidget *widget, EventRef event) #endif return !block_event; } +#endif OSStatus QApplicationPrivate::tabletProximityCallback(EventHandlerCallRef, EventRef carbonEvent, void *) @@ -2890,52 +2892,25 @@ bool QApplicationPrivate::canQuit() #endif } -void onApplicationWindowChangedActivation( QWidget*widget, bool activated ) +void onApplicationWindowChangedActivation(QWidget *widget, bool activated) { #if QT_MAC_USE_COCOA - QApplication *app = qApp; + if (!widget) + return; - if ( activated ) - { - if (QApplicationPrivate::app_style) - { + if (activated) { + if (QApplicationPrivate::app_style) { QEvent ev(QEvent::Style); qt_sendSpontaneousEvent(QApplicationPrivate::app_style, &ev); } - - if (widget && app_do_modal && !qt_try_modal(widget, NULL)) - return; - - if (widget && widget->window()->isVisible()) - { - QWidget *tlw = widget->window(); - - if (tlw->isWindow() && !(tlw->windowType() == Qt::Popup) - && !qt_mac_is_macdrawer(tlw) - && (!tlw->parentWidget() || tlw->isModal() || !(tlw->windowType() == Qt::Tool))) { - bool just_send_event = false; -#if 0 - WindowActivationScope scope; - if ( GetWindowActivationScope((OSWindowRef)wid, &scope) == noErr && - scope == kWindowActivationScopeIndependent) - { - if ( GetFrontWindowOfClass(kAllWindowClasses, true) != wid ) - just_send_event = true; - } -#endif - if (just_send_event) { - QEvent e(QEvent::WindowActivate); - qt_sendSpontaneousEvent(widget, &e); - } else { - app->setActiveWindow(tlw); - } - } - } + qApp->setActiveWindow(widget); } else { // deactivated - if (widget && QApplicationPrivate::active_window == widget) - app->setActiveWindow(0); + if (QApplicationPrivate::active_window == widget) + qApp->setActiveWindow(0); } + QMenuBar::macUpdateMenuBar(); + #else Q_UNUSED(widget); Q_UNUSED(activated); diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp index d5c820c..2bded5c 100644 --- a/src/gui/kernel/qapplication_win.cpp +++ b/src/gui/kernel/qapplication_win.cpp @@ -3735,22 +3735,22 @@ bool QETWidget::translateGestureEvent(const MSG &msg) alienWidget = 0; QWidget *widget = alienWidget ? alienWidget : this; - QWinGestureEvent event; + QNativeGestureEvent event; event.sequenceId = gi.dwSequenceID; event.position = QPoint(gi.ptsLocation.x, gi.ptsLocation.y); if (bResult) { switch (gi.dwID) { case GID_BEGIN: - // we are not interested in this type of event. + event.gestureType = QNativeGestureEvent::GestureBegin; break; case GID_END: - event.gestureType = QWinGestureEvent::GestureEnd; + event.gestureType = QNativeGestureEvent::GestureEnd; break; case GID_ZOOM: - event.gestureType = QWinGestureEvent::Pinch; + event.gestureType = QNativeGestureEvent::Pinch; break; case GID_PAN: - event.gestureType = QWinGestureEvent::Pan; + event.gestureType = QNativeGestureEvent::Pan; break; case GID_ROTATE: case GID_TWOFINGERTAP: @@ -3758,7 +3758,7 @@ bool QETWidget::translateGestureEvent(const MSG &msg) default: break; } - if (event.gestureType != QWinGestureEvent::None) + if (event.gestureType != QNativeGestureEvent::None) qt_sendSpontaneousEvent(widget, &event); } else { DWORD dwErr = GetLastError(); diff --git a/src/gui/kernel/qboxlayout.cpp b/src/gui/kernel/qboxlayout.cpp index 770f5bb..23d20ff 100644 --- a/src/gui/kernel/qboxlayout.cpp +++ b/src/gui/kernel/qboxlayout.cpp @@ -527,7 +527,7 @@ void QBoxLayoutPrivate::calcHfw(int w) You will almost always want to use QVBoxLayout and QHBoxLayout rather than QBoxLayout because of their convenient constructors. - \sa QGridLayout, QStackedLayout, {Layout Classes} + \sa QGridLayout, QStackedLayout, {Layout Management} */ /*! @@ -1295,7 +1295,7 @@ QBoxLayout::Direction QBoxLayout::direction() const \image qhboxlayout-with-5-children.png Horizontal box layout with five child widgets - \sa QVBoxLayout, QGridLayout, QStackedLayout, {Layout Classes}, {Basic Layouts Example} + \sa QVBoxLayout, QGridLayout, QStackedLayout, {Layout Management}, {Basic Layouts Example} */ @@ -1413,7 +1413,7 @@ QHBoxLayout::~QHBoxLayout() \image qvboxlayout-with-5-children.png Horizontal box layout with five child widgets - \sa QHBoxLayout, QGridLayout, QStackedLayout, {Layout Classes}, {Basic Layouts Example} + \sa QHBoxLayout, QGridLayout, QStackedLayout, {Layout Management}, {Basic Layouts Example} */ /*! diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 57c9117..1d352cb 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -327,7 +327,7 @@ extern "C" { return NSDragOperationNone; } else { // save the mouse position, used by draggingExited handler. - DnDParams *dndParams = [QCocoaView currentMouseEvent]; + DnDParams *dndParams = [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]; dndParams->activeDragEnterPos = windowPoint; // send a drag move event immediately after a drag enter event (as per documentation). QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); @@ -406,7 +406,7 @@ extern "C" { dragEnterSequence = -1; if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) { // try sending the leave event to the last view which accepted drag enter. - DnDParams *dndParams = [QCocoaView currentMouseEvent]; + DnDParams *dndParams = [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]; NSView *candidateView = [[[self window] contentView] hitTest:dndParams->activeDragEnterPos]; if (candidateView && candidateView != self) return [candidateView draggingExited:sender]; diff --git a/src/gui/kernel/qcursor.cpp b/src/gui/kernel/qcursor.cpp index 9cca0d6..0b47b6f 100644 --- a/src/gui/kernel/qcursor.cpp +++ b/src/gui/kernel/qcursor.cpp @@ -127,11 +127,11 @@ QT_BEGIN_NAMESPACE \o Qt::SizeAllCursor \o \c size_all \row \o \inlineimage cursor-busy.png \o Qt::BusyCursor \o \c left_ptr_watch - \o \inlineimage cursor-hsplit.png + \o \inlineimage cursor-vsplit.png \o Qt::SplitVCursor \o \c split_v \row \o \inlineimage cursor-forbidden.png \o Qt::ForbiddenCursor \o \c forbidden - \o \inlineimage cursor-vsplit.png + \o \inlineimage cursor-hsplit.png \o Qt::SplitHCursor \o \c split_h \row \o \inlineimage cursor-hand.png \o Qt::PointingHandCursor \o \c pointing_hand diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index bc3633c..0fc36e7 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -49,7 +49,6 @@ #include "qmime.h" #include "qdnd_p.h" #include "qevent_p.h" -#include "qgesture.h" QT_BEGIN_NAMESPACE diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h index 67441ea..92c4fc1 100644 --- a/src/gui/kernel/qevent_p.h +++ b/src/gui/kernel/qevent_p.h @@ -119,20 +119,30 @@ public: qreal pressure; }; -class QWinGestureEvent : public QEvent +class QNativeGestureEvent : public QEvent { public: enum Type { None, + GestureBegin, GestureEnd, Pan, Pinch }; - QWinGestureEvent() : QEvent(QEvent::WinGesture), gestureType(None), sequenceId(0) { } + QNativeGestureEvent() + : QEvent(QEvent::NativeGesture), gestureType(None) +#ifdef Q_WS_WIN + , sequenceId(0) +#endif + { + } + Type gestureType; +#ifdef Q_WS_WIN QPoint position; ulong sequenceId; +#endif }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qformlayout.cpp b/src/gui/kernel/qformlayout.cpp index 0b7656f..de33f93 100644 --- a/src/gui/kernel/qformlayout.cpp +++ b/src/gui/kernel/qformlayout.cpp @@ -689,16 +689,12 @@ void QFormLayoutPrivate::setupVerticalLayoutData(int width) // are split. maxLabelWidth = 0; if (!wrapAllRows) { - int maxFieldMinWidth = 0; //the maximum minimum size of the field for (int i = 0; i < rr; ++i) { const QFormLayoutItem *label = m_matrix(i, 0); const QFormLayoutItem *field = m_matrix(i, 1); - if (label && field && label->sideBySide) + if (label && (label->sizeHint.width() + (field ? field->minSize.width() : 0) <= width)) maxLabelWidth = qMax(maxLabelWidth, label->sizeHint.width()); - if (field) - maxFieldMinWidth = qMax(maxFieldMinWidth, field->minSize.width() + field->sbsHSpace); } - maxLabelWidth = qMin(maxLabelWidth, width - maxFieldMinWidth); } else { maxLabelWidth = width; } diff --git a/src/gui/kernel/qgesture.cpp b/src/gui/kernel/qgesture.cpp index d53b419..8075ad9 100644 --- a/src/gui/kernel/qgesture.cpp +++ b/src/gui/kernel/qgesture.cpp @@ -97,7 +97,7 @@ private: This is a base class, to create a custom gesture type, you should subclass it and implement its pure virtual functions. - \sa QPanGesture, QTapAndHoldGesture + \sa QPanGesture */ /*! \fn bool QGesture::filterEvent(QEvent *event) @@ -153,7 +153,7 @@ QGesture::QGesture(QObject *parent) : QObject(*new QGesturePrivate, parent) { if (parent) - installEventFilter(parent); + parent->installEventFilter(this); } /*! \internal @@ -162,7 +162,7 @@ QGesture::QGesture(QGesturePrivate &dd, QObject *parent) : QObject(dd, parent) { if (parent) - installEventFilter(parent); + parent->installEventFilter(this); } /*! @@ -205,51 +205,6 @@ void QGesture::setState(Qt::GestureState state) } /*! - \property QGesture::startPos - - \brief The start position of the gesture (if relevant). -*/ -QPoint QGesture::startPos() const -{ - return d_func()->startPos; -} - -void QGesture::setStartPos(const QPoint &point) -{ - d_func()->startPos = point; -} - -/*! - \property QGesture::lastPos - - \brief The last recorded position of the gesture (if relevant). -*/ -QPoint QGesture::lastPos() const -{ - return d_func()->lastPos; -} - -void QGesture::setLastPos(const QPoint &point) -{ - d_func()->lastPos = point; -} - -/*! - \property QGesture::pos - - \brief The current position of the gesture (if relevant). -*/ -QPoint QGesture::pos() const -{ - return d_func()->pos; -} - -void QGesture::setPos(const QPoint &point) -{ - d_func()->pos = point; -} - -/*! Sets the \a graphicsItem the gesture is filtering events for. The gesture will install an event filter to the \a graphicsItem and diff --git a/src/gui/kernel/qgesture.h b/src/gui/kernel/qgesture.h index 1cd9cae..0735160 100644 --- a/src/gui/kernel/qgesture.h +++ b/src/gui/kernel/qgesture.h @@ -64,10 +64,6 @@ class Q_GUI_EXPORT QGesture : public QObject Q_PROPERTY(Qt::GestureState state READ state) - Q_PROPERTY(QPoint startPos READ startPos WRITE setStartPos) - Q_PROPERTY(QPoint lastPos READ lastPos WRITE setLastPos) - Q_PROPERTY(QPoint pos READ pos WRITE setPos) - public: explicit QGesture(QObject *parent = 0); ~QGesture(); @@ -80,19 +76,13 @@ public: virtual void reset(); Qt::GestureState state() const; - void setState(Qt::GestureState state); - - QPoint startPos() const; - void setStartPos(const QPoint &point); - QPoint lastPos() const; - void setLastPos(const QPoint &point); - QPoint pos() const; - void setPos(const QPoint &point); protected: QGesture(QGesturePrivate &dd, QObject *parent); bool eventFilter(QObject*, QEvent*); + void setState(Qt::GestureState state); + Q_SIGNALS: void started(); void triggered(); diff --git a/src/gui/kernel/qgesture_p.h b/src/gui/kernel/qgesture_p.h index 99f572f..56eaee7 100644 --- a/src/gui/kernel/qgesture_p.h +++ b/src/gui/kernel/qgesture_p.h @@ -73,22 +73,11 @@ public: { } - void init(const QPoint &startPos, const QPoint &lastPos, - const QPoint &pos) - { - this->startPos = startPos; - this->lastPos = lastPos; - this->pos = pos; - } QGraphicsItem *graphicsItem; QGraphicsItem *eventFilterProxyGraphicsItem; Qt::GestureState state; - - QPoint startPos; - QPoint lastPos; - QPoint pos; }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qgridlayout.cpp b/src/gui/kernel/qgridlayout.cpp index 7ac874e..558f570 100644 --- a/src/gui/kernel/qgridlayout.cpp +++ b/src/gui/kernel/qgridlayout.cpp @@ -1046,7 +1046,7 @@ QRect QGridLayoutPrivate::cellRect(int row, int col) const the margin width for a top-level layout, or to the same as the parent layout. - \sa QBoxLayout, QStackedLayout, {Layout Classes}, {Basic Layouts Example} + \sa QBoxLayout, QStackedLayout, {Layout Management}, {Basic Layouts Example} */ diff --git a/src/gui/kernel/qlayout.cpp b/src/gui/kernel/qlayout.cpp index e750088..941db8f 100644 --- a/src/gui/kernel/qlayout.cpp +++ b/src/gui/kernel/qlayout.cpp @@ -83,7 +83,7 @@ static int menuBarHeightForWidth(QWidget *menubar, int w) For users of QLayout subclasses or of QMainWindow there is seldom any need to use the basic functions provided by QLayout, such as - setSizeConstraint() or setMenuBar(). See \l{Layout Classes} + setSizeConstraint() or setMenuBar(). See \l{Layout Management} for more information. To make your own layout manager, implement the functions @@ -98,7 +98,7 @@ static int menuBarHeightForWidth(QWidget *menubar, int w) Geometry management stops when the layout manager is deleted. - \sa QLayoutItem, {Layout Classes}, {Basic Layouts Example}, + \sa QLayoutItem, {Layout Management}, {Basic Layouts Example}, {Border Layout Example}, {Flow Layout Example} */ diff --git a/src/gui/kernel/qstandardgestures.cpp b/src/gui/kernel/qstandardgestures.cpp index c8b11c5..4753416 100644 --- a/src/gui/kernel/qstandardgestures.cpp +++ b/src/gui/kernel/qstandardgestures.cpp @@ -45,9 +45,14 @@ #include <qabstractscrollarea.h> #include <qscrollbar.h> #include <private/qapplication_p.h> +#include <private/qevent_p.h> QT_BEGIN_NAMESPACE +#ifdef Q_WS_WIN +QApplicationPrivate* getQApplicationPrivateInternal(); +#endif + /*! \class QPanGesture \since 4.6 @@ -68,7 +73,6 @@ QPanGesture::QPanGesture(QWidget *parent) { #ifdef Q_WS_WIN if (parent) { - QApplicationPrivate* getQApplicationPrivateInternal(); QApplicationPrivate *qAppPriv = getQApplicationPrivateInternal(); qAppPriv->widgetGestures[parent].pan = this; } @@ -93,9 +97,81 @@ bool QPanGesture::event(QEvent *event) break; } #endif + +#if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA) + Q_D(QPanGesture); + if (event->type() == QEvent::Timer) { + const QTimerEvent *te = static_cast<QTimerEvent *>(event); + if (te->timerId() == d->panFinishedTimer) { + killTimer(d->panFinishedTimer); + d->panFinishedTimer = 0; + d->lastOffset = QSize(0, 0); + setState(Qt::GestureFinished); + emit triggered(); + setState(Qt::NoGesture); + } + } +#endif + return QObject::event(event); } +bool QPanGesture::eventFilter(QObject *receiver, QEvent *event) +{ +#ifdef Q_WS_WIN + if (receiver->isWidgetType() && event->type() == QEvent::NativeGesture) { + QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event); + QApplicationPrivate *qAppPriv = getQApplicationPrivateInternal(); + QApplicationPrivate::WidgetStandardGesturesMap::iterator it; + it = qAppPriv->widgetGestures.find(static_cast<QWidget*>(receiver)); + if (it == qAppPriv->widgetGestures.end()) + return false; + QPanGesture *gesture = it.value().pan; + if (!gesture) + return false; + Qt::GestureState nextState = state(); + switch(ev->gestureType) { + case QNativeGestureEvent::GestureBegin: + // next we might receive the first gesture update event, so we + // prepare for it. + setState(Qt::GestureStarted); + return false; + case QNativeGestureEvent::Pan: + nextState = Qt::GestureUpdated; + break; + case QNativeGestureEvent::GestureEnd: + if (state() != QNativeGestureEvent::Pan) + return false; // some other gesture has ended + setState(Qt::GestureFinished); + nextState = Qt::GestureFinished; + break; + default: + return false; + } + QPanGesturePrivate *d = gesture->d_func(); + if (state() == Qt::GestureStarted) { + d->lastPosition = ev->position; + d->lastOffset = d->totalOffset = QSize(); + } else { + d->lastOffset = QSize(ev->position.x() - d->lastPosition.x(), + ev->position.y() - d->lastPosition.y()); + d->totalOffset += d->lastOffset; + } + d->lastPosition = ev->position; + + if (state() == Qt::GestureStarted) + emit gesture->started(); + emit gesture->triggered(); + if (state() == Qt::GestureFinished) + emit gesture->finished(); + event->accept(); + gesture->setState(nextState); + return true; + } +#endif + return QGesture::eventFilter(receiver, event); +} + /*! \internal */ bool QPanGesture::filterEvent(QEvent *event) { @@ -104,28 +180,34 @@ bool QPanGesture::filterEvent(QEvent *event) return false; const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); if (event->type() == QEvent::TouchBegin) { - d->touchPoints = ev->touchPoints(); - const QPoint p = ev->touchPoints().at(0).pos().toPoint(); - setStartPos(p); - setLastPos(p); - setPos(p); - return false; + QTouchEvent::TouchPoint p = ev->touchPoints().at(0); + d->lastPosition = p.pos().toPoint(); + d->lastOffset = d->totalOffset = QSize(); } else if (event->type() == QEvent::TouchEnd) { if (state() != Qt::NoGesture) { setState(Qt::GestureFinished); - setLastPos(pos()); - setPos(ev->touchPoints().at(0).pos().toPoint()); + if (!ev->touchPoints().isEmpty()) { + QTouchEvent::TouchPoint p = ev->touchPoints().at(0); + const QPoint pos = p.pos().toPoint(); + const QPoint lastPos = p.lastPos().toPoint(); + const QPoint startPos = p.startPos().toPoint(); + d->lastOffset = QSize(pos.x() - lastPos.x(), pos.y() - lastPos.y()); + d->totalOffset = QSize(pos.x() - startPos.x(), pos.y() - startPos.y()); + } emit triggered(); emit finished(); } setState(Qt::NoGesture); reset(); } else if (event->type() == QEvent::TouchUpdate) { - d->touchPoints = ev->touchPoints(); - QPointF pt = d->touchPoints.at(0).pos() - d->touchPoints.at(0).startPos(); - setLastPos(pos()); - setPos(ev->touchPoints().at(0).pos().toPoint()); - if (pt.x() > 10 || pt.y() > 10 || pt.x() < -10 || pt.y() < -10) { + QTouchEvent::TouchPoint p = ev->touchPoints().at(0); + const QPoint pos = p.pos().toPoint(); + const QPoint lastPos = p.lastPos().toPoint(); + const QPoint startPos = p.startPos().toPoint(); + d->lastOffset = QSize(pos.x() - lastPos.x(), pos.y() - lastPos.y()); + d->totalOffset = QSize(pos.x() - startPos.x(), pos.y() - startPos.y()); + if (d->totalOffset.width() > 10 || d->totalOffset.height() > 10 || + d->totalOffset.width() < -10 || d->totalOffset.height() < -10) { if (state() == Qt::NoGesture) setState(Qt::GestureStarted); else @@ -133,6 +215,36 @@ bool QPanGesture::filterEvent(QEvent *event) emit triggered(); } } +#ifdef Q_OS_MAC + else if (event->type() == QEvent::Wheel) { + // On Mac, there is really no native panning gesture. Instead, a two + // finger pan is delivered as mouse wheel events. Otoh, on Windows, you + // either get mouse wheel events or pan events. We have decided to make this + // the Qt behaviour as well, meaning that on Mac, wheel + // events will be masked away when listening for pan events. +#ifndef QT_MAC_USE_COCOA + // In Carbon we receive neither touch-, nor pan gesture events. + // So we create pan gestures by converting wheel events. After all, this + // is how things are supposed to work on mac in the first place. + const QWheelEvent *wev = static_cast<const QWheelEvent*>(event); + int offset = wev->delta() / -120; + d->lastOffset = wev->orientation() == Qt::Horizontal ? QSize(offset, 0) : QSize(0, offset); + + if (state() == Qt::NoGesture) { + setState(Qt::GestureStarted); + d->totalOffset = d->lastOffset; + } else { + setState(Qt::GestureUpdated); + d->totalOffset += d->lastOffset; + } + + killTimer(d->panFinishedTimer); + d->panFinishedTimer = startTimer(200); + emit triggered(); +#endif + return true; + } +#endif return false; } @@ -140,7 +252,14 @@ bool QPanGesture::filterEvent(QEvent *event) void QPanGesture::reset() { Q_D(QPanGesture); - d->touchPoints.clear(); + d->lastOffset = d->totalOffset = QSize(); + d->lastPosition = QPoint(); +#if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA) + if (d->panFinishedTimer) { + killTimer(d->panFinishedTimer); + d->panFinishedTimer = 0; + } +#endif } /*! @@ -150,8 +269,8 @@ void QPanGesture::reset() */ QSize QPanGesture::totalOffset() const { - QPoint pt = pos() - startPos(); - return QSize(pt.x(), pt.y()); + Q_D(const QPanGesture); + return d->totalOffset; } /*! @@ -162,93 +281,11 @@ QSize QPanGesture::totalOffset() const */ QSize QPanGesture::lastOffset() const { - QPoint pt = pos() - lastPos(); - return QSize(pt.x(), pt.y()); -} - -/*! - \class QTapAndHoldGesture - \since 4.6 - - \brief The QTapAndHoldGesture class represents a Tap-and-Hold gesture, - providing additional information. -*/ - -const int QTapAndHoldGesturePrivate::iterationCount = 40; -const int QTapAndHoldGesturePrivate::iterationTimeout = 50; - -/*! - Creates a new Tap and Hold gesture handler object and marks it as a child - of \a parent. - - On some platforms like Windows there is a system-wide tap and hold gesture - that cannot be overriden, hence the gesture might never trigger and default - context menu will be shown instead. -*/ -QTapAndHoldGesture::QTapAndHoldGesture(QWidget *parent) - : QGesture(*new QTapAndHoldGesturePrivate, parent) -{ -} - -/*! \internal */ -bool QTapAndHoldGesture::filterEvent(QEvent *event) -{ - Q_D(QTapAndHoldGesture); - if (!event->spontaneous()) - return false; - const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); - switch (event->type()) { - case QEvent::TouchBegin: { - if (d->timer.isActive()) - d->timer.stop(); - d->timer.start(QTapAndHoldGesturePrivate::iterationTimeout, this); - const QPoint p = ev->touchPoints().at(0).pos().toPoint(); - setStartPos(p); - setLastPos(p); - setPos(p); - break; - } - case QEvent::TouchUpdate: - if (ev->touchPoints().size() != 1) - reset(); - else if ((startPos() - ev->touchPoints().at(0).pos().toPoint()).manhattanLength() > 15) - reset(); - break; - case QEvent::TouchEnd: - reset(); - break; - default: - break; - } - return false; + Q_D(const QPanGesture); + return d->lastOffset; } -/*! \internal */ -void QTapAndHoldGesture::timerEvent(QTimerEvent *event) -{ - Q_D(QTapAndHoldGesture); - if (event->timerId() != d->timer.timerId()) - return; - if (d->iteration == QTapAndHoldGesturePrivate::iterationCount) { - d->timer.stop(); - setState(Qt::GestureFinished); - emit triggered(); - } else { - setState(Qt::GestureStarted); - emit triggered(); - } - ++d->iteration; -} +QT_END_NAMESPACE -/*! \internal */ -void QTapAndHoldGesture::reset() -{ - Q_D(QTapAndHoldGesture); - if (state() != Qt::NoGesture) - emit cancelled(); - setState(Qt::NoGesture); - d->timer.stop(); - d->iteration = 0; -} +#include "moc_qstandardgestures.cpp" -QT_END_NAMESPACE diff --git a/src/gui/kernel/qstandardgestures.h b/src/gui/kernel/qstandardgestures.h index 2234702..c734fba 100644 --- a/src/gui/kernel/qstandardgestures.h +++ b/src/gui/kernel/qstandardgestures.h @@ -71,29 +71,13 @@ public: QSize totalOffset() const; QSize lastOffset() const; -protected: +private: bool event(QEvent *event); + bool eventFilter(QObject *receiver, QEvent *event); -private: friend class QWidget; }; -class QTapAndHoldGesturePrivate; -class Q_GUI_EXPORT QTapAndHoldGesture : public QGesture -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QTapAndHoldGesture) - -public: - QTapAndHoldGesture(QWidget *parent); - - bool filterEvent(QEvent *event); - void reset(); - -protected: - void timerEvent(QTimerEvent *event); -}; - QT_END_NAMESPACE QT_END_HEADER diff --git a/src/gui/kernel/qstandardgestures_p.h b/src/gui/kernel/qstandardgestures_p.h index bb11c9f..0fe24ee 100644 --- a/src/gui/kernel/qstandardgestures_p.h +++ b/src/gui/kernel/qstandardgestures_p.h @@ -60,6 +60,8 @@ #include "qgesture.h" #include "qgesture_p.h" +#include "qstandardgestures.h" + QT_BEGIN_NAMESPACE class QPanGesturePrivate : public QGesturePrivate @@ -67,23 +69,20 @@ class QPanGesturePrivate : public QGesturePrivate Q_DECLARE_PUBLIC(QPanGesture) public: - QPanGesturePrivate() { } - - QList<QTouchEvent::TouchPoint> touchPoints; -}; + QPanGesturePrivate() + { +#if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA) + panFinishedTimer = 0; +#endif + } -class QTapAndHoldGesturePrivate : public QGesturePrivate -{ - Q_DECLARE_PUBLIC(QTapAndHoldGesture) - -public: - QTapAndHoldGesturePrivate() - : iteration(0) { } + QSize totalOffset; + QSize lastOffset; + QPoint lastPosition; - QBasicTimer timer; - int iteration; - static const int iterationCount; - static const int iterationTimeout; +#if defined(Q_OS_MAC) && !defined(QT_MAC_USE_COCOA) + int panFinishedTimer; +#endif }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 7026525..a827967 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -505,7 +505,7 @@ void QWidget::setAutoFillBackground(bool enabled) been outlined to indicate their full sizes. If you want to use a QWidget to hold child widgets you will usually want to - add a layout to the parent QWidget. See \l{Layout Classes} for more + add a layout to the parent QWidget. See \l{Layout Management} for more information. @@ -1375,6 +1375,11 @@ QWidget::~QWidget() // set all QPointers for this object to zero QObjectPrivate::clearGuards(this); + if (d->declarativeData) { + d->declarativeData->destroyed(this); + d->declarativeData = 0; // don't activate again in ~QObject + } + if (!d->children.isEmpty()) d->deleteChildren(); @@ -3491,11 +3496,16 @@ bool QWidgetPrivate::setMinimumSize_helper(int &minw, int &minh) minh = qMax(minh, 0); } createExtra(); - if (extra->minw == minw && extra->minh == minh) + int mw = minw, mh = minh; + if (mw == QWIDGETSIZE_MAX) + mw = 0; + if (mh == QWIDGETSIZE_MAX) + mh = 0; + if (extra->minw == mw && extra->minh == mh) return false; - extra->minw = minw; - extra->minh = minh; - extra->explicitMinSize = (minw ? Qt::Horizontal : 0) | (minh ? Qt::Vertical : 0); + extra->minw = mw; + extra->minh = mh; + extra->explicitMinSize = (mw ? Qt::Horizontal : 0) | (mh ? Qt::Vertical : 0); return true; } @@ -3555,7 +3565,8 @@ bool QWidgetPrivate::setMaximumSize_helper(int &maxw, int &maxh) return false; extra->maxw = maxw; extra->maxh = maxh; - extra->explicitMaxSize = (maxw != QWIDGETSIZE_MAX ? Qt::Horizontal : 0) | (maxh != QWIDGETSIZE_MAX ? Qt::Vertical : 0); + extra->explicitMaxSize = (maxw != QWIDGETSIZE_MAX ? Qt::Horizontal : 0) | + (maxh != QWIDGETSIZE_MAX ? Qt::Vertical : 0); return true; } @@ -3634,6 +3645,8 @@ void QWidget::setBaseSize(int basew, int baseh) This will override the default size constraints set by QLayout. + To remove constraints, set the size to QWIDGETSIZE_MAX. + Alternatively, if you want the widget to have a fixed size based on its contents, you can call QLayout::setSizeConstraint(QLayout::SetFixedSize); @@ -3675,7 +3688,8 @@ void QWidget::setFixedSize(int w, int h) else d->updateGeometry_helper(true); - resize(w, h); + if (w != QWIDGETSIZE_MAX || h != QWIDGETSIZE_MAX) + resize(w, h); } void QWidget::setMinimumWidth(int w) @@ -7925,59 +7939,6 @@ bool QWidget::event(QEvent *event) (void) QApplication::sendEvent(this, &mouseEvent); break; } -#ifdef Q_WS_WIN - case QEvent::WinGesture: { - QWinGestureEvent *ev = static_cast<QWinGestureEvent*>(event); - QApplicationPrivate *qAppPriv = qApp->d_func(); - QApplicationPrivate::WidgetStandardGesturesMap::iterator it; - it = qAppPriv->widgetGestures.find(this); - if (it != qAppPriv->widgetGestures.end()) { - Qt::GestureState state = Qt::GestureUpdated; - if (qAppPriv->lastGestureId == 0) - state = Qt::GestureStarted; - QWinGestureEvent::Type type = ev->gestureType; - if (ev->gestureType == QWinGestureEvent::GestureEnd) { - type = (QWinGestureEvent::Type)qAppPriv->lastGestureId; - state = Qt::GestureFinished; - } - - QGesture *gesture = 0; - switch (type) { - case QWinGestureEvent::Pan: { - QPanGesture *pan = it.value().pan; - gesture = pan; - if (state == Qt::GestureStarted) { - gesture->setStartPos(ev->position); - gesture->setLastPos(ev->position); - } else { - gesture->setLastPos(gesture->pos()); - } - gesture->setPos(ev->position); - break; - } - case QWinGestureEvent::Pinch: - break; - default: - break; - } - if (gesture) { - gesture->setState(state); - if (state == Qt::GestureStarted) - emit gesture->started(); - emit gesture->triggered(); - if (state == Qt::GestureFinished) - emit gesture->finished(); - event->accept(); - } - if (ev->gestureType == QWinGestureEvent::GestureEnd) { - qAppPriv->lastGestureId = 0; - } else { - qAppPriv->lastGestureId = type; - } - } - break; - } -#endif #ifndef QT_NO_PROPERTIES case QEvent::DynamicPropertyChange: { const QByteArray &propName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName(); @@ -8854,7 +8815,7 @@ QRegion QWidget::mask() const The layout manager sets the geometry of the widget's children that have been added to the layout. - \sa setLayout(), sizePolicy(), {Layout Classes} + \sa setLayout(), sizePolicy(), {Layout Management} */ QLayout *QWidget::layout() const { @@ -8884,7 +8845,7 @@ QLayout *QWidget::layout() const The QWidget will take ownership of \a layout. - \sa layout(), {Layout Classes} + \sa layout(), {Layout Management} */ void QWidget::setLayout(QLayout *l) @@ -9850,6 +9811,10 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) data->window_modality = (w && w->testAttribute(Qt::WA_GroupLeader)) ? Qt::WindowModal : Qt::ApplicationModal; + // Some window managers does not allow us to enter modal after the + // window is showing. Therefore, to be consistent, we cannot call + // QApplicationPrivate::enterModal(this) here. The window must be + // hidden before changing modality. } if (testAttribute(Qt::WA_WState_Created)) { // don't call setModal_sys() before create_sys() diff --git a/src/gui/kernel/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm index 70eea3a..78df09d 100644 --- a/src/gui/kernel/qwidget_mac.mm +++ b/src/gui/kernel/qwidget_mac.mm @@ -3100,7 +3100,12 @@ void QWidgetPrivate::update_sys(const QRegion &rgn) return; dirtyOnWidget += rgn; #ifndef QT_MAC_USE_COCOA - HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgn.toQDRgn()), true); + RgnHandle rgnHandle = rgn.toQDRgnForUpdate_sys(); + if (rgnHandle) + HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgnHandle), true); + else { + HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); // do a complete repaint on overflow. + } #else // Cocoa doesn't do regions, it seems more efficient to just update the bounding rect instead of a potential number of message passes for each rect. const QRect &boundingRect = rgn.boundingRect(); @@ -4584,6 +4589,7 @@ void QWidgetPrivate::setModal_sys() OSWindowRef windowRef = qt_mac_window_for(q); #ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; bool alreadySheet = [windowRef styleMask] & NSDocModalWindowMask; if (windowParent && q->windowModality() == Qt::WindowModal){ @@ -4660,31 +4666,40 @@ void QWidgetPrivate::setModal_sys() || (primaryWindow && primaryWindow->windowModality() == Qt::WindowModal)){ // Window should be window-modal (which implies a sheet). if (old_wclass != kSheetWindowClass){ - // We cannot convert a created window to a sheet. So we recreate the window: + // We cannot convert a created window to a sheet. + // So we recreate the window: recreateMacWindow(); return; } - } else if (!(q->data->window_flags & Qt::CustomizeWindowHint)) { - if (old_wclass == kDocumentWindowClass || old_wclass == kFloatingWindowClass || old_wclass == kUtilityWindowClass){ - // Only change the class to kMovableModalWindowClass if the no explicit jewels - // are set (kMovableModalWindowClass can't contain them), and the current window class - // can be converted to modal (according to carbon doc). Mind the order of - // HIWindowChangeClass and ChangeWindowAttributes. - WindowGroupRef group = GetWindowGroup(windowRef); - HIWindowChangeClass(windowRef, kMovableModalWindowClass); - quint32 tmpWattr = kWindowCloseBoxAttribute | kWindowHorizontalZoomAttribute; - ChangeWindowAttributes(windowRef, tmpWattr, kWindowNoAttributes); - ChangeWindowAttributes(windowRef, kWindowNoAttributes, tmpWattr); - // If the window belongs to a qt-created group, set that group once more: - if (data.window_flags & Qt::WindowStaysOnTopHint - || q->windowType() == Qt::Popup - || q->windowType() == Qt::ToolTip) - SetWindowGroup(windowRef, group); + } else { + // Window should be application-modal (which implies NOT using a sheet). + if (old_wclass == kSheetWindowClass){ + // We cannot convert a sheet to a window. + // So we recreate the window: + recreateMacWindow(); + return; + } else if (!(q->data->window_flags & Qt::CustomizeWindowHint)) { + if (old_wclass == kDocumentWindowClass || old_wclass == kFloatingWindowClass || old_wclass == kUtilityWindowClass){ + // Only change the class to kMovableModalWindowClass if the no explicit jewels + // are set (kMovableModalWindowClass can't contain them), and the current window class + // can be converted to modal (according to carbon doc). Mind the order of + // HIWindowChangeClass and ChangeWindowAttributes. + WindowGroupRef group = GetWindowGroup(windowRef); + HIWindowChangeClass(windowRef, kMovableModalWindowClass); + quint32 tmpWattr = kWindowCloseBoxAttribute | kWindowHorizontalZoomAttribute; + ChangeWindowAttributes(windowRef, tmpWattr, kWindowNoAttributes); + ChangeWindowAttributes(windowRef, kWindowNoAttributes, tmpWattr); + // If the window belongs to a qt-created group, set that group once more: + if (data.window_flags & Qt::WindowStaysOnTopHint + || q->windowType() == Qt::Popup + || q->windowType() == Qt::ToolTip) + SetWindowGroup(windowRef, group); + } + // Popups are usually handled "special" and are never modal. + Qt::WindowType winType = q->windowType(); + if (winType != Qt::Popup && winType != Qt::ToolTip) + SetWindowModality(windowRef, kWindowModalityAppModal, 0); } - // Popups are usually handled "special" and are never modal. - Qt::WindowType winType = q->windowType(); - if (winType != Qt::Popup && winType != Qt::ToolTip) - SetWindowModality(windowRef, kWindowModalityAppModal, 0); } } else if (windowRef) { if (old_wclass == kSheetWindowClass){ diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index 46fa3be..b11b661 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -467,6 +467,17 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO } } + if (topLevel) { + if (data.window_flags & Qt::CustomizeWindowHint + && data.window_flags & Qt::WindowTitleHint) { + HMENU systemMenu = GetSystemMenu((HWND)q->internalWinId(), FALSE); + if (data.window_flags & Qt::WindowCloseButtonHint) + EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED); + else + EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED); + } + } + q->setAttribute(Qt::WA_WState_Created); // accept move/resize events hd = 0; // no display context @@ -638,16 +649,6 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) || (!q->isWindow() && q->parentWidget() && q->parentWidget()->testAttribute(Qt::WA_DropSiteRegistered))) q->setAttribute(Qt::WA_DropSiteRegistered, true); - - if (data.window_flags & Qt::CustomizeWindowHint - && data.window_flags & Qt::WindowTitleHint) { - HMENU systemMenu = GetSystemMenu((HWND)q->internalWinId(), FALSE); - if (data.window_flags & Qt::WindowCloseButtonHint) - EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED); - else - EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED); - } - #ifdef Q_WS_WINCE // Show borderless toplevel windows in tasklist & NavBar if (!parent) { @@ -1431,10 +1432,7 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) qt_wince_maximize(q); } else { #endif - if (!isTranslucentWindow) - MoveWindow(q->internalWinId(), fs.x(), fs.y(), fs.width(), fs.height(), true); - else if (isMove && !isResize) - SetWindowPos(q->internalWinId(), 0, fs.x(), fs.y(), 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); + MoveWindow(q->internalWinId(), fs.x(), fs.y(), fs.width(), fs.height(), true); } if (!q->isVisible()) InvalidateRect(q->internalWinId(), 0, FALSE); @@ -1524,6 +1522,11 @@ bool QWidgetPrivate::shouldShowMaximizeButton() { if (data.window_flags & Qt::MSWindowsFixedSizeDialogHint) return false; + // if the user explicitely asked for the maximize button, we try to add + // it even if the window has fixed size. + if (data.window_flags & Qt::CustomizeWindowHint && + data.window_flags & Qt::WindowMaximizeButtonHint) + return true; if (extra) { if ((extra->maxw && extra->maxw != QWIDGETSIZE_MAX && extra->maxw != QLAYOUTSIZE_MAX) || (extra->maxh && extra->maxh != QWIDGETSIZE_MAX && extra->maxh != QLAYOUTSIZE_MAX)) diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index 34d1779..d226be2 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -100,8 +100,8 @@ win32 { painting/qcolormap_win.cpp \ painting/qpaintdevice_win.cpp \ painting/qprintengine_win.cpp \ - painting/qprinterinfo_win.cpp \ - painting/qregion_win.cpp + painting/qprinterinfo_win.cpp + !win32-borland:!wince*:LIBS += -lmsimg32 } @@ -126,10 +126,6 @@ embedded { painting/qwindowsurface_raster.cpp \ } -wince* { - SOURCES -= painting/qregion_win.cpp -} - unix:x11 { HEADERS += \ painting/qpaintengine_x11_p.h diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp index 831d389..fc2eb60 100644 --- a/src/gui/painting/qblendfunctions.cpp +++ b/src/gui/painting/qblendfunctions.cpp @@ -223,8 +223,8 @@ void qt_scale_image_16bit(uchar *destPixels, int dbpl, int h = ty2 - ty1; int w = tx2 - tx1; - const int dstx = int((tx1 + 0.5 - qMin(targetRect.left(), targetRect.right())) * ix); - const int dsty = int((ty1 + 0.5 - qMin(targetRect.top(), targetRect.bottom())) * iy); + const int dstx = qCeil((tx1 + 0.5 - qMin(targetRect.left(), targetRect.right())) * ix) - 1; + const int dsty = qCeil((ty1 + 0.5 - qMin(targetRect.top(), targetRect.bottom())) * iy) - 1; quint32 basex = quint32((sx < 0 ? srcRect.right() : srcRect.left()) * 65536) + dstx; quint32 srcy = quint32((sy < 0 ? srcRect.bottom() : srcRect.top()) * 65536) + dsty; @@ -723,8 +723,8 @@ template <typename T> void qt_scale_image_32bit(uchar *destPixels, int dbpl, int h = ty2 - ty1; int w = tx2 - tx1; - const int dstx = int((tx1 + 0.5 - qMin(targetRect.left(), targetRect.right())) * ix); - const int dsty = int((ty1 + 0.5 - qMin(targetRect.top(), targetRect.bottom())) * iy); + const int dstx = qCeil((tx1 + 0.5 - qMin(targetRect.left(), targetRect.right())) * ix) - 1; + const int dsty = qCeil((ty1 + 0.5 - qMin(targetRect.top(), targetRect.bottom())) * iy) - 1; quint32 basex = quint32((sx < 0 ? srcRect.right() : srcRect.left()) * 65536) + dstx; quint32 srcy = quint32((sy < 0 ? srcRect.bottom() : srcRect.top()) * 65536) + dsty; diff --git a/src/gui/painting/qdatabuffer_p.h b/src/gui/painting/qdatabuffer_p.h index b568f43..275ec13 100644 --- a/src/gui/painting/qdatabuffer_p.h +++ b/src/gui/painting/qdatabuffer_p.h @@ -114,23 +114,6 @@ public: qSwap(buffer, other.buffer); } - inline void insertBlank(int pos, int count) { - Q_ASSERT(pos >= 0); - Q_ASSERT(pos < siz); - reserve(siz + count); - for (int i = siz - pos - 1; i >= 0; --i) - buffer[pos + count + i] = buffer[pos + i]; - siz += count; - } - - inline void removeAndShift(int pos, int count) { - Q_ASSERT(pos >= 0); - Q_ASSERT(pos < siz); - for (int i=pos; i<siz-count; ++i) - buffer[i] = buffer[i+count]; - siz -= count; - } - inline QDataBuffer &operator<<(const Type &t) { add(t); return *this; } private: diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 979390a..b6603e4 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -4794,30 +4794,60 @@ Q_STATIC_TEMPLATE_FUNCTION void blendTiled(int count, const QSpan *spans, void * if (sy < 0) sy += image_height; - while (length) { - int l = qMin(image_width - sx, length); - if (buffer_size < l) - l = buffer_size; - - DST *dest = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x; - const SRC *src = (SRC*)data->texture.scanLine(sy) + sx; - if (modeSource && coverage == 255) { + if (modeSource && coverage == 255) { + // Copy the first texture block + length = image_width; + while (length) { + int l = qMin(image_width - sx, length); + if (buffer_size < l) + l = buffer_size; + DST *dest = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x; + const SRC *src = (SRC*)data->texture.scanLine(sy) + sx; qt_memconvert<DST, SRC>(dest, src, l); - } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 && - (quintptr(dest) & 3) == (quintptr(src) & 3)) - { - blendUntransformed_dest24(dest, src, coverage, l); - } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 && - (quintptr(dest) & 3) == (quintptr(src) & 3)) - { - blendUntransformed_dest16(dest, src, coverage, l); - } else { - blendUntransformed_unaligned(dest, src, coverage, l); + length -= l; + sx = 0; } - x += l; - length -= l; - sx = 0; + // Now use the rasterBuffer as the source of the texture, + // We can now progressively copy larger blocks + // - Less cpu time in code figuring out what to copy + // We are dealing with one block of data + // - More likely to fit in the cache + // - can use memcpy + int copy_image_width = image_width; + length = spans->len - image_width; + DST *src = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x; + DST *dest = src + copy_image_width; + while (copy_image_width < length) { + qt_memconvert(dest, src, copy_image_width); + dest += copy_image_width; + length -= copy_image_width; + copy_image_width *= 2; + } + qt_memconvert(dest, src, length); + } else { + while (length) { + int l = qMin(image_width - sx, length); + if (buffer_size < l) + l = buffer_size; + DST *dest = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x; + const SRC *src = (SRC*)data->texture.scanLine(sy) + sx; + if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 && + (quintptr(dest) & 3) == (quintptr(src) & 3)) + { + blendUntransformed_dest24(dest, src, coverage, l); + } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 && + (quintptr(dest) & 3) == (quintptr(src) & 3)) + { + blendUntransformed_dest16(dest, src, coverage, l); + } else { + blendUntransformed_unaligned(dest, src, coverage, l); + } + + x += l; + length -= l; + sx = 0; + } } ++spans; } diff --git a/src/gui/painting/qoutlinemapper.cpp b/src/gui/painting/qoutlinemapper.cpp index 9c073f2..1287672 100644 --- a/src/gui/painting/qoutlinemapper.cpp +++ b/src/gui/painting/qoutlinemapper.cpp @@ -40,7 +40,6 @@ ****************************************************************************/ #include "qoutlinemapper_p.h" -#include "qbezier_p.h" #include "qmath.h" @@ -199,70 +198,30 @@ void QOutlineMapper::endOutline() m_m22 * e.y() + m_m12 * e.x() + m_dy); } } else { - // ## TODO: this case needs to be plain code polygonal paths - QTransform matrix(m_m11, m_m12, m_m13, m_m21, m_m22, m_m23, m_dx, m_dy, m_m33); - - if (m_element_types.isEmpty()) { - if (!m_elements.isEmpty()) - m_elements_dev << m_elements.at(0) * matrix; - for (int i=1; i<m_elements.size(); ++i) - m_elements_dev << m_elements.at(i) * matrix; - - } else { - for (int i=0, t=0; i<m_elements.size(); ++i) { - switch (m_element_types.at(t)) { - case QPainterPath::MoveToElement: - m_elements_dev << m_elements.at(i) * matrix; - ++t; - break; - case QPainterPath::LineToElement: - m_elements_dev << m_elements.at(i) * matrix; - ++t; - break; - case QPainterPath::CurveToElement: { - QPolygonF segment = QBezier::fromPoints(m_elements.at(i-1), - m_elements.at(i), - m_elements.at(i+1), - m_elements.at(i+2)).toPolygon(); - if (segment.size() > 3) - m_element_types.insertBlank(t, segment.size() - 3); - else if (segment.size() < 3) - m_element_types.removeAndShift(t, 3 - segment.size()); - - for (QPolygonF::const_iterator it = segment.constBegin(); - it < segment.constEnd(); ++it, ++t) { - m_elements_dev << *it * matrix; - m_element_types.at(t) = QPainterPath::LineToElement; - } - i += 2; - } break; - default: - Q_ASSERT(false); - break; - } - } - element_count = m_elements_dev.size(); - } + const QVectorPath vp((qreal *)m_elements.data(), m_elements.size(), m_element_types.data()); + QPainterPath path = vp.convertToPainterPath(); + path = QTransform(m_m11, m_m12, m_m13, m_m21, m_m22, m_m23, m_dx, m_dy, m_m33).map(path); + if (!(m_outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL)) + path.setFillRule(Qt::WindingFill); + uint old_txop = m_txop; + m_txop = QTransform::TxNone; + if (path.isEmpty()) + m_valid = false; + else + convertPath(path); + m_txop = old_txop; + return; } elements = m_elements_dev.data(); } if (m_round_coords) { // round coordinates to match outlines drawn with drawLine_midpoint_i - for (int i = 0; i < element_count; ++i) + for (int i = 0; i < m_elements.size(); ++i) elements[i] = QPointF(qFloor(elements[i].x() + aliasedCoordinateDelta), qFloor(elements[i].y() + aliasedCoordinateDelta)); } -#ifdef QT_DEBUG_CONVERT - for (int i=0; i<element_count; ++i) { - printf("%d: (%.2f, %.2f)\n", - !m_element_types.isEmpty() ? m_element_types.at(i) : -1, - elements[i].x(), - elements[i].y()); - } -#endif - controlPointRect = boundingRect(elements, element_count); #ifdef QT_DEBUG_CONVERT diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 69e490a..74456dd 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -1736,6 +1736,8 @@ void QRasterPaintEngine::stroke(const QVectorPath &path, const QPen &pen) const QLineF *lines = reinterpret_cast<const QLineF *>(path.points()); for (int i = 0; i < lineCount; ++i) { + if (path.shape() == QVectorPath::LinesHint) + dashOffset = s->lastPen.dashOffset(); if (lines[i].p1() == lines[i].p2()) { if (s->lastPen.capStyle() != Qt::FlatCap) { QPointF p = lines[i].p1(); @@ -3454,8 +3456,8 @@ void QRasterPaintEngine::drawLines(const QLine *lines, int lineCount) int m22 = int(s->matrix.m22()); int dx = qFloor(s->matrix.dx() + aliasedCoordinateDelta); int dy = qFloor(s->matrix.dy() + aliasedCoordinateDelta); - int dashOffset = int(s->lastPen.dashOffset()); for (int i=0; i<lineCount; ++i) { + int dashOffset = int(s->lastPen.dashOffset()); if (s->flags.int_xform) { const QLine &l = lines[i]; int x1 = l.x1() * m11 + dx; @@ -3554,8 +3556,8 @@ void QRasterPaintEngine::drawLines(const QLineF *lines, int lineCount) ? LineDrawNormal : LineDrawIncludeLastPixel; - int dashOffset = int(s->lastPen.dashOffset()); for (int i=0; i<lineCount; ++i) { + int dashOffset = int(s->lastPen.dashOffset()); QLineF line = (lines[i] * s->matrix).translated(aliasedCoordinateDelta, aliasedCoordinateDelta); const QRectF brect(QPointF(line.x1(), line.y1()), QPointF(line.x2(), line.y2())); diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index 8ec881e..ed9b858 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -421,7 +421,6 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) const qreal *lastPoint = points + (pointCount<<1); - d->activeStroker->begin(d->strokeHandler); d->strokeHandler->types.reset(); d->strokeHandler->pts.reset(); @@ -430,13 +429,13 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) if (d->stroker.capStyle() == Qt::RoundCap || d->stroker.joinStyle() == Qt::RoundJoin) flags |= QVectorPath::CurvedShapeHint; - // ### Perspective Xforms are currently not supported... qreal txscale = 1; if (!(pen.isCosmetic() || (qt_scaleForTransform(state()->matrix, &txscale) && txscale != 1))) { // We include cosmetic pens in this case to avoid having to // change the current transform. Normal transformed, // non-cosmetic pens will be transformed as part of fill // later, so they are also covered here.. + d->activeStroker->begin(d->strokeHandler); if (types) { while (points < lastPoint) { switch (*types) { @@ -491,79 +490,64 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) const qreal strokeWidth = d->stroker.strokeWidth(); d->stroker.setStrokeWidth(strokeWidth * txscale); // For cosmetic pens we need a bit of trickery... We to process xform the input points - if (types) { - bool isProject = state()->matrix.type() >= QTransform::TxProject; - while (points < lastPoint) { - switch (*types) { - case QPainterPath::MoveToElement: { - QPointF pt = (*(QPointF *) points) * state()->matrix; - d->activeStroker->moveTo(pt.x(), pt.y()); - points += 2; - ++types; - break; - } - case QPainterPath::LineToElement: { - QPointF pt = (*(QPointF *) points) * state()->matrix; - d->activeStroker->lineTo(pt.x(), pt.y()); - points += 2; - ++types; - break; - } - case QPainterPath::CurveToElement: { - // Convert projective xformed curves to line - // segments so they can be transformed more - // accurately - if (isProject) { - // -1 access here is safe because there is - // always an element prior to the cubicTo, we - // just need the value.. - QPolygonF segment = - QBezier::fromPoints(*(((QPointF *) points) - 1), - *((QPointF *) points), - *(((QPointF *) points) + 1), - *(((QPointF *) points) + 2)).toPolygon(); - - for (QPolygonF::const_iterator it = segment.constBegin(); - it < segment.constEnd(); ++it) { - const QPointF pt = *it * state()->matrix; - d->activeStroker->lineTo(pt.x(), pt.y()); - } - } else { + if (state()->matrix.type() >= QTransform::TxProject) { + QPainterPath painterPath = state()->matrix.map(path.convertToPainterPath()); + d->activeStroker->strokePath(painterPath, d->strokeHandler, QTransform()); + } else { + d->activeStroker->begin(d->strokeHandler); + if (types) { + while (points < lastPoint) { + switch (*types) { + case QPainterPath::MoveToElement: { + QPointF pt = (*(QPointF *) points) * state()->matrix; + d->activeStroker->moveTo(pt.x(), pt.y()); + points += 2; + ++types; + break; + } + case QPainterPath::LineToElement: { + QPointF pt = (*(QPointF *) points) * state()->matrix; + d->activeStroker->lineTo(pt.x(), pt.y()); + points += 2; + ++types; + break; + } + case QPainterPath::CurveToElement: { QPointF c1 = ((QPointF *) points)[0] * state()->matrix; QPointF c2 = ((QPointF *) points)[1] * state()->matrix; QPointF e = ((QPointF *) points)[2] * state()->matrix; d->activeStroker->cubicTo(c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y()); + points += 6; + types += 3; + flags |= QVectorPath::CurvedShapeHint; + break; + } + default: + break; } - points += 6; - types += 3; - flags |= QVectorPath::CurvedShapeHint; - break; } - default: - break; + if (path.hasImplicitClose()) { + QPointF pt = * ((QPointF *) path.points()) * state()->matrix; + d->activeStroker->lineTo(pt.x(), pt.y()); } - } - if (path.hasImplicitClose()) { - QPointF pt = * ((QPointF *) path.points()) * state()->matrix; - d->activeStroker->lineTo(pt.x(), pt.y()); - } - } else { - QPointF p = ((QPointF *)points)[0] * state()->matrix; - d->activeStroker->moveTo(p.x(), p.y()); - points += 2; - ++types; - while (points < lastPoint) { + } else { QPointF p = ((QPointF *)points)[0] * state()->matrix; - d->activeStroker->lineTo(p.x(), p.y()); + d->activeStroker->moveTo(p.x(), p.y()); points += 2; ++types; + while (points < lastPoint) { + QPointF p = ((QPointF *)points)[0] * state()->matrix; + d->activeStroker->lineTo(p.x(), p.y()); + points += 2; + ++types; + } + if (path.hasImplicitClose()) + d->activeStroker->lineTo(p.x(), p.y()); } - if (path.hasImplicitClose()) - d->activeStroker->lineTo(p.x(), p.y()); + d->activeStroker->end(); } - d->activeStroker->end(); d->stroker.setStrokeWidth(strokeWidth); QVectorPath strokePath(d->strokeHandler->pts.data(), d->strokeHandler->types.size(), diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 9c7a7fa..8192fb7 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -1672,7 +1672,7 @@ bool QPainter::begin(QPaintDevice *pd) if (!d->engine) { qWarning("QPainter::begin: Paint device returned engine == 0, type: %d", pd->devType()); - return true; + return false; } // Slip a painter state into the engine before we do any other operations diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp index 762e9e0..d59f3ff 100644 --- a/src/gui/painting/qregion.cpp +++ b/src/gui/painting/qregion.cpp @@ -49,7 +49,7 @@ #include <qdebug.h> -#if defined(Q_OS_UNIX) || defined(Q_OS_WINCE) +#if defined(Q_OS_UNIX) || defined(Q_WS_WIN) #include "qimage.h" #include "qbitmap.h" #include <stdlib.h> @@ -545,7 +545,7 @@ QRegion& QRegion::operator|=(const QRegion &r) \sa intersected() */ -#if !defined (Q_OS_UNIX) && !defined (Q_OS_WINCE) +#if !defined (Q_OS_UNIX) && !defined (Q_WS_WIN) QRegion& QRegion::operator+=(const QRect &r) { return operator+=(QRegion(r)); @@ -561,16 +561,14 @@ QRegion& QRegion::operator+=(const QRect &r) \sa intersected() */ -#if !defined(Q_WS_WIN) || defined(Q_OS_WINCE) QRegion& QRegion::operator&=(const QRegion &r) { return *this = *this & r; } -#endif /*! \overload \since 4.4 */ -#if defined (Q_OS_UNIX) || defined (Q_OS_WINCE) +#if defined (Q_OS_UNIX) || defined (Q_WS_WIN) QRegion& QRegion::operator&=(const QRect &r) { return *this = *this & r; @@ -591,10 +589,8 @@ QRegion& QRegion::operator&=(const QRect &r) \sa subtracted() */ -#if !defined(Q_WS_WIN) || defined(Q_OS_WINCE) QRegion& QRegion::operator-=(const QRegion &r) { return *this = *this - r; } -#endif /*! Applies the xored() function to this region and \a r and @@ -731,7 +727,7 @@ bool QRegion::intersects(const QRect &rect) const return false; } -#if !defined (Q_OS_UNIX) && !defined (Q_OS_WINCE) +#if !defined (Q_OS_UNIX) && !defined (Q_WS_WIN) /*! \overload \since 4.4 @@ -1086,7 +1082,7 @@ Q_AUTOTEST_EXPORT QPainterPath qt_regionToPath(const QRegion ®ion) return result; } -#if defined(Q_OS_UNIX) || defined(Q_OS_WINCE) +#if defined(Q_OS_UNIX) || defined(Q_WS_WIN) //#define QT_REGION_DEBUG /* @@ -1627,9 +1623,9 @@ QT_END_INCLUDE_NAMESPACE QT_BEGIN_INCLUDE_NAMESPACE # include "qregion_mac.cpp" QT_END_INCLUDE_NAMESPACE -#elif defined(Q_OS_WINCE) +#elif defined(Q_WS_WIN) QT_BEGIN_INCLUDE_NAMESPACE -# include "qregion_wince.cpp" +# include "qregion_win.cpp" QT_END_INCLUDE_NAMESPACE #elif defined(Q_WS_QWS) static QRegionPrivate qrp; @@ -3829,7 +3825,7 @@ QRegion::QRegion(const QRect &r, RegionType t) #if defined(Q_WS_X11) d->rgn = 0; d->xrectangles = 0; -#elif defined(Q_OS_WINCE) +#elif defined(Q_WS_WIN) d->rgn = 0; #endif if (t == Rectangle) { @@ -3851,7 +3847,7 @@ QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule) #if defined(Q_WS_X11) d->rgn = 0; d->xrectangles = 0; -#elif defined(Q_OS_WINCE) +#elif defined(Q_WS_WIN) d->rgn = 0; #endif d->qt_rgn = PolygonRegion(a.constData(), a.size(), @@ -3881,7 +3877,7 @@ QRegion::QRegion(const QBitmap &bm) #if defined(Q_WS_X11) d->rgn = 0; d->xrectangles = 0; -#elif defined(Q_OS_WINCE) +#elif defined(Q_WS_WIN) d->rgn = 0; #endif d->qt_rgn = qt_bitmapToRegion(bm); @@ -3896,7 +3892,7 @@ void QRegion::cleanUp(QRegion::QRegionData *x) XDestroyRegion(x->rgn); if (x->xrectangles) free(x->xrectangles); -#elif defined(Q_OS_WINCE) +#elif defined(Q_WS_WIN) if (x->rgn) qt_win_dispose_rgn(x->rgn); #endif @@ -3932,7 +3928,7 @@ QRegion QRegion::copy() const #if defined(Q_WS_X11) x->rgn = 0; x->xrectangles = 0; -#elif defined(Q_OS_WINCE) +#elif defined(Q_WS_WIN) x->rgn = 0; #endif if (d->qt_rgn) diff --git a/src/gui/painting/qregion.h b/src/gui/painting/qregion.h index bfedcb1..84024ce 100644 --- a/src/gui/painting/qregion.h +++ b/src/gui/painting/qregion.h @@ -59,7 +59,7 @@ QT_MODULE(Gui) template <class T> class QVector; class QVariant; -#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_OS_WINCE) +#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) struct QRegionPrivate; #endif @@ -148,6 +148,7 @@ public: #elif defined(Q_WS_MAC) #if defined Q_WS_MAC32 RgnHandle toQDRgn() const; + RgnHandle toQDRgnForUpdate_sys() const; static QRegion fromQDRgn(RgnHandle shape); #endif #ifdef QT_MAC_USE_COCOA @@ -199,7 +200,7 @@ private: #elif defined(Q_WS_MAC) && !defined(QT_MAC_USE_COCOA) mutable RgnHandle unused; // Here for binary compatability reasons. ### Qt 5 remove. #endif -#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_OS_WINCE) +#if defined(Q_WS_QWS) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_WIN) QRegionPrivate *qt_rgn; #endif }; diff --git a/src/gui/painting/qregion_mac.cpp b/src/gui/painting/qregion_mac.cpp index b57f234..6fe7805 100644 --- a/src/gui/painting/qregion_mac.cpp +++ b/src/gui/painting/qregion_mac.cpp @@ -155,6 +155,43 @@ RgnHandle QRegion::toQDRgn() const } return rgnHandle; } + +/*! + \internal + Create's a RegionHandle, it's the caller's responsibility to release. + Returns 0 if the QRegion overflows. +*/ +RgnHandle QRegion::toQDRgnForUpdate_sys() const +{ + RgnHandle rgnHandle = qt_mac_get_rgn(); + if(d->qt_rgn && d->qt_rgn->numRects) { + RgnHandle tmp_rgn = qt_mac_get_rgn(); + int n = d->qt_rgn->numRects; + const QRect *qt_r = (n == 1) ? &d->qt_rgn->extents : d->qt_rgn->rects.constData(); + while (n--) { + + // detect overflow. Tested for use with HIViewSetNeedsDisplayInRegion + // in QWidgetPrivate::update_sys(). + enum { HIViewSetNeedsDisplayInRegionOverflow = 10000 }; // empirically determined conservative value + if (qt_r->right() > HIViewSetNeedsDisplayInRegionOverflow || qt_r->bottom() > HIViewSetNeedsDisplayInRegionOverflow) { + qt_mac_dispose_rgn(tmp_rgn); + qt_mac_dispose_rgn(rgnHandle); + return 0; + } + + SetRectRgn(tmp_rgn, + qMax(SHRT_MIN, qt_r->x()), + qMax(SHRT_MIN, qt_r->y()), + qMin(SHRT_MAX, qt_r->right() + 1), + qMin(SHRT_MAX, qt_r->bottom() + 1)); + UnionRgn(rgnHandle, tmp_rgn, rgnHandle); + ++qt_r; + } + qt_mac_dispose_rgn(tmp_rgn); + } + return rgnHandle; +} + #endif #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) diff --git a/src/gui/painting/qregion_win.cpp b/src/gui/painting/qregion_win.cpp index 249b1a6..8708461 100644 --- a/src/gui/painting/qregion_win.cpp +++ b/src/gui/painting/qregion_win.cpp @@ -39,22 +39,20 @@ ** ****************************************************************************/ +#include "qatomic.h" #include "qbitmap.h" #include "qbuffer.h" #include "qimage.h" #include "qpolygon.h" #include "qregion.h" #include "qt_windows.h" +#include "qpainterpath.h" +#include "qguifunctions_wince.h" QT_BEGIN_NAMESPACE +QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0 }; -/* - In Windows versions before Windows Vista CreateRectRgn - when called in a multi-threaded - environment - might return an invalid handle. This function works around this limitation - by verifying the handle with a quick GetRegionData() call and re-creates the region - if necessary. -*/ HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, int bottom) { const int tries = 10; @@ -80,497 +78,73 @@ HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, return 0; } -#ifndef Q_OS_WINCE -HRGN qt_tryCreatePolygonRegion(const QPolygon &a, Qt::FillRule fillRule) -{ - const int tries = 10; - for (int i = 0; i < tries; ++i) { - HRGN region = CreatePolygonRgn(reinterpret_cast<const POINT*>(a.data()), a.size(), - fillRule == Qt::OddEvenFill ? ALTERNATE : WINDING); - if (region) { - if (GetRegionData(region, 0, 0)) - return region; - else - DeleteObject(region); - } - } - return 0; -} -#endif - -QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 }; - -QRegion::QRegion() - : d(&shared_empty) -{ - d->ref.ref(); -} - -#ifndef Q_OS_WINCE //implementation for WindowsCE in qregion_wce.cpp -QRegion::QRegion(const QRect &r, RegionType t) -{ - if (r.isEmpty()) { - d = &shared_empty; - d->ref.ref(); - } else { - d = new QRegionData; - d->ref = 1; - if (t == Rectangle) - d->rgn = qt_tryCreateRegion(t, r.left(), r.top(), r.x() + r.width(), r.y() + r.height()); - else if (t == Ellipse) { - // need to add 1 to width/height for the ellipse to have correct boundingrect. - d->rgn = qt_tryCreateRegion(t, r.x(), r.y(), r.x() + r.width() + 1, r.y() + r.height() + 1); - } - } -} -#endif - -#ifndef Q_OS_WINCE //implementation for WindowsCE in qregion_wce.cpp -QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule) -{ - if (a.size() < 3) { - d = &shared_empty; - d->ref.ref(); - } else { - d = new QRegionData; - d->ref = 1; - d->rgn = qt_tryCreatePolygonRegion(a, fillRule); - } -} -#endif - -QRegion::QRegion(const QRegion &r) -{ - d = r.d; - d->ref.ref(); -} - -HRGN qt_win_bitmapToRegion(const QBitmap& bitmap) -{ - HRGN region=0; - QImage image = bitmap.toImage(); - const int MAXRECT = 256; - struct RData { - RGNDATAHEADER header; - RECT rect[MAXRECT]; - }; - RData data; - -#define FlushSpans \ - { \ - data.header.dwSize = sizeof(RGNDATAHEADER); \ - data.header.iType = RDH_RECTANGLES; \ - data.header.nCount = n; \ - data.header.nRgnSize = 0; \ - data.header.rcBound.bottom = y; \ - HRGN r = ExtCreateRegion(0, \ - sizeof(RGNDATAHEADER)+n*sizeof(RECT),(RGNDATA*)&data); \ - if (region) { \ - CombineRgn(region, region, r, RGN_OR); \ - DeleteObject(r); \ - } else { \ - region = r; \ - } \ - data.header.rcBound.top = y; \ - } - -#define AddSpan \ - { \ - data.rect[n].left=prev1; \ - data.rect[n].top=y; \ - data.rect[n].right=x-1+1; \ - data.rect[n].bottom=y+1; \ - n++; \ - if (n == MAXRECT) { \ - FlushSpans \ - n=0; \ - } \ - } - - data.header.rcBound.top = 0; - data.header.rcBound.left = 0; - data.header.rcBound.right = image.width()-1; - int n = 0; - - int zero = 0x00; - - int x, y; - for (y = 0; y < image.height(); ++y) { - uchar *line = image.scanLine(y); - int w = image.width(); - uchar all=zero; - int prev1 = -1; - for (x = 0; x < w;) { - uchar byte = line[x/8]; - if (x > w - 8 || byte != all) { - for (int b = 8; b > 0 && x < w; --b) { - if (!(byte & 0x01) == !all) { - // More of the same - } else { - // A change. - if (all != zero) { - AddSpan; - all = zero; - } else { - prev1 = x; - all = ~zero; - } - } - byte >>= 1; - ++x; - } - } else { - x += 8; - } - } - if (all != zero) { - AddSpan; - } - } - if (n) { - FlushSpans; - } - - if (!region) { - // Surely there is some better way. - region = qt_tryCreateRegion(QRegion::Rectangle, 0,0,1,1); - CombineRgn(region, region, region, RGN_XOR); - } - return region; -} - -#ifndef Q_OS_WINCE //implementation for WindowsCE in qregion_wce.cpp -QRegion::QRegion(const QBitmap &bm) -{ - if (bm.isNull()) { - d = &shared_empty; - d->ref.ref(); - } else { - d = new QRegionData; - d->ref = 1; - d->rgn = qt_win_bitmapToRegion(bm); - } -} -#endif - -void QRegion::cleanUp(QRegion::QRegionData *x) -{ - if (x->rgn) - DeleteObject(x->rgn); - delete x; -} - -QRegion::~QRegion() -{ - if (!d->ref.deref()) - cleanUp(d); -} - -QRegion &QRegion::operator=(const QRegion &r) -{ - r.d->ref.ref(); - if (!d->ref.deref()) - cleanUp(d); - d = r.d; - return *this; -} - - -QRegion QRegion::copy() const -{ - QRegion r; - QRegionData *x = new QRegionData; - x->ref = 1; - if (d->rgn) { - x->rgn = qt_tryCreateRegion(QRegion::Rectangle, 0, 0, 2, 2); - CombineRgn(x->rgn, d->rgn, 0, RGN_COPY); - } else { - x->rgn = 0; - } - if (!r.d->ref.deref()) - cleanUp(r.d); - r.d = x; - return r; -} - -bool QRegion::isEmpty() const -{ - return (d == &shared_empty || boundingRect().isEmpty()); -} - - -bool QRegion::contains(const QPoint &p) const -{ - return d->rgn ? PtInRegion(d->rgn, p.x(), p.y()) : false; -} - -bool QRegion::contains(const QRect &r) const -{ - if (!d->rgn) - return false; - RECT rect; - SetRect(&rect, r.left(), r.top(), r.right(), r.bottom()); - return RectInRegion(d->rgn, &rect); -} - - -void QRegion::translate(int dx, int dy) -{ - if (!d->rgn || (dx == 0 && dy == 0)) - return; - detach(); - OffsetRgn(d->rgn, dx, dy); -} - - -#define RGN_NOP -1 - -// Duplicates of those in qregion.cpp -#define QRGN_OR 6 -#define QRGN_AND 7 -#define QRGN_SUB 8 -#define QRGN_XOR 9 - -/* - Performs the actual OR, AND, SUB and XOR operation between regions. - Sets the resulting region handle to 0 to indicate an empty region. -*/ - -QRegion QRegion::winCombine(const QRegion &r, int op) const +QRegion qt_region_from_HRGN(HRGN rgn) { - int both=RGN_NOP, - left=RGN_NOP, - right=RGN_NOP; - switch (op) { - case QRGN_OR: - both = RGN_OR; - left = right = RGN_COPY; - break; - case QRGN_AND: - both = RGN_AND; - break; - case QRGN_SUB: - both = RGN_DIFF; - left = RGN_COPY; - break; - case QRGN_XOR: - both = RGN_XOR; - left = right = RGN_COPY; - break; - default: - qWarning("QRegion: Internal error in winCombine"); - } - - int allCombineRgnResults = NULLREGION; - QRegion result; - result.detach(); - result.d->rgn = qt_tryCreateRegion(QRegion::Rectangle, 0, 0, 0, 0); - if (d->rgn && r.d->rgn) - allCombineRgnResults = CombineRgn(result.d->rgn, d->rgn, r.d->rgn, both); - else if (d->rgn && left != RGN_NOP) - allCombineRgnResults = CombineRgn(result.d->rgn, d->rgn, d->rgn, left); - else if (r.d->rgn && right != RGN_NOP) - allCombineRgnResults = CombineRgn(result.d->rgn, r.d->rgn, r.d->rgn, right); - - if (allCombineRgnResults == NULLREGION || allCombineRgnResults == ERROR) - result = QRegion(); - - //##### do not delete this. A null pointer is different from an empty region in SelectClipRgn in qpainter_win! (M) -// if (allCombineRgnResults == NULLREGION) { -// if (result.data->rgn) -// DeleteObject(result.data->rgn); -// result.data->rgn = 0; // empty region -// } - return result; -} - -QRegion QRegion::unite(const QRegion &r) const -{ - if (!d->rgn) - return r; - if (!r.d->rgn) - return *this; - return winCombine(r, QRGN_OR); -} - -QRegion QRegion::unite(const QRect &r) const -{ - return unite(QRegion(r)); -} - -QRegion QRegion::intersect(const QRegion &r) const -{ - if (!r.d->rgn || !d->rgn) - return QRegion(); - return winCombine(r, QRGN_AND); -} - -QRegion QRegion::subtract(const QRegion &r) const -{ - if (!r.d->rgn || !d->rgn) - return *this; - return winCombine(r, QRGN_SUB); -} - -QRegion QRegion::eor(const QRegion &r) const -{ - if (!d->rgn) - return r; - if (!r.d->rgn) - return *this; - return winCombine(r, QRGN_XOR); -} - - -QRect QRegion::boundingRect() const -{ - if (!d->rgn) - return QRect(); - RECT r; - if (GetRgnBox(d->rgn, &r) == NULLREGION) - return QRect(); - else - return QRect(r.left, r.top, r.right - r.left, r.bottom - r.top); -} - -QVector<QRect> QRegion::rects() const -{ - if (d->rgn == 0) - return QVector<QRect>(); - - int numBytes = GetRegionData(d->rgn, 0, 0); + int numBytes = GetRegionData(rgn, 0, 0); if (numBytes == 0) - return QVector<QRect>(); + return QRegion(); char *buf = new char[numBytes]; if (buf == 0) - return QVector<QRect>(); + return QRegion(); RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf); - if (GetRegionData(d->rgn, numBytes, rd) == 0) { + if (GetRegionData(rgn, numBytes, rd) == 0) { delete [] buf; - return QVector<QRect>(); + return QRegion(); } - QVector<QRect> a(rd->rdh.nCount); + QRegion region; RECT *r = reinterpret_cast<RECT*>(rd->Buffer); - for (int i = 0; i < a.size(); ++i) { - a[i].setCoords(r->left, r->top, r->right - 1, r->bottom - 1); + for (uint i = 0; i < rd->rdh.nCount; ++i) { + QRect rect; + rect.setCoords(r->left, r->top, r->right - 1, r->bottom - 1); ++r; + region |= rect; } delete [] buf; - return a; -} - -void QRegion::setRects(const QRect *rects, int num) -{ - *this = QRegion(); - if (!rects || num == 0 || (num == 1 && rects->isEmpty())) - return; - for (int i = 0; i < num; ++i) - *this |= rects[i]; -} - -int QRegion::numRects() const -{ - if (d->rgn == 0) - return 0; - - const int numBytes = GetRegionData(d->rgn, 0, 0); - if (numBytes == 0) - return 0; - - char *buf = new char[numBytes]; - if (buf == 0) - return 0; - - RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf); - if (GetRegionData(d->rgn, numBytes, rd) == 0) { - delete[] buf; - return 0; - } - - const int n = rd->rdh.nCount; - delete[] buf; - return n; + return region; } -bool QRegion::operator==(const QRegion &r) const +void qt_win_dispose_rgn(HRGN r) { - if (d == r.d) - return true; - if ((d->rgn == 0) ^ (r.d->rgn == 0)) // one is empty, not both - return false; - return d->rgn == 0 ? true // both empty - : EqualRgn(d->rgn, r.d->rgn); // both non-empty + if (r) + DeleteObject(r); } -QRegion& QRegion::operator+=(const QRegion &r) +static void qt_add_rect(HRGN &winRegion, QRect r) { - if (!r.d->rgn) - return *this; - - if (!d->rgn) { - *this = r; - return *this; + HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height()); + if (rgn) { + HRGN dest = CreateRectRgn(0,0,0,0); + int result = CombineRgn(dest, winRegion, rgn, RGN_OR); + if (result) { + DeleteObject(winRegion); + winRegion = dest; + } + DeleteObject(rgn); } - - detach(); - - int result; - result = CombineRgn(d->rgn, d->rgn, r.d->rgn, RGN_OR); - if (result == NULLREGION || result == ERROR) - *this = QRegion(); - - return *this; -} - -QRegion& QRegion::operator-=(const QRegion &r) -{ - if (!r.d->rgn || !d->rgn) - return *this; - - detach(); - - int result; - result = CombineRgn(d->rgn, d->rgn, r.d->rgn, RGN_DIFF); - if (result == NULLREGION || result == ERROR) - *this = QRegion(); - - return *this; } -QRegion& QRegion::operator&=(const QRegion &r) +void QRegion::ensureHandle() const { - if (!d->rgn) - return *this; - - if (!r.d->rgn) { - *this = QRegion(); - return *this; + if (d->rgn) + DeleteObject(d->rgn); + d->rgn = CreateRectRgn(0,0,0,0); + if (d->qt_rgn) { + if (d->qt_rgn->numRects == 1) { + QRect r = d->qt_rgn->extents; + qt_add_rect(d->rgn, r); + return; + } + for (int i = 0;i < d->qt_rgn->numRects;i++) { + QRect r = d->qt_rgn->rects.at(i); + qt_add_rect(d->rgn, r); + } } - - detach(); - - int result; - result = CombineRgn(d->rgn, d->rgn, r.d->rgn, RGN_AND); - if (result == NULLREGION || result == ERROR) - *this = QRegion(); - - return *this; -} - -bool qt_region_strictContains(const QRegion ®ion, const QRect &rect) -{ - Q_UNUSED(region); - Q_UNUSED(rect); - return false; } -void QRegion::ensureHandle() const -{ -} QT_END_NAMESPACE diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index a322fe4..a13b494 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -48,6 +48,8 @@ #include "qvariant.h" #include <qmath.h> +#include <private/qbezier_p.h> + QT_BEGIN_NAMESPACE #define Q_NEAR_CLIP 0.000001 @@ -1492,27 +1494,12 @@ static inline bool lineTo_clipped(QPainterPath &path, const QTransform &transfor static inline bool cubicTo_clipped(QPainterPath &path, const QTransform &transform, const QPointF &a, const QPointF &b, const QPointF &c, const QPointF &d, bool needsMoveTo) { - const QHomogeneousCoordinate ha = mapHomogeneous(transform, a); - const QHomogeneousCoordinate hb = mapHomogeneous(transform, b); - const QHomogeneousCoordinate hc = mapHomogeneous(transform, c); - const QHomogeneousCoordinate hd = mapHomogeneous(transform, d); - - if (ha.w < Q_NEAR_CLIP && hb.w < Q_NEAR_CLIP && hc.w < Q_NEAR_CLIP && hd.w < Q_NEAR_CLIP) - return false; + // Convert projective xformed curves to line + // segments so they can be transformed more accurately + QPolygonF segment = QBezier::fromPoints(a, b, c, d).toPolygon(); - if (ha.w >= Q_NEAR_CLIP && hb.w >= Q_NEAR_CLIP && hc.w >= Q_NEAR_CLIP && hd.w >= Q_NEAR_CLIP) { - if (needsMoveTo) - path.moveTo(ha.toPoint()); - - path.cubicTo(hb.toPoint(), hc.toPoint(), hd.toPoint()); - return true; - } - - if (lineTo_clipped(path, transform, a, b, needsMoveTo)) - needsMoveTo = false; - if (lineTo_clipped(path, transform, b, c, needsMoveTo)) - needsMoveTo = false; - if (lineTo_clipped(path, transform, c, d, needsMoveTo)) + for (int i = 0; i < segment.size() - 1; ++i) + if (lineTo_clipped(path, transform, segment.at(i), segment.at(i+1), needsMoveTo)) needsMoveTo = false; return !needsMoveTo; @@ -1554,6 +1541,7 @@ static QPainterPath mapProjective(const QTransform &transform, const QPainterPat if (path.elementCount() > 0 && lastMoveTo != last) lineTo_clipped(result, transform, last, lastMoveTo, needsMoveTo, false); + result.setFillRule(path.fillRule()); return result; } @@ -1793,6 +1781,14 @@ void QTransform::setMatrix(qreal m11, qreal m12, qreal m13, m_dirty = TxProject; } +static inline bool needsPerspectiveClipping(const QRectF &rect, const QTransform &transform) +{ + const qreal wx = qMin(transform.m13() * rect.left(), transform.m13() * rect.right()); + const qreal wy = qMin(transform.m23() * rect.top(), transform.m23() * rect.bottom()); + + return wx + wy + transform.m33() < Q_NEAR_CLIP; +} + QRect QTransform::mapRect(const QRect &rect) const { TransformationType t = inline_type(); @@ -1813,7 +1809,7 @@ QRect QTransform::mapRect(const QRect &rect) const y -= h; } return QRect(x, y, w, h); - } else { + } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) { // see mapToPolygon for explanations of the algorithm. qreal x = 0, y = 0; MAP(rect.left(), rect.top(), x, y); @@ -1837,6 +1833,10 @@ QRect QTransform::mapRect(const QRect &rect) const xmax = qMax(xmax, x); ymax = qMax(ymax, y); return QRect(qRound(xmin), qRound(ymin), qRound(xmax)-qRound(xmin), qRound(ymax)-qRound(ymin)); + } else { + QPainterPath path; + path.addRect(rect); + return map(path).boundingRect().toRect(); } } @@ -1879,7 +1879,7 @@ QRectF QTransform::mapRect(const QRectF &rect) const y -= h; } return QRectF(x, y, w, h); - } else { + } else if (t < TxProject || !needsPerspectiveClipping(rect, *this)) { qreal x = 0, y = 0; MAP(rect.x(), rect.y(), x, y); qreal xmin = x; @@ -1902,6 +1902,10 @@ QRectF QTransform::mapRect(const QRectF &rect) const xmax = qMax(xmax, x); ymax = qMax(ymax, y); return QRectF(xmin, ymin, xmax-xmin, ymax - ymin); + } else { + QPainterPath path; + path.addRect(rect); + return map(path).boundingRect(); } } diff --git a/src/gui/painting/qwindowsurface_raster.cpp b/src/gui/painting/qwindowsurface_raster.cpp index 22433dd..eddbfd2 100644 --- a/src/gui/painting/qwindowsurface_raster.cpp +++ b/src/gui/painting/qwindowsurface_raster.cpp @@ -118,7 +118,7 @@ QPaintDevice *QRasterWindowSurface::paintDevice() void QRasterWindowSurface::beginPaint(const QRegion &rgn) { #if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_WS_WINCE)) - if (!qt_widget_private(window())->isOpaque) { + if (!qt_widget_private(window())->isOpaque && window()->testAttribute(Qt::WA_TranslucentBackground)) { #if defined(Q_WS_WIN) && !defined(Q_WS_WINCE) if (d_ptr->image->image.format() != QImage::Format_ARGB32_Premultiplied) prepareBuffer(QImage::Format_ARGB32_Premultiplied, window()); @@ -149,7 +149,7 @@ void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoi QRect br = rgn.boundingRect(); #ifndef Q_WS_WINCE - if (!qt_widget_private(window())->isOpaque) { + if (!qt_widget_private(window())->isOpaque && window()->testAttribute(Qt::WA_TranslucentBackground)) { QRect r = window()->frameGeometry(); QPoint frameOffset = qt_widget_private(window())->frameStrut().topLeft(); QRect dirtyRect = br.translated(offset + frameOffset); diff --git a/src/gui/painting/qwindowsurface_x11.cpp b/src/gui/painting/qwindowsurface_x11.cpp index d8b0d9e..95f6ce3 100644 --- a/src/gui/painting/qwindowsurface_x11.cpp +++ b/src/gui/painting/qwindowsurface_x11.cpp @@ -154,7 +154,7 @@ void QX11WindowSurface::setGeometry(const QRect &rect) QX11PixmapData *oldData = static_cast<QX11PixmapData *>(d_ptr->device.pixmapData()); Q_ASSERT(oldData); - if (!oldData->uninit && hasStaticContents()) { + if (!(oldData->flags & QX11PixmapData::Uninitialized) && hasStaticContents()) { // Copy the content of the old pixmap into the new one. QX11PixmapData *newData = new QX11PixmapData(QPixmapData::PixmapType); newData->resize(size.width(), size.height()); @@ -175,7 +175,7 @@ void QX11WindowSurface::setGeometry(const QRect &rect) dx, dy, qMin(boundingRect.width(), size.width()), qMin(boundingRect.height(), size.height()), dx, dy); XFreeGC(X11->display, tmpGc); - newData->uninit = false; + newData->flags &= ~QX11PixmapData::Uninitialized; d_ptr->device = QPixmap(newData); } else { diff --git a/src/gui/statemachine/qkeyeventtransition.cpp b/src/gui/statemachine/qkeyeventtransition.cpp index 51b3ccc..21a0736 100644 --- a/src/gui/statemachine/qkeyeventtransition.cpp +++ b/src/gui/statemachine/qkeyeventtransition.cpp @@ -106,19 +106,6 @@ QKeyEventTransition::QKeyEventTransition(QObject *object, QEvent::Type type, } /*! - Constructs a new key event transition for events of the given \a type for - the given \a object, with the given \a key, \a targets and \a sourceState. -*/ -QKeyEventTransition::QKeyEventTransition(QObject *object, QEvent::Type type, - int key, const QList<QAbstractState*> &targets, - QState *sourceState) - : QEventTransition(*new QKeyEventTransitionPrivate, object, type, targets, sourceState) -{ - Q_D(QKeyEventTransition); - d->transition = new QBasicKeyEventTransition(type, key); -} - -/*! Destroys this key event transition. */ QKeyEventTransition::~QKeyEventTransition() diff --git a/src/gui/statemachine/qkeyeventtransition.h b/src/gui/statemachine/qkeyeventtransition.h index f5e8de3..45ae684 100644 --- a/src/gui/statemachine/qkeyeventtransition.h +++ b/src/gui/statemachine/qkeyeventtransition.h @@ -62,9 +62,6 @@ public: QKeyEventTransition(QState *sourceState = 0); QKeyEventTransition(QObject *object, QEvent::Type type, int key, QState *sourceState = 0); - QKeyEventTransition(QObject *object, QEvent::Type type, int key, - const QList<QAbstractState*> &targets, - QState *sourceState = 0); ~QKeyEventTransition(); int key() const; diff --git a/src/gui/statemachine/qmouseeventtransition.cpp b/src/gui/statemachine/qmouseeventtransition.cpp index 0cd096a..dbe50b3 100644 --- a/src/gui/statemachine/qmouseeventtransition.cpp +++ b/src/gui/statemachine/qmouseeventtransition.cpp @@ -112,21 +112,6 @@ QMouseEventTransition::QMouseEventTransition(QObject *object, QEvent::Type type, } /*! - Constructs a new mouse event transition for events of the given \a type for - the given \a object, with the given \a button, \a targets and \a - sourceState. -*/ -QMouseEventTransition::QMouseEventTransition(QObject *object, QEvent::Type type, - Qt::MouseButton button, - const QList<QAbstractState*> &targets, - QState *sourceState) - : QEventTransition(*new QMouseEventTransitionPrivate, object, type, targets, sourceState) -{ - Q_D(QMouseEventTransition); - d->transition = new QBasicMouseEventTransition(type, button); -} - -/*! Destroys this mouse event transition. */ QMouseEventTransition::~QMouseEventTransition() diff --git a/src/gui/statemachine/qmouseeventtransition.h b/src/gui/statemachine/qmouseeventtransition.h index 73ae6c9..a56a554 100644 --- a/src/gui/statemachine/qmouseeventtransition.h +++ b/src/gui/statemachine/qmouseeventtransition.h @@ -63,10 +63,6 @@ public: QMouseEventTransition(QState *sourceState = 0); QMouseEventTransition(QObject *object, QEvent::Type type, Qt::MouseButton button, QState *sourceState = 0); - QMouseEventTransition(QObject *object, QEvent::Type type, - Qt::MouseButton button, - const QList<QAbstractState*> &targets, - QState *sourceState = 0); ~QMouseEventTransition(); Qt::MouseButton button() const; diff --git a/src/gui/styles/gtksymbols.cpp b/src/gui/styles/gtksymbols.cpp index c2c7876..f947ac1 100644 --- a/src/gui/styles/gtksymbols.cpp +++ b/src/gui/styles/gtksymbols.cpp @@ -752,7 +752,24 @@ static void setupGtkFileChooser(GtkWidget* gtkFileChooser, QWidget *parent, QGtk::gtk_file_filter_set_name(gtkFilter, qPrintable(name.isEmpty() ? extensions.join(QLS(", ")) : name)); foreach (const QString &fileExtension, extensions) { - QGtk::gtk_file_filter_add_pattern (gtkFilter, qPrintable(fileExtension)); + // Note Gtk file dialogs are by default case sensitive + // and only supports basic glob syntax so we + // rewrite .xyz to .[xX][yY][zZ] + QString caseInsensitive; + for (int i = 0 ; i < fileExtension.length() ; ++i) { + QChar ch = fileExtension.at(i); + if (ch.isLetter()) { + caseInsensitive.append( + QLatin1Char('[') + + ch.toLower() + + ch.toUpper() + + QLatin1Char(']')); + } else { + caseInsensitive.append(ch); + } + } + QGtk::gtk_file_filter_add_pattern (gtkFilter, qPrintable(caseInsensitive)); + } if (filterMap) filterMap->insert(gtkFilter, rawfilter); diff --git a/src/gui/styles/qstylesheetstyle.cpp b/src/gui/styles/qstylesheetstyle.cpp index 2efa4a7..5f6d4ab 100644 --- a/src/gui/styles/qstylesheetstyle.cpp +++ b/src/gui/styles/qstylesheetstyle.cpp @@ -2891,8 +2891,8 @@ void QStyleSheetStyle::drawComplexControl(ComplexControl cc, const QStyleOptionC bool customUp = true, customDown = true; QRenderRule upRule = renderRule(w, opt, PseudoElement_SpinBoxUpButton); QRenderRule downRule = renderRule(w, opt, PseudoElement_SpinBoxDownButton); - bool upRuleMatch = upRule.hasGeometry(); - bool downRuleMatch = downRule.hasGeometry(); + bool upRuleMatch = upRule.hasGeometry() || upRule.hasPosition(); + bool downRuleMatch = downRule.hasGeometry() || downRule.hasPosition(); if (rule.hasNativeBorder() && !upRuleMatch && !downRuleMatch) { rule.drawBackgroundImage(p, spinOpt.rect); customUp = (opt->subControls & QStyle::SC_SpinBoxUp) @@ -5167,8 +5167,8 @@ QRect QStyleSheetStyle::subControlRect(ComplexControl cc, const QStyleOptionComp QRenderRule upRule = renderRule(w, opt, PseudoElement_SpinBoxUpButton); QRenderRule downRule = renderRule(w, opt, PseudoElement_SpinBoxDownButton); bool ruleMatch = rule.hasBox() || !rule.hasNativeBorder(); - bool upRuleMatch = upRule.hasGeometry(); - bool downRuleMatch = downRule.hasGeometry(); + bool upRuleMatch = upRule.hasGeometry() || upRule.hasPosition(); + bool downRuleMatch = downRule.hasGeometry() || upRule.hasPosition(); if (ruleMatch || upRuleMatch || downRuleMatch) { switch (sc) { case SC_SpinBoxFrame: diff --git a/src/gui/styles/qwindowsxpstyle.cpp b/src/gui/styles/qwindowsxpstyle.cpp index 9560c4b..ad87354 100644 --- a/src/gui/styles/qwindowsxpstyle.cpp +++ b/src/gui/styles/qwindowsxpstyle.cpp @@ -134,6 +134,7 @@ static const int windowsRightBorder = 12; // right border on windows // External function calls extern Q_GUI_EXPORT HDC qt_win_display_dc(); +extern QRegion qt_region_from_HRGN(HRGN rgn); @@ -445,6 +446,7 @@ bool QWindowsXPStylePrivate::isTransparent(XPThemeData &themeData) themeData.stateId); } + /*! \internal Returns a QRegion of the region of the part */ @@ -456,12 +458,18 @@ QRegion QWindowsXPStylePrivate::region(XPThemeData &themeData) themeData.stateId, &rect, &hRgn))) return QRegion(); - QRegion rgn = QRegion(0,0,1,1); - const bool success = CombineRgn(rgn.handle(), hRgn, 0, RGN_COPY) != ERROR; - DeleteObject(hRgn); + HRGN dest = CreateRectRgn(0, 0, 0, 0); + const bool success = CombineRgn(dest, hRgn, 0, RGN_COPY) != ERROR; + + QRegion region; + if (success) - return rgn; - return QRegion(); + region = qt_region_from_HRGN(dest); + + DeleteObject(hRgn); + DeleteObject(dest); + + return region; } /*! \internal diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index a0b6f0c..d210cb4 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -1378,8 +1378,8 @@ bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, G FT_Face face = 0; for (int i = 0; i < num_glyphs; ++i) { - if (!gs->glyph_data.contains(glyphs[i]) - || gs->glyph_data.value(glyphs[i])->format != format) { + Glyph *glyph = gs->glyph_data.value(glyphs[i]); + if (glyph == 0 || glyph->format != format) { if (!face) { face = lockFace(); FT_Matrix m = matrix; diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm index 76132df..dbf3015 100644 --- a/src/gui/text/qfontengine_mac.mm +++ b/src/gui/text/qfontengine_mac.mm @@ -156,7 +156,7 @@ QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const ATSFontFamilyRef &, con if (!kerning) { float zero = 0.0; QCFType<CFNumberRef> noKern = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &zero); - CFDictionaryAddValue(attributeDict, kCTKernAttributeName, &noKern); + CFDictionaryAddValue(attributeDict, kCTKernAttributeName, noKern); } QCoreTextFontEngine *fe = new QCoreTextFontEngine(ctfont, fontDef, this); @@ -1588,6 +1588,7 @@ QFontEngine::FaceId QFontEngineMac::faceId() const { FaceId ret; #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) +if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { // CTFontGetPlatformFont FSRef ref; if (ATSFontGetFileReference(FMGetATSFontRefFromFont(fontID), &ref) != noErr) @@ -1595,7 +1596,9 @@ QFontEngine::FaceId QFontEngineMac::faceId() const ret.filename = QByteArray(128, 0); ret.index = fontID; FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size()); -#else +}else +#endif +{ FSSpec spec; if (ATSFontGetFileSpecification(FMGetATSFontRefFromFont(fontID), &spec) != noErr) return ret; @@ -1605,7 +1608,7 @@ QFontEngine::FaceId QFontEngineMac::faceId() const ret.filename = QByteArray(128, 0); ret.index = fontID; FSRefMakePath(&ref, (UInt8 *)ret.filename.data(), ret.filename.size()); -#endif +} return ret; } diff --git a/src/gui/text/qtextcontrol_p.h b/src/gui/text/qtextcontrol_p.h index 872bcd5..eb0d749 100644 --- a/src/gui/text/qtextcontrol_p.h +++ b/src/gui/text/qtextcontrol_p.h @@ -83,7 +83,7 @@ class QAbstractScrollArea; class QEvent; class QTimerEvent; -class Q_AUTOTEST_EXPORT QTextControl : public QObject +class Q_GUI_EXPORT QTextControl : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QTextControl) diff --git a/src/gui/util/qcompleter.cpp b/src/gui/util/qcompleter.cpp index bf1fa6a..f4dd87c 100644 --- a/src/gui/util/qcompleter.cpp +++ b/src/gui/util/qcompleter.cpp @@ -772,7 +772,7 @@ QMatchData QUnsortedModelEngine::filter(const QString& part, const QModelIndex& /////////////////////////////////////////////////////////////////////////////// QCompleterPrivate::QCompleterPrivate() : widget(0), proxy(0), popup(0), cs(Qt::CaseSensitive), role(Qt::EditRole), column(0), - sorting(QCompleter::UnsortedModel), wrap(true), maxVisibleItems(7), eatFocusOut(true) + maxVisibleItems(7), sorting(QCompleter::UnsortedModel), wrap(true), eatFocusOut(true) { } @@ -824,6 +824,9 @@ void QCompleterPrivate::_q_complete(QModelIndex index, bool highlighted) Q_Q(QCompleter); QString completion; + if (!(index.flags() & Qt::ItemIsEnabled)) + return; + if (!index.isValid() || (!proxy->showAll && (index.row() >= proxy->engine->matchCount()))) { completion = prefix; } else { @@ -1102,7 +1105,8 @@ void QCompleter::setPopup(QAbstractItemView *popup) QObject::connect(popup, SIGNAL(clicked(QModelIndex)), this, SLOT(_q_complete(QModelIndex))); - QObject::connect(popup, SIGNAL(clicked(QModelIndex)), popup, SLOT(hide())); + QObject::connect(this, SIGNAL(activated(QModelIndex)), + popup, SLOT(hide())); QObject::connect(popup->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(_q_completionSelected(QItemSelection))); diff --git a/src/gui/widgets/qlinecontrol.cpp b/src/gui/widgets/qlinecontrol.cpp index 106d8f2..f68d287 100644 --- a/src/gui/widgets/qlinecontrol.cpp +++ b/src/gui/widgets/qlinecontrol.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/widgets/qlineedit_p.cpp b/src/gui/widgets/qlineedit_p.cpp index 0e39304..f0ec8ad 100644 --- a/src/gui/widgets/qlineedit_p.cpp +++ b/src/gui/widgets/qlineedit_p.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) +** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -34,7 +34,7 @@ ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. +** contact the sales department at http://www.qtsoftware.com/contact. ** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/src/gui/widgets/qmainwindowlayout.cpp b/src/gui/widgets/qmainwindowlayout.cpp index 3936a67..55afa70 100644 --- a/src/gui/widgets/qmainwindowlayout.cpp +++ b/src/gui/widgets/qmainwindowlayout.cpp @@ -1988,6 +1988,9 @@ void QMainWindowLayout::timerEvent(QTimerEvent *e) if (movingSeparatorOrigin == movingSeparatorPos) return; + //when moving the separator, we need to update the previous position + parentWidget()->update(layoutState.dockAreaLayout.separatorRegion()); + layoutState = savedState; layoutState.dockAreaLayout.separatorMove(movingSeparator, movingSeparatorOrigin, movingSeparatorPos); diff --git a/src/gui/widgets/qmenu.cpp b/src/gui/widgets/qmenu.cpp index 8eec0fc..05426a0 100644 --- a/src/gui/widgets/qmenu.cpp +++ b/src/gui/widgets/qmenu.cpp @@ -225,9 +225,17 @@ void QMenuPrivate::updateActionRects() const dh = popupGeometry(QApplication::desktop()->screenNumber(q)).height(), y = 0; QStyle *style = q->style(); - const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, 0, q), - vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, 0, q), - icone = style->pixelMetric(QStyle::PM_SmallIconSize, 0, q); + QStyleOption opt; + opt.init(q); + const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q), + vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q), + icone = style->pixelMetric(QStyle::PM_SmallIconSize, &opt, q); + const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q); + const int deskFw = style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, &opt, q); + + const int sfcMargin = style->sizeFromContents(QStyle::CT_Menu, &opt, QApplication::globalStrut(), q).width() - QApplication::globalStrut().width(); + const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin)); + const int tearoffHeight = tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q) : 0; //for compatability now - will have to refactor this away.. tabWidth = 0; @@ -300,10 +308,10 @@ void QMenuPrivate::updateActionRects() const if (!sz.isEmpty()) { - max_column_width = qMax(max_column_width, sz.width()); + max_column_width = qMax(min_column_width, qMax(max_column_width, sz.width())); //wrapping if (!scroll && - y+sz.height()+vmargin > dh - (style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) { + y+sz.height()+vmargin > dh - (deskFw * 2)) { ncols++; y = vmargin; } @@ -316,10 +324,9 @@ void QMenuPrivate::updateActionRects() const max_column_width += tabWidth; //finally add in the tab width //calculate position - const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q); const int base_y = vmargin + fw + topmargin + (scroll ? scroll->scrollOffset : 0) + - (tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q) : 0); + tearoffHeight; int x = hmargin + fw + leftmargin; y = base_y; @@ -328,7 +335,7 @@ void QMenuPrivate::updateActionRects() const if (rect.isNull()) continue; if (!scroll && - y+rect.height() > dh - (style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) { + y+rect.height() > dh - deskFw * 2) { x += max_column_width + hmargin; y = base_y; } @@ -1703,9 +1710,7 @@ QSize QMenu::sizeHint() const QSize s; QStyleOption opt(0); - opt.rect = rect(); - opt.palette = palette(); - opt.state = QStyle::State_None; + opt.init(this); for (int i = 0; i < d->actionRects.count(); ++i) { const QRect &rect = d->actionRects.at(i); if (rect.isNull()) @@ -2721,7 +2726,7 @@ void QMenu::mouseMoveEvent(QMouseEvent *e) QAction *action = d->actionAt(e->pos()); if (!action) { - if (d->hasHadMouse && !rect().contains(e->pos())) + if (d->hasHadMouse) d->setCurrentAction(0); return; } else if(e->buttons()) { diff --git a/src/gui/widgets/qmenubar.cpp b/src/gui/widgets/qmenubar.cpp index a3964f7..4a900d6 100644 --- a/src/gui/widgets/qmenubar.cpp +++ b/src/gui/widgets/qmenubar.cpp @@ -716,7 +716,7 @@ void QMenuBar::initStyleOption(QStyleOptionMenuItem *option, const QAction *acti application examples} also provide menus using these classes. \sa QMenu, QShortcut, QAction, - {http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/index.html}{Introduction to Apple Human Interface Guidelines}, + {http://developer.apple.com/documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGIntro/XHIGIntro.html}{Introduction to Apple Human Interface Guidelines}, {fowler}{GUI Design Handbook: Menu Bar}, {Menus Example} */ diff --git a/src/gui/widgets/qtextedit.cpp b/src/gui/widgets/qtextedit.cpp index e80df92..c095f5c 100644 --- a/src/gui/widgets/qtextedit.cpp +++ b/src/gui/widgets/qtextedit.cpp @@ -2625,12 +2625,12 @@ void QTextEditPrivate::_q_gestureTriggered() return; QScrollBar *hBar = q->horizontalScrollBar(); QScrollBar *vBar = q->verticalScrollBar(); - QPoint delta = g->pos() - (g->lastPos().isNull() ? g->pos() : g->lastPos()); + QSize delta = g->lastOffset(); if (!delta.isNull()) { if (QApplication::isRightToLeft()) - delta.rx() *= -1; - int newX = hBar->value() - delta.x(); - int newY = vBar->value() - delta.y(); + delta.rwidth() *= -1; + int newX = hBar->value() - delta.width(); + int newY = vBar->value() - delta.height(); hbar->setValue(newX); vbar->setValue(newY); } diff --git a/src/multimedia/audio/audio.pri b/src/multimedia/audio/audio.pri new file mode 100644 index 0000000..3ddb23b --- /dev/null +++ b/src/multimedia/audio/audio.pri @@ -0,0 +1,56 @@ +HEADERS += $$PWD/qaudio.h \ + $$PWD/qaudioformat.h \ + $$PWD/qaudioinput.h \ + $$PWD/qaudiooutput.h \ + $$PWD/qaudiodeviceinfo.h \ + $$PWD/qaudioengineplugin.h \ + $$PWD/qaudioengine.h \ + $$PWD/qaudiodevicefactory_p.h \ + $$PWD/qaudiodeviceid.h \ + $$PWD/qaudiodeviceid_p.h + + +SOURCES += $$PWD/qaudio.cpp \ + $$PWD/qaudioformat.cpp \ + $$PWD/qaudiodeviceinfo.cpp \ + $$PWD/qaudiooutput.cpp \ + $$PWD/qaudioinput.cpp \ + $$PWD/qaudioengineplugin.cpp \ + $$PWD/qaudioengine.cpp \ + $$PWD/qaudiodevicefactory.cpp \ + $$PWD/qaudiodeviceid.cpp + +mac { + HEADERS += $$PWD/qaudioinput_mac_p.h \ + $$PWD/qaudiooutput_mac_p.h \ + $$PWD/qaudiodeviceinfo_mac_p.h \ + $$PWD/qaudio_mac_p.h + + SOURCES += $$PWD/qaudiodeviceinfo_mac_p.cpp \ + $$PWD/qaudiooutput_mac_p.cpp \ + $$PWD/qaudioinput_mac_p.cpp \ + $$PWD/qaudio_mac.cpp + + LIBS += -framework CoreAudio -framework AudioUnit -framework AudioToolbox + +} else:win32 { + + HEADERS += $$PWD/qaudioinput_win32_p.h $$PWD/qaudiooutput_win32_p.h $$PWD/qaudiodeviceinfo_win32_p.h + SOURCES += $$PWD/qaudiodeviceinfo_win32_p.cpp \ + $$PWD/qaudiooutput_win32_p.cpp \ + $$PWD/qaudioinput_win32_p.cpp + !wince*:LIBS += -lwinmm + wince*:LIBS += -lcoredll + +} else:unix { + unix:contains(QT_CONFIG, alsa) { + linux-*|freebsd-*|openbsd-*:{ + DEFINES += HAS_ALSA + HEADERS += $$PWD/qaudiooutput_alsa_p.h $$PWD/qaudioinput_alsa_p.h $$PWD/qaudiodeviceinfo_alsa_p.h + SOURCES += $$PWD/qaudiodeviceinfo_alsa_p.cpp \ + $$PWD/qaudiooutput_alsa_p.cpp \ + $$PWD/qaudioinput_alsa_p.cpp + LIBS += -lasound + } + } +} diff --git a/src/multimedia/audio/qaudio.cpp b/src/multimedia/audio/qaudio.cpp new file mode 100644 index 0000000..5ba6493 --- /dev/null +++ b/src/multimedia/audio/qaudio.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtMultimedia/qaudio.h> + + +QT_BEGIN_NAMESPACE + +namespace QAudio +{ + +class RegisterMetaTypes +{ +public: + RegisterMetaTypes() + { + qRegisterMetaType<QAudio::Error>(); + qRegisterMetaType<QAudio::State>(); + qRegisterMetaType<QAudio::Mode>(); + } + +} _register; + +} + +/*! + \namespace QAudio + \brief The QAudio namespace contains enums used by the audio classes. + \since 4.6 +*/ + +/*! + \enum QAudio::Error + + \value NoError No errors have occurred + \value OpenError An error opening the audio device + \value IOError An error occurred during read/write of audio device + \value UnderrunError Audio data is not being fed to the audio device at a fast enough rate + \value FatalError A non-recoverable error has occurred, the audio device is not usable at this time. +*/ + +/*! + \enum QAudio::State + + \value ActiveState Audio data is being processed, this state is set after start() is called + and while audio data is available to be processed. + \value SuspendState The audio device is in a suspended state, this state will only be entered + after suspend() is called. + \value StopState The audio device is closed, not processing any audio data + \value IdleState The QIODevice passed in has no data and audio system's buffer is empty, this state + is set after start() is called and while no audio data is available to be processed. +*/ + +/*! + \enum QAudio::Mode + + \value AudioOutput audio output device + \value AudioInput audio input device +*/ + + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudio.h b/src/multimedia/audio/qaudio.h new file mode 100644 index 0000000..b996cc6 --- /dev/null +++ b/src/multimedia/audio/qaudio.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIO_H +#define QAUDIO_H + + +#include <QtCore/qglobal.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +namespace QAudio +{ + enum Error { NoError, OpenError, IOError, UnderrunError, FatalError }; + enum State { ActiveState, SuspendState, StopState, IdleState }; + enum Mode { AudioInput, AudioOutput }; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +Q_DECLARE_METATYPE(QAudio::Error); +Q_DECLARE_METATYPE(QAudio::State); +Q_DECLARE_METATYPE(QAudio::Mode); + +#endif // QAUDIO_H diff --git a/src/multimedia/audio/qaudio_mac.cpp b/src/multimedia/audio/qaudio_mac.cpp new file mode 100644 index 0000000..cfa7ca3 --- /dev/null +++ b/src/multimedia/audio/qaudio_mac.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qaudio_mac_p.h" + +QT_BEGIN_NAMESPACE + +// Debugging +QDebug operator<<(QDebug dbg, const QAudioFormat& audioFormat) +{ + dbg.nospace() << "QAudioFormat(" << + audioFormat.frequency() << "," << + audioFormat.channels() << "," << + audioFormat.sampleSize()<< "," << + audioFormat.codec() << "," << + audioFormat.byteOrder() << "," << + audioFormat.sampleType() << ")"; + + return dbg.space(); +} + + +// Conversion +QAudioFormat toQAudioFormat(AudioStreamBasicDescription const& sf) +{ + QAudioFormat audioFormat; + + audioFormat.setFrequency(sf.mSampleRate); + audioFormat.setChannels(sf.mChannelsPerFrame); + audioFormat.setSampleSize(sf.mBitsPerChannel); + audioFormat.setCodec(QString::fromLatin1("audio/pcm")); + audioFormat.setByteOrder(sf.mFormatFlags & kLinearPCMFormatFlagIsBigEndian != 0 ? QAudioFormat::BigEndian : QAudioFormat::LittleEndian); + QAudioFormat::SampleType type = QAudioFormat::UnSignedInt; + if ((sf.mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) + type = QAudioFormat::SignedInt; + else if ((sf.mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) + type = QAudioFormat::Float; + audioFormat.setSampleType(type); + + return audioFormat; +} + +AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat) +{ + AudioStreamBasicDescription sf; + + sf.mFormatFlags = kAudioFormatFlagIsPacked; + sf.mSampleRate = audioFormat.frequency(); + sf.mFramesPerPacket = 1; + sf.mChannelsPerFrame = audioFormat.channels(); + sf.mBitsPerChannel = audioFormat.sampleSize(); + sf.mBytesPerFrame = sf.mChannelsPerFrame * (sf.mBitsPerChannel / 8); + sf.mBytesPerPacket = sf.mFramesPerPacket * sf.mBytesPerFrame; + sf.mFormatID = kAudioFormatLinearPCM; + + switch (audioFormat.sampleType()) { + case QAudioFormat::SignedInt: sf.mFormatFlags |= kAudioFormatFlagIsSignedInteger; break; + case QAudioFormat::UnSignedInt: /* default */ break; + case QAudioFormat::Float: sf.mFormatFlags |= kAudioFormatFlagIsFloat; break; + case QAudioFormat::Unknown: default: break; + } + + return sf; +} + +// QAudioRingBuffer +QAudioRingBuffer::QAudioRingBuffer(int bufferSize): + m_bufferSize(bufferSize) +{ + m_buffer = new char[m_bufferSize]; + reset(); +} + +QAudioRingBuffer::~QAudioRingBuffer() +{ + delete m_buffer; +} + +int QAudioRingBuffer::used() const +{ + return m_bufferUsed; +} + +int QAudioRingBuffer::free() const +{ + return m_bufferSize - m_bufferUsed; +} + +int QAudioRingBuffer::size() const +{ + return m_bufferSize; +} + +void QAudioRingBuffer::reset() +{ + m_readPos = 0; + m_writePos = 0; + m_bufferUsed = 0; +} + +QT_END_NAMESPACE + + diff --git a/src/multimedia/audio/qaudio_mac_p.h b/src/multimedia/audio/qaudio_mac_p.h new file mode 100644 index 0000000..8e2d522 --- /dev/null +++ b/src/multimedia/audio/qaudio_mac_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIO_MAC_P_H +#define QAUDIO_MAC_P_H + +#include <CoreAudio/CoreAudio.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qatomic.h> + +#include <QtMultimedia/qaudioformat.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +extern QDebug operator<<(QDebug dbg, const QAudioFormat& audioFormat); + +extern QAudioFormat toQAudioFormat(const AudioStreamBasicDescription& streamFormat); +extern AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat); + +class QAudioRingBuffer +{ +public: + typedef QPair<char*, int> Region; + + QAudioRingBuffer(int bufferSize); + ~QAudioRingBuffer(); + + Region acquireReadRegion(int size) + { + const int used = m_bufferUsed.fetchAndAddAcquire(0); + + if (used > 0) { + const int readSize = qMin(size, qMin(m_bufferSize - m_readPos, used)); + + return readSize > 0 ? Region(m_buffer + m_readPos, readSize) : Region(0, 0); + } + + return Region(0, 0); + } + + void releaseReadRegion(Region const& region) + { + m_readPos = (m_readPos + region.second) % m_bufferSize; + + m_bufferUsed.fetchAndAddRelease(-region.second); + } + + Region acquireWriteRegion(int size) + { + const int free = m_bufferSize - m_bufferUsed.fetchAndAddAcquire(0); + + if (free > 0) { + const int writeSize = qMin(size, qMin(m_bufferSize - m_writePos, free)); + + return writeSize > 0 ? Region(m_buffer + m_writePos, writeSize) : Region(0, 0); + } + + return Region(0, 0); + } + + void releaseWriteRegion(Region const& region) + { + m_writePos = (m_writePos + region.second) % m_bufferSize; + + m_bufferUsed.fetchAndAddRelease(region.second); + } + + int used() const; + int free() const; + int size() const; + + void reset(); + +private: + int m_bufferSize; + int m_readPos; + int m_writePos; + char* m_buffer; + QAtomicInt m_bufferUsed; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIO_MAC_P_H + + diff --git a/src/multimedia/audio/qaudiodevicefactory.cpp b/src/multimedia/audio/qaudiodevicefactory.cpp new file mode 100644 index 0000000..35e9c91 --- /dev/null +++ b/src/multimedia/audio/qaudiodevicefactory.cpp @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qdebug.h> +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudioengineplugin.h> +#include <private/qfactoryloader_p.h> +#include "qaudiodevicefactory_p.h" +#include "qaudiodeviceid_p.h" + +#if defined(Q_OS_WIN) +#include "qaudiodeviceinfo_win32_p.h" +#include "qaudiooutput_win32_p.h" +#include "qaudioinput_win32_p.h" +#elif defined(Q_OS_MAC) +#include "qaudiodeviceinfo_mac_p.h" +#include "qaudiooutput_mac_p.h" +#include "qaudioinput_mac_p.h" +#elif defined(HAS_ALSA) +#include "qaudiodeviceinfo_alsa_p.h" +#include "qaudiooutput_alsa_p.h" +#include "qaudioinput_alsa_p.h" +#endif + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QAudioEngineFactoryInterface_iid, QLatin1String("/audio"), Qt::CaseInsensitive)) + + +class QNullDeviceInfo : public QAbstractAudioDeviceInfo +{ +public: + QAudioFormat preferredFormat() const { qWarning()<<"using null deviceinfo, none available"; return QAudioFormat(); } + bool isFormatSupported(const QAudioFormat& ) const { return false; } + QAudioFormat nearestFormat(const QAudioFormat& ) const { return QAudioFormat(); } + QString deviceName() const { return QString(); } + QStringList codecList() { return QStringList(); } + QList<int> frequencyList() { return QList<int>(); } + QList<int> channelsList() { return QList<int>(); } + QList<int> sampleSizeList() { return QList<int>(); } + QList<QAudioFormat::Endian> byteOrderList() { return QList<QAudioFormat::Endian>(); } + QList<QAudioFormat::SampleType> sampleTypeList() { return QList<QAudioFormat::SampleType>(); } +}; + +class QNullInputDevice : public QAbstractAudioInput +{ +public: + QIODevice* start(QIODevice* ) { qWarning()<<"using null input device, none available"; return 0; } + void stop() {} + void reset() {} + void suspend() {} + void resume() {} + int bytesReady() const { return 0; } + int periodSize() const { return 0; } + void setBufferSize(int ) {} + int bufferSize() const { return 0; } + void setNotifyInterval(int ) {} + int notifyInterval() const { return 0; } + qint64 totalTime() const { return 0; } + qint64 clock() const { return 0; } + QAudio::Error error() const { return QAudio::OpenError; } + QAudio::State state() const { return QAudio::StopState; } + QAudioFormat format() const { return QAudioFormat(); } +}; + +class QNullOutputDevice : public QAbstractAudioOutput +{ +public: + QIODevice* start(QIODevice* ) { qWarning()<<"using null output device, none available"; return 0; } + void stop() {} + void reset() {} + void suspend() {} + void resume() {} + int bytesFree() const { return 0; } + int periodSize() const { return 0; } + void setBufferSize(int ) {} + int bufferSize() const { return 0; } + void setNotifyInterval(int ) {} + int notifyInterval() const { return 0; } + qint64 totalTime() const { return 0; } + qint64 clock() const { return 0; } + QAudio::Error error() const { return QAudio::OpenError; } + QAudio::State state() const { return QAudio::StopState; } + QAudioFormat format() const { return QAudioFormat(); } +}; + +QList<QAudioDeviceId> QAudioDeviceFactory::deviceList(QAudio::Mode mode) +{ + QList<QAudioDeviceId> devices; +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + foreach (const QByteArray &handle, QAudioDeviceInfoPrivate::deviceList(mode)) + devices += createDeviceId(QLatin1String("builtin"), mode, handle); +#endif + QFactoryLoader* l = loader(); + + foreach (QString const& key, l->keys()) { + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(l->instance(key)); + if (plugin) { + foreach (QByteArray const& handle, plugin->deviceList(mode)) + devices += createDeviceId(key, mode, handle); + } + + delete plugin; + } + + return devices; +} + +QAudioDeviceId QAudioDeviceFactory::defaultInputDevice() +{ + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(QLatin1String("default"))); + + if (plugin) { + QList<QByteArray> list = plugin->deviceList(QAudio::AudioInput); + if (list.size() > 0) + return createDeviceId(QLatin1String("default"), QAudio::AudioInput, list.at(0)); + } +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + return createDeviceId(QLatin1String("builtin"), QAudio::AudioInput, QAudioDeviceInfoPrivate::defaultInputDevice()); +#endif + return QAudioDeviceId(); +} + +QAudioDeviceId QAudioDeviceFactory::defaultOutputDevice() +{ + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(QLatin1String("default"))); + + if (plugin) { + QList<QByteArray> list = plugin->deviceList(QAudio::AudioOutput); + if (list.size() > 0) + return createDeviceId(QLatin1String("default"), QAudio::AudioOutput, list.at(0)); + } +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + return createDeviceId(QLatin1String("builtin"), QAudio::AudioOutput, QAudioDeviceInfoPrivate::defaultOutputDevice()); +#endif + return QAudioDeviceId(); +} + +QAbstractAudioDeviceInfo* QAudioDeviceFactory::audioDeviceInfo(QAudioDeviceId const& id) +{ + if (id.isNull()) + return new QNullDeviceInfo(); +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + if (id.d->key == QLatin1String("builtin")) + return new QAudioDeviceInfoPrivate(id.d->handle, QAudio::Mode(id.d->mode)); +#endif + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(id.d->key)); + + if (plugin) + return plugin->createDeviceInfo(id.d->handle, QAudio::Mode(id.d->mode)); + + return new QNullDeviceInfo(); +} + +QAbstractAudioInput* QAudioDeviceFactory::createDefaultInputDevice(QAudioFormat const &format) +{ + return createInputDevice(defaultInputDevice(), format); +} + +QAbstractAudioOutput* QAudioDeviceFactory::createDefaultOutputDevice(QAudioFormat const &format) +{ + return createOutputDevice(defaultOutputDevice(), format); +} + +QAbstractAudioInput* QAudioDeviceFactory::createInputDevice(QAudioDeviceId const& id, QAudioFormat const &format) +{ + if (id.isNull()) + return new QNullInputDevice(); +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + if (id.d->key == QLatin1String("builtin")) { + if(!defaultInputDevice().isNull()) + return new QAudioInputPrivate(id.d->handle, format); + else + return new QNullInputDevice(); + } +#endif + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance((id.d->key))); + + if (plugin) + return plugin->createInput(id.d->handle, format); + + return new QNullInputDevice(); +} + +QAbstractAudioOutput* QAudioDeviceFactory::createOutputDevice(QAudioDeviceId const& id, QAudioFormat const &format) +{ + if (id.isNull()) + return new QNullOutputDevice(); +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA)) + if (id.d->key == QLatin1String("builtin")) { + if(!defaultOutputDevice().isNull()) + return new QAudioOutputPrivate(id.d->handle, format); + else + return new QNullOutputDevice(); + } +#endif + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(id.d->key)); + + if (plugin) + return plugin->createOutput(id.d->handle, format); + + return new QNullOutputDevice(); +} + +QAudioDeviceId QAudioDeviceFactory::createDeviceId(QString const& key, int mode, QByteArray const& handle) +{ + return QAudioDeviceId(new QAudioDeviceIdPrivate(key, mode, handle)); +} + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudiodevicefactory_p.h b/src/multimedia/audio/qaudiodevicefactory_p.h new file mode 100644 index 0000000..a8e2b28 --- /dev/null +++ b/src/multimedia/audio/qaudiodevicefactory_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIODEVICEFACTORY_P_H +#define QAUDIODEVICEFACTORY_P_H + +#include <QtCore/qglobal.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qlist.h> + +#include <QtMultimedia/qaudiodeviceid.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class QAbstractAudioInput; +class QAbstractAudioOutput; + + +class QAudioDeviceFactory +{ +public: + static QList<QAudioDeviceId> deviceList(QAudio::Mode mode); + + static QAudioDeviceId defaultInputDevice(); + static QAudioDeviceId defaultOutputDevice(); + + static QAbstractAudioDeviceInfo* audioDeviceInfo(QAudioDeviceId const &device); + + static QAbstractAudioInput* createDefaultInputDevice(QAudioFormat const &format); + static QAbstractAudioOutput* createDefaultOutputDevice(QAudioFormat const &format); + + static QAbstractAudioInput* createInputDevice(QAudioDeviceId const &device, QAudioFormat const &format); + static QAbstractAudioOutput* createOutputDevice(QAudioDeviceId const &device, QAudioFormat const &format); + + static QAbstractAudioInput* createNullInput(); + static QAbstractAudioOutput* createNullOutput(); + + static QAudioDeviceId createDeviceId(QString const& key, int mode, QByteArray const& handle); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIODEVICEFACTORY_P_H + diff --git a/src/multimedia/audio/qaudiodeviceid.cpp b/src/multimedia/audio/qaudiodeviceid.cpp new file mode 100644 index 0000000..21a9cd8 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceid.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qdatastream.h> + +#include <QtMultimedia/qaudiodeviceid.h> +#include "qaudiodeviceid_p.h" + + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioDeviceId + \brief The QAudioDeviceId class provides means for identifying a unique input or output device on a system. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + \sa QAudioDeviceInfo, QAudioOutput, QAudioInput +*/ + +/*! + Construct a new null QAudioDeviceId. +*/ + +QAudioDeviceId::QAudioDeviceId() +{ +} + +/*! + Copy the QAudDeviceId referenced by \a other. +*/ + +QAudioDeviceId::QAudioDeviceId(const QAudioDeviceId &other): + d(other.d) +{ +} + +/*! + Destroy the QAudioDeviceId. +*/ + +QAudioDeviceId::~QAudioDeviceId() +{ +} + +/*! + Make a copy of the \a other QAudioDeviceId. +*/ + +QAudioDeviceId& QAudioDeviceId::operator=(const QAudioDeviceId &other) +{ + d = other.d; + return *this; +} + +/*! + Compare with the \a other QAudioDeviceId, return true if they are the same; + otherwise false. +*/ + +bool QAudioDeviceId::operator==(const QAudioDeviceId &other) const +{ + return (d.constData() == 0 && other.d.constData() == 0) || + (d.constData() != 0 && other.d.constData() != 0 && + d->key == other.d->key && d->mode == other.d->mode && d->handle == other.d->handle); +} + +/*! + Compare with the \a other QAudioDeviceId, return false if they are the same; + otherwise true. +*/ + +bool QAudioDeviceId::operator!=(const QAudioDeviceId &other) const +{ + return !(*this == other); +} + +/*! + Returns true if this is not a valid QAudioDeviceId; otherwise false. +*/ + +bool QAudioDeviceId::isNull() const +{ + return d.constData() == 0; +} + +/*! + \internal +*/ + +QAudioDeviceId::QAudioDeviceId(QAudioDeviceIdPrivate* data): + d(data) +{ +} + +/*! + \internal +*/ + +QAudioDeviceIdPrivate::QAudioDeviceIdPrivate(QString const& k, int m, QByteArray const& h): + key(k), mode(m), handle(h) +{ +} + +#ifndef QT_NO_DATASTREAM +Q_MULTIMEDIA_EXPORT QDataStream &operator<<(QDataStream &s, const QAudioDeviceId &id) +{ + s << id.d->key << id.d->mode << id.d->handle; + return s; +} + +Q_MULTIMEDIA_EXPORT QDataStream &operator>>(QDataStream &s, QAudioDeviceId &id) +{ + QString key; + int mode; + QByteArray handle; + + s >> key >> mode >> handle; + id = QAudioDeviceId(new QAudioDeviceIdPrivate(key, mode, handle)); + + return s; +} +#endif + + +QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudiodeviceid.h b/src/multimedia/audio/qaudiodeviceid.h new file mode 100644 index 0000000..f9188d3 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceid.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAUDIODEVICEID_H +#define QAUDIODEVICEID_H + +#include <QtCore/qshareddata.h> +#include <QtCore/qmetatype.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAudioDeviceFactory; +class QAudioDeviceIdPrivate; + +class Q_MULTIMEDIA_EXPORT QAudioDeviceId +{ + friend class QAudioDeviceFactory; + friend Q_MULTIMEDIA_EXPORT QDataStream &operator<<(QDataStream&, const QAudioDeviceId&); + friend Q_MULTIMEDIA_EXPORT QDataStream &operator>>(QDataStream&, QAudioDeviceId&); + +public: + QAudioDeviceId(); + QAudioDeviceId(const QAudioDeviceId &other); + ~QAudioDeviceId(); + + QAudioDeviceId& operator=(const QAudioDeviceId &other); + bool operator==(const QAudioDeviceId &id) const; + bool operator!=(const QAudioDeviceId &id) const; + + bool isNull() const; + +private: + QAudioDeviceId(QAudioDeviceIdPrivate *data); + + QSharedDataPointer<QAudioDeviceIdPrivate> d; +}; + +#ifndef QT_NO_DATASTREAM +Q_MULTIMEDIA_EXPORT QDataStream &operator<<(QDataStream&, const QAudioDeviceId&); +Q_MULTIMEDIA_EXPORT QDataStream &operator>>(QDataStream&, QAudioDeviceId&); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +Q_DECLARE_METATYPE(QAudioDeviceId); + + +#endif // QAUDIODEVICEID_H diff --git a/src/multimedia/audio/qaudiodeviceid_p.h b/src/multimedia/audio/qaudiodeviceid_p.h new file mode 100644 index 0000000..3034574 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceid_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIODEVICEIDPRIVATE_H +#define QAUDIODEVICEIDPRIVATE_H + +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAudioDeviceIdPrivate : public QSharedData +{ +public: + QAudioDeviceIdPrivate(QString const& k, int m, QByteArray const& h); + + QString key; + int mode; + QByteArray handle; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIODEVICEIDPRIVATE_H diff --git a/src/multimedia/audio/qaudiodeviceinfo.cpp b/src/multimedia/audio/qaudiodeviceinfo.cpp new file mode 100644 index 0000000..c1895d7 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo.cpp @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaudiodevicefactory_p.h" +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioDeviceInfo + \brief The QAudioDeviceInfo class provides an interface to query audio devices and their functionality. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + QAudioDeviceInfo lets you query for audio devices--such as sound + cards and USB headsets--that are currently available on the system. + The audio devices available are dependent on the platform or audio plugins installed. + + You can also query each device for the formats it supports. A + format in this context is a set consisting of a specific byte + order, channel, codec, frequency, sample rate, and sample type. A + format is represented by the QAudioFormat class. + + The values supported by the the device for each of these + parameters can be fetched with + supportedByteOrders(), supportedChannels(), supportedCodecs(), + supportedFrequencies(), supportedSampleSizes(), and + supportedSampleTypes(). The combinations supported are dependent on the platform, + audio plugins installed and the audio device capabilities. If you need a specific format, you can check if + the device supports it with isFormatSupported(), or fetch a + supported format that is as close as possible to the format with + nearestFormat(). + + A QAudioDeviceInfo is constructed with a QAudioDeviceId, which is + an identifier for a physical device. It is used by Qt to construct + classes that communicate with the device--such as + QAudioDeviceInfo, QAudioInput, and QAudioOutput. The static + functions defaultInputDevice(), defaultOutputDevice(), and + deviceList() let you get a hold of the ids for all available + devices. You fetch ids based on whether you will use the device + for input or output; this is specified by the QAudio::Mode enum. + The QAudioDeviceId returned are only valid for the QAudio::Mode. + + For instance: + + \code + foreach(QAudioDeviceId audioId, QAudioDeviceInfo::deviceList(QAudio::AudioOutput)) { + QAudioDeviceInfo info(audioId); + qDebug() << "Device name: " << info.deviceName(); + } + \endcode + + In this code sample, we loop through all devices that are able to output + sound, i.e., play an audio stream in a supported format. For each device we + find, we simply print the deviceName(). + + \sa QAudioOutput, QAudioInput, QAudioDeviceId +*/ + +/*! + Construct a new audio device info and attach it to \a parent. + Using the audio device with the specified \a id. +*/ + +QAudioDeviceInfo::QAudioDeviceInfo(const QAudioDeviceId &id, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::audioDeviceInfo(id); +} + +/*! + Destroy this audio device info. +*/ + +QAudioDeviceInfo::~QAudioDeviceInfo() +{ + delete d; +} + +/*! + Returns human readable name of audio device. + + Device names vary depending on platform/audio plugin being used. + + They are a unique string identifiers for the audio device. + + eg. default, Intel, U0x46d0x9a4 +*/ + +QString QAudioDeviceInfo::deviceName() const +{ + return d->deviceName(); +} + +/*! + Returns true if \a settings are supported by the audio device of this QAudioDeviceInfo. +*/ + +bool QAudioDeviceInfo::isFormatSupported(const QAudioFormat &settings) const +{ + return d->isFormatSupported(settings); +} + +/*! + Returns QAudioFormat of default settings. + + These settings are provided by the platform/audio plugin being used. + + They also are dependent on the QAudio::Mode being used. + + A typical audio system would provide something like: + \list + \o Input settings: 8000Hz mono 8 bit. + \o Output settings: 44100Hz stereo 16 bit little endian. + \endlist +*/ + +QAudioFormat QAudioDeviceInfo::preferredFormat() const +{ + return d->preferredFormat(); +} + +/*! + Returns closest QAudioFormat to \a settings that system audio supports. + + These settings are provided by the platform/audio plugin being used. + + They also are dependent on the QAudio::Mode being used. +*/ + +QAudioFormat QAudioDeviceInfo::nearestFormat(const QAudioFormat &settings) const +{ + return d->nearestFormat(settings); +} + +/*! + Returns a list of supported codecs. + + All platform and plugin implementations should provide support for: + + "audio/pcm" - Linear PCM + + For writing plugins to support additional codecs refer to: + + http://www.iana.org/assignments/media-types/audio/ +*/ + +QStringList QAudioDeviceInfo::supportedCodecs() const +{ + return d->codecList(); +} + +/*! + Returns a list of supported frequencies. +*/ + +QList<int> QAudioDeviceInfo::supportedFrequencies() const +{ + return d->frequencyList(); +} + +/*! + Returns a list of supported channels. +*/ + +QList<int> QAudioDeviceInfo::supportedChannels() const +{ + return d->channelsList(); +} + +/*! + Returns a list of supported sample sizes. +*/ + +QList<int> QAudioDeviceInfo::supportedSampleSizes() const +{ + return d->sampleSizeList(); +} + +/*! + Returns a list of supported byte orders. +*/ + +QList<QAudioFormat::Endian> QAudioDeviceInfo::supportedByteOrders() const +{ + return d->byteOrderList(); +} + +/*! + Returns a list of supported sample types. +*/ + +QList<QAudioFormat::SampleType> QAudioDeviceInfo::supportedSampleTypes() const +{ + return d->sampleTypeList(); +} + +/*! + Returns the name of the default input audio device. + All platform and audio plugin implementations provide a default audio device to use. +*/ + +QAudioDeviceId QAudioDeviceInfo::defaultInputDevice() +{ + return QAudioDeviceFactory::defaultInputDevice(); +} + +/*! + Returns the name of the default output audio device. + All platform and audio plugin implementations provide a default audio device to use. +*/ + +QAudioDeviceId QAudioDeviceInfo::defaultOutputDevice() +{ + return QAudioDeviceFactory::defaultOutputDevice(); +} + +/*! + Returns a list of audio devices that support \a mode. +*/ + +QList<QAudioDeviceId> QAudioDeviceInfo::deviceList(QAudio::Mode mode) +{ + return QAudioDeviceFactory::deviceList(mode); +} + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudiodeviceinfo.h b/src/multimedia/audio/qaudiodeviceinfo.h new file mode 100644 index 0000000..444a00a --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIODEVICEINFO_H +#define QAUDIODEVICEINFO_H + +#include <QtCore/qobject.h> +#include <QtCore/qglobal.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceid.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAbstractAudioDeviceInfo; + +class Q_MULTIMEDIA_EXPORT QAudioDeviceInfo : public QObject +{ + Q_OBJECT + +public: + explicit QAudioDeviceInfo(const QAudioDeviceId &id, QObject *parent = 0); + ~QAudioDeviceInfo(); + + QString deviceName() const; + + bool isFormatSupported(const QAudioFormat &format) const; + QAudioFormat preferredFormat() const; + QAudioFormat nearestFormat(const QAudioFormat &format) const; + + QStringList supportedCodecs() const; + QList<int> supportedFrequencies() const; + QList<int> supportedChannels() const; + QList<int> supportedSampleSizes() const; + QList<QAudioFormat::Endian> supportedByteOrders() const; + QList<QAudioFormat::SampleType> supportedSampleTypes() const; + + static QAudioDeviceId defaultInputDevice(); + static QAudioDeviceId defaultOutputDevice(); + + static QList<QAudioDeviceId> deviceList(QAudio::Mode mode); + +private: + Q_DISABLE_COPY(QAudioDeviceInfo) + + QAbstractAudioDeviceInfo* d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIODEVICEINFO_H diff --git a/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp new file mode 100644 index 0000000..fe45f82 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp @@ -0,0 +1,394 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qaudiodeviceinfo_alsa_p.h" + +QAudioDeviceInfoPrivate::QAudioDeviceInfoPrivate(QByteArray dev, QAudio::Mode mode) +{ + handle = 0; + + device = QLatin1String(dev); + this->mode = mode; +} + +QAudioDeviceInfoPrivate::~QAudioDeviceInfoPrivate() +{ + close(); +} + +bool QAudioDeviceInfoPrivate::isFormatSupported(const QAudioFormat& format) const +{ + return testSettings(format); +} + +QAudioFormat QAudioDeviceInfoPrivate::preferredFormat() const +{ + QAudioFormat nearest; + if(mode == QAudio::AudioOutput) { + nearest.setFrequency(44100); + nearest.setChannels(2); + nearest.setByteOrder(QAudioFormat::LittleEndian); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(16); + nearest.setCodec(tr("audio/pcm")); + } else { + nearest.setFrequency(8000); + nearest.setChannels(1); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(8); + nearest.setCodec(tr("audio/pcm")); + } + return nearest; +} + +QAudioFormat QAudioDeviceInfoPrivate::nearestFormat(const QAudioFormat& format) const +{ + if(testSettings(format)) + return format; + else + return preferredFormat(); +} + +QString QAudioDeviceInfoPrivate::deviceName() const +{ + return device; +} + +QStringList QAudioDeviceInfoPrivate::codecList() +{ + updateLists(); + return codecz; +} + +QList<int> QAudioDeviceInfoPrivate::frequencyList() +{ + updateLists(); + return freqz; +} + +QList<int> QAudioDeviceInfoPrivate::channelsList() +{ + updateLists(); + return channelz; +} + +QList<int> QAudioDeviceInfoPrivate::sampleSizeList() +{ + updateLists(); + return sizez; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoPrivate::byteOrderList() +{ + updateLists(); + return byteOrderz; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoPrivate::sampleTypeList() +{ + updateLists(); + return typez; +} + +bool QAudioDeviceInfoPrivate::open() +{ + int err = 0; + QString dev = device; + if(!dev.contains(tr("default"))) { + int idx = snd_card_get_index(dev.toLocal8Bit().constData()); + dev = QString(tr("hw:%1,0")).arg(idx); + } + if(mode == QAudio::AudioOutput) { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); + } else { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); + } + if(err < 0) { + handle = 0; + return false; + } + return true; +} + +void QAudioDeviceInfoPrivate::close() +{ + if(handle) + snd_pcm_close(handle); + handle = 0; +} + +bool QAudioDeviceInfoPrivate::testSettings(const QAudioFormat& format) const +{ + // Set nearest to closest settings that do work. + // See if what is in settings will work (return value). + + int err = 0; + snd_pcm_t* handle; + snd_pcm_hw_params_t *params; + QString dev = device; + + // open() + if(!dev.contains(tr("default"))) { + int idx = snd_card_get_index(dev.toLocal8Bit().constData()); + dev = QString(tr("hw:%1,0")).arg(idx); + } + if(mode == QAudio::AudioOutput) { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); + } else { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); + } + if(err < 0) { + handle = 0; + return false; + } + + bool testChannel = false; + bool testCodec = false; + bool testFreq = false; + bool testType = false; + bool testSize = false; + + int dir = 0; + + snd_pcm_nonblock( handle, 0 ); + snd_pcm_hw_params_alloca( ¶ms ); + snd_pcm_hw_params_any( handle, params ); + + // For now, just accept only audio/pcm codec + if(!format.codec().startsWith(tr("audio/pcm"))) { + err=-1; + } else + testCodec = true; + + if(err>=0 && format.channels() != -1) { + err = snd_pcm_hw_params_test_channels(handle,params,format.channels()); + if(err>=0) + err = snd_pcm_hw_params_set_channels(handle,params,format.channels()); + if(err>=0) + testChannel = true; + } + + if(err>=0 && format.frequency() != -1) { + err = snd_pcm_hw_params_test_rate(handle,params,format.frequency(),0); + if(err>=0) + err = snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir); + if(err>=0) + testFreq = true; + } + + if((err>=0 && format.sampleSize() != -1) && + (format.sampleType() != QAudioFormat::Unknown)) { + switch(format.sampleSize()) { + case 8: + if(format.sampleType() == QAudioFormat::SignedInt) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8); + else if(format.sampleType() == QAudioFormat::UnSignedInt) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8); + break; + case 16: + if(format.sampleType() == QAudioFormat::SignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE); + } else if(format.sampleType() == QAudioFormat::UnSignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE); + } + break; + case 32: + if(format.sampleType() == QAudioFormat::SignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE); + } else if(format.sampleType() == QAudioFormat::UnSignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE); + } + } + if(err>=0) { + testSize = true; + testType = true; + } + } + if(err>=0) + err = snd_pcm_hw_params(handle, params); + + if(err == 0) { + // settings work + // close() + if(handle) + snd_pcm_close(handle); + return true; + } + if(handle) + snd_pcm_close(handle); + + return false; +} + +void QAudioDeviceInfoPrivate::updateLists() +{ + // redo all lists based on current settings + freqz.clear(); + channelz.clear(); + sizez.clear(); + byteOrderz.clear(); + typez.clear(); + codecz.clear(); + + if(!handle) + open(); + + if(!handle) + return; + + for(int i=0; i<(int)MAX_SAMPLE_RATES; i++) { + //if(snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0) + freqz.append(SAMPLE_RATES[i]); + } + channelz.append(1); + channelz.append(2); + sizez.append(8); + sizez.append(16); + sizez.append(32); + byteOrderz.append(QAudioFormat::LittleEndian); + byteOrderz.append(QAudioFormat::BigEndian); + typez.append(QAudioFormat::SignedInt); + typez.append(QAudioFormat::UnSignedInt); + typez.append(QAudioFormat::Float); + codecz.append(tr("audio/pcm")); + close(); +} + +QList<QByteArray> QAudioDeviceInfoPrivate::deviceList(QAudio::Mode mode) +{ + QAudio::Mode _m; + QList<QByteArray> devices; + QByteArray filter; + QString dir; + + // Create a list of all current audio devices that support mode + void **hints, **n; + char *name, *descr, *io; + + if(snd_device_name_hint(-1, "pcm", &hints) < 0) { + qWarning()<<"no alsa devices available"; + return devices; + } + n = hints; + + while (*n != NULL) { + _m = QAudio::AudioOutput; + name = snd_device_name_get_hint(*n, "NAME"); + descr = snd_device_name_get_hint(*n, "DESC"); + io = snd_device_name_get_hint(*n, "IOID"); + dir = QString::fromUtf8(io); + if((name != NULL) && (descr != NULL) && ((io == NULL) || (dir.length() ==filter.length()))) { + if(dir.length() == 5) + _m = QAudio::AudioInput; + if(io == NULL) + _m = mode; + + QString str = tr(name); + + if(str.contains(tr("default"))) { + int pos = str.indexOf(tr("="),0); + devices.append(str.mid(pos+1).toLocal8Bit().constData()); + } + } + if(name != NULL) + free(name); + if(descr != NULL) + free(descr); + if(io != NULL) + free(io); + n++; + } + snd_device_name_free_hint(hints); + + if(devices.size() > 0) { + devices.append("default"); + if(mode == QAudio::AudioInput) { + filter.append("Input"); + } else { + filter.append("Output"); + } + } + + return devices; +} + +QByteArray QAudioDeviceInfoPrivate::defaultInputDevice() +{ + QList<QByteArray> devices = deviceList(QAudio::AudioInput); + if(devices.size() == 0) + return QByteArray(); + + return QByteArray("default"); +} + +QByteArray QAudioDeviceInfoPrivate::defaultOutputDevice() +{ + QList<QByteArray> devices = deviceList(QAudio::AudioOutput); + if(devices.size() == 0) + return QByteArray(); + + return QByteArray("default"); +} + + diff --git a/src/multimedia/audio/qaudiodeviceinfo_alsa_p.h b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.h new file mode 100644 index 0000000..3ac9239 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_alsa_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIODEVICEINFOALSA_H +#define QAUDIODEVICEINFOALSA_H + +#include <alsa/asoundlib.h> + +#include <QtCore/qbytearray.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + +const unsigned int MAX_SAMPLE_RATES = 5; +const unsigned int SAMPLE_RATES[] = + { 8000, 11025, 22050, 44100, 48000 }; + +class QAudioDeviceInfoPrivate : public QAbstractAudioDeviceInfo +{ + Q_OBJECT +public: + QAudioDeviceInfoPrivate(QByteArray dev,QAudio::Mode mode); + ~QAudioDeviceInfoPrivate(); + + bool testSettings(const QAudioFormat& format) const; + void updateLists(); + QAudioFormat preferredFormat() const; + bool isFormatSupported(const QAudioFormat& format) const; + QAudioFormat nearestFormat(const QAudioFormat& format) const; + QString deviceName() const; + QStringList codecList(); + QList<int> frequencyList(); + QList<int> channelsList(); + QList<int> sampleSizeList(); + QList<QAudioFormat::Endian> byteOrderList(); + QList<QAudioFormat::SampleType> sampleTypeList(); + static QByteArray defaultInputDevice(); + static QByteArray defaultOutputDevice(); + static QList<QByteArray> deviceList(QAudio::Mode); + +private: + bool open(); + void close(); + + QString device; + QAudio::Mode mode; + QAudioFormat nearest; + QList<int> freqz; + QList<int> channelz; + QList<int> sizez; + QList<QAudioFormat::Endian> byteOrderz; + QStringList codecz; + QList<QAudioFormat::SampleType> typez; + snd_pcm_t* handle; + snd_pcm_hw_params_t *params; +}; + +#endif + diff --git a/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp new file mode 100644 index 0000000..c94e0c4 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_mac_p.cpp @@ -0,0 +1,357 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qdatastream.h> +#include <QtCore/qdebug.h> +#include <private/qcore_mac_p.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> +#include "qaudio_mac_p.h" +#include "qaudiodeviceinfo_mac_p.h" + + + +QT_BEGIN_NAMESPACE + + +QAudioDeviceInfoPrivate::QAudioDeviceInfoPrivate(QByteArray const& handle, QAudio::Mode) +{ + QDataStream ds(handle); + quint32 did, tm; + + ds >> did >> tm >> name; + deviceId = AudioDeviceID(did); + mode = QAudio::Mode(tm); +} + +bool QAudioDeviceInfoPrivate::isFormatSupported(const QAudioFormat& format) const +{ + return format.codec() == QString::fromLatin1("audio/pcm"); +} + +QAudioFormat QAudioDeviceInfoPrivate::preferredFormat() const +{ + QAudioFormat rc; + + UInt32 propSize = 0; + + if (AudioDeviceGetPropertyInfo(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreams, + &propSize, + 0) == noErr) { + + const int sc = propSize / sizeof(AudioStreamID); + + if (sc > 0) { + AudioStreamID* streams = new AudioStreamID[sc]; + + if (AudioDeviceGetProperty(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreams, + &propSize, + streams) == noErr) { + + for (int i = 0; i < sc; ++i) { + if (AudioStreamGetPropertyInfo(streams[i], + 0, + kAudioStreamPropertyPhysicalFormat, + &propSize, + 0) == noErr) { + + AudioStreamBasicDescription sf; + + if (AudioStreamGetProperty(streams[i], + 0, + kAudioStreamPropertyPhysicalFormat, + &propSize, + &sf) == noErr) { + rc = toQAudioFormat(sf); + break; + } + } + } + } + + delete streams; + } + } + + return rc; +} + +QAudioFormat QAudioDeviceInfoPrivate::nearestFormat(const QAudioFormat& format) const +{ + QAudioFormat rc(format); + QAudioFormat target = preferredFormat(); + + if (!format.codec().isEmpty() && format.codec() != QString::fromLatin1("audio/pcm")) + return QAudioFormat(); + + rc.setCodec(QString::fromLatin1("audio/pcm")); + + if (rc.frequency() != target.frequency()) + rc.setFrequency(target.frequency()); + if (rc.channels() != target.channels()) + rc.setChannels(target.channels()); + if (rc.sampleSize() != target.sampleSize()) + rc.setSampleSize(target.sampleSize()); + if (rc.byteOrder() != target.byteOrder()) + rc.setByteOrder(target.byteOrder()); + if (rc.sampleType() != target.sampleType()) + rc.setSampleType(target.sampleType()); + + return rc; +} + +QString QAudioDeviceInfoPrivate::deviceName() const +{ + return name; +} + +QStringList QAudioDeviceInfoPrivate::codecList() +{ + return QStringList() << QString::fromLatin1("audio/pcm"); +} + +QList<int> QAudioDeviceInfoPrivate::frequencyList() +{ + QSet<int> rc; + + // Add some common frequencies + rc << 8000 << 11025 << 22050 << 44100; + + // + UInt32 propSize = 0; + + if (AudioDeviceGetPropertyInfo(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyAvailableNominalSampleRates, + &propSize, + 0) == noErr) { + + const int pc = propSize / sizeof(AudioValueRange); + + if (pc > 0) { + AudioValueRange* vr = new AudioValueRange[pc]; + + if (AudioDeviceGetProperty(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyAvailableNominalSampleRates, + &propSize, + vr) == noErr) { + + for (int i = 0; i < pc; ++i) + rc << vr[i].mMaximum; + } + + delete vr; + } + } + + return rc.toList(); +} + +QList<int> QAudioDeviceInfoPrivate::channelsList() +{ + QList<int> rc; + + // Can mix down to 1 channel + rc << 1; + + UInt32 propSize = 0; + int channels = 0; + + if (AudioDeviceGetPropertyInfo(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreamConfiguration, + &propSize, + 0) == noErr) { + + AudioBufferList* audioBufferList = static_cast<AudioBufferList*>(qMalloc(propSize)); + + if (audioBufferList != 0) { + if (AudioDeviceGetProperty(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreamConfiguration, + &propSize, + audioBufferList) == noErr) { + + for (int i = 0; i < int(audioBufferList->mNumberBuffers); ++i) { + channels += audioBufferList->mBuffers[i].mNumberChannels; + rc << channels; + } + } + + qFree(audioBufferList); + } + } + + return rc; +} + +QList<int> QAudioDeviceInfoPrivate::sampleSizeList() +{ + return QList<int>() << 8 << 16 << 24 << 32 << 64; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoPrivate::byteOrderList() +{ + return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoPrivate::sampleTypeList() +{ + return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float; +} + +static QByteArray get_device_info(AudioDeviceID audioDevice, QAudio::Mode mode) +{ + UInt32 size; + QByteArray device; + QDataStream ds(&device, QIODevice::WriteOnly); + AudioStreamBasicDescription sf; + CFStringRef name; + Boolean isInput = mode == QAudio::AudioInput; + + // Id + ds << quint32(audioDevice); + + // Mode + size = sizeof(AudioStreamBasicDescription); + if (AudioDeviceGetProperty(audioDevice, 0, isInput, kAudioDevicePropertyStreamFormat, + &size, &sf) != noErr) { + return QByteArray(); + } + ds << quint32(mode); + + // Name + size = sizeof(CFStringRef); + if (AudioDeviceGetProperty(audioDevice, 0, isInput, kAudioObjectPropertyName, + &size, &name) != noErr) { + qWarning() << "QAudioDeviceInfo: Unable to find device name"; + } + ds << QCFString::toQString(name); + + CFRelease(name); + + return device; +} + +QByteArray QAudioDeviceInfoPrivate::defaultInputDevice() +{ + AudioDeviceID audioDevice; + UInt32 size = sizeof(audioDevice); + + if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &size, + &audioDevice) != noErr) { + qWarning() << "QAudioDeviceInfo: Unable to find default input device"; + return QByteArray(); + } + + return get_device_info(audioDevice, QAudio::AudioInput); +} + +QByteArray QAudioDeviceInfoPrivate::defaultOutputDevice() +{ + AudioDeviceID audioDevice; + UInt32 size = sizeof(audioDevice); + + if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, + &audioDevice) != noErr) { + qWarning() << "QAudioDeviceInfo: Unable to find default output device"; + return QByteArray(); + } + + return get_device_info(audioDevice, QAudio::AudioOutput); +} + +QList<QByteArray> QAudioDeviceInfoPrivate::deviceList(QAudio::Mode mode) +{ + QList<QByteArray> devices; + + UInt32 propSize = 0; + + if (AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propSize, 0) == noErr) { + + const int dc = propSize / sizeof(AudioDeviceID); + + if (dc > 0) { + AudioDeviceID* audioDevices = new AudioDeviceID[dc]; + + if (AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propSize, audioDevices) == noErr) { + for (int i = 0; i < dc; ++i) { + QByteArray info = get_device_info(audioDevices[i], mode); + if (!info.isNull()) + devices << info; + } + } + + delete audioDevices; + } + } + + return devices; +} + + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudiodeviceinfo_mac_p.h b/src/multimedia/audio/qaudiodeviceinfo_mac_p.h new file mode 100644 index 0000000..ee593de --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_mac_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QDEVICEINFO_MAC_P_H +#define QDEVICEINFO_MAC_P_H + +#include <CoreAudio/CoreAudio.h> + +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QAudioDeviceInfoPrivate : public QAbstractAudioDeviceInfo +{ +public: + AudioDeviceID deviceId; + QString name; + QAudio::Mode mode; + + QAudioDeviceInfoPrivate(QByteArray const& handle, QAudio::Mode mode); + + bool isFormatSupported(const QAudioFormat& format) const; + QAudioFormat preferredFormat() const; + QAudioFormat nearestFormat(const QAudioFormat& format) const; + + QString deviceName() const; + + QStringList codecList(); + QList<int> frequencyList(); + QList<int> channelsList(); + QList<int> sampleSizeList(); + QList<QAudioFormat::Endian> byteOrderList(); + QList<QAudioFormat::SampleType> sampleTypeList(); + + static QByteArray defaultInputDevice(); + static QByteArray defaultOutputDevice(); + + static QList<QByteArray> deviceList(QAudio::Mode mode); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDEVICEINFO_MAC_P_H diff --git a/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp b/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp new file mode 100644 index 0000000..1a1995a --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_win32_p.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include <windows.h> +#include <mmsystem.h> +#include "qaudiodeviceinfo_win32_p.h" + +// For mingw toolchain mmsystem.h only defines half the defines, so add if needed. +#ifndef WAVE_FORMAT_44M08 +#define WAVE_FORMAT_44M08 0x00000100 +#define WAVE_FORMAT_44S08 0x00000200 +#define WAVE_FORMAT_44M16 0x00000400 +#define WAVE_FORMAT_44S16 0x00000800 +#define WAVE_FORMAT_48M08 0x00001000 +#define WAVE_FORMAT_48S08 0x00002000 +#define WAVE_FORMAT_48M16 0x00004000 +#define WAVE_FORMAT_48S16 0x00008000 +#define WAVE_FORMAT_96M08 0x00010000 +#define WAVE_FORMAT_96S08 0x00020000 +#define WAVE_FORMAT_96M16 0x00040000 +#define WAVE_FORMAT_96S16 0x00080000 +#endif + + +QAudioDeviceInfoPrivate::QAudioDeviceInfoPrivate(QByteArray dev, QAudio::Mode mode) +{ + device = QLatin1String(dev); + this->mode = mode; +} + +QAudioDeviceInfoPrivate::~QAudioDeviceInfoPrivate() +{ + close(); +} + +bool QAudioDeviceInfoPrivate::isFormatSupported(const QAudioFormat& format) const +{ + return testSettings(format); +} + +QAudioFormat QAudioDeviceInfoPrivate::preferredFormat() const +{ + QAudioFormat nearest; + if(mode == QAudio::AudioOutput) { + nearest.setFrequency(44100); + nearest.setChannels(2); + nearest.setByteOrder(QAudioFormat::LittleEndian); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(16); + nearest.setCodec(tr("audio/pcm")); + } else { + nearest.setFrequency(11025); + nearest.setChannels(1); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(8); + nearest.setCodec(tr("audio/pcm")); + } + return nearest; +} + +QAudioFormat QAudioDeviceInfoPrivate::nearestFormat(const QAudioFormat& format) const +{ + if(testSettings(format)) + return format; + else + return preferredFormat(); +} + +QString QAudioDeviceInfoPrivate::deviceName() const +{ + return device; +} + +QStringList QAudioDeviceInfoPrivate::codecList() +{ + updateLists(); + return codecz; +} + +QList<int> QAudioDeviceInfoPrivate::frequencyList() +{ + updateLists(); + return freqz; +} + +QList<int> QAudioDeviceInfoPrivate::channelsList() +{ + updateLists(); + return channelz; +} + +QList<int> QAudioDeviceInfoPrivate::sampleSizeList() +{ + updateLists(); + return sizez; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoPrivate::byteOrderList() +{ + updateLists(); + return byteOrderz; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoPrivate::sampleTypeList() +{ + updateLists(); + return typez; +} + + +bool QAudioDeviceInfoPrivate::open() +{ + return true; +} + +void QAudioDeviceInfoPrivate::close() +{ +} + +bool QAudioDeviceInfoPrivate::testSettings(const QAudioFormat& format) const +{ + // Set nearest to closest settings that do work. + // See if what is in settings will work (return value). + + bool testChannel = false; + bool testCodec = false; + bool testFreq = false; + + int err = 0; + + // For now, just accept only audio/pcm codec + if(!format.codec().startsWith(tr("audio/pcm"))) { + err=-1; + } else + testCodec = true; + + if(err>=0 && format.channels() != -1) { + testChannel = true; + } + + if(err>=0 && format.frequency() != -1) { + testFreq = true; + } + + if(err == 0) { + // settings work + return true; + } + return false; +} + +void QAudioDeviceInfoPrivate::updateLists() +{ + // redo all lists based on current settings + bool base = false; + bool match = false; + DWORD fmt = NULL; + QString tmp; + + if(device.compare(tr("default")) == 0) + base = true; + + if(mode == QAudio::AudioOutput) { + WAVEOUTCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveOutGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveOutGetDevCaps(i, &woc, sizeof(WAVEOUTCAPS)) + == MMSYSERR_NOERROR) { + tmp = QString::fromUtf16((const unsigned short*)woc.szPname); + if(tmp.compare(device) == 0) { + match = true; + fmt = woc.dwFormats; + break; + } + if(base) { + match = true; + fmt = woc.dwFormats; + break; + } + } + } + } else { + WAVEINCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveInGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveInGetDevCaps(i, &woc, sizeof(WAVEINCAPS)) + == MMSYSERR_NOERROR) { + tmp = QString::fromUtf16((const unsigned short*)woc.szPname); + if(tmp.compare(device) == 0) { + match = true; + fmt = woc.dwFormats; + break; + } + if(base) { + match = true; + fmt = woc.dwFormats; + break; + } + } + } + } + sizez.clear(); + freqz.clear(); + channelz.clear(); + byteOrderz.clear(); + typez.clear(); + codecz.clear(); + + if(match) { + if((fmt && WAVE_FORMAT_1M08) + || (fmt && WAVE_FORMAT_1S08) + || (fmt && WAVE_FORMAT_2M08) + || (fmt && WAVE_FORMAT_2S08) + || (fmt && WAVE_FORMAT_4M08) + || (fmt && WAVE_FORMAT_4S08) +#ifndef Q_OS_WINCE + || (fmt && WAVE_FORMAT_48M08) + || (fmt && WAVE_FORMAT_48S08) + || (fmt && WAVE_FORMAT_96M08) + || (fmt && WAVE_FORMAT_96S08) +#endif + ) { + sizez.append(8); + } + if((fmt && WAVE_FORMAT_1M16) + || (fmt && WAVE_FORMAT_1S16) + || (fmt && WAVE_FORMAT_2M16) + || (fmt && WAVE_FORMAT_2S16) + || (fmt && WAVE_FORMAT_4M16) + || (fmt && WAVE_FORMAT_4S16) +#ifndef Q_OS_WINCE + || (fmt && WAVE_FORMAT_48M16) + || (fmt && WAVE_FORMAT_48S16) + || (fmt && WAVE_FORMAT_96M16) + || (fmt && WAVE_FORMAT_96S16) +#endif + ) { + sizez.append(16); + } + if((fmt && WAVE_FORMAT_1M08) + || (fmt && WAVE_FORMAT_1S08) + || (fmt && WAVE_FORMAT_1M16) + || (fmt && WAVE_FORMAT_1S16)) { + freqz.append(11025); + } + if((fmt && WAVE_FORMAT_2M08) + || (fmt && WAVE_FORMAT_2S08) + || (fmt && WAVE_FORMAT_2M16) + || (fmt && WAVE_FORMAT_2S16)) { + freqz.append(22050); + } + if((fmt && WAVE_FORMAT_4M08) + || (fmt && WAVE_FORMAT_4S08) + || (fmt && WAVE_FORMAT_4M16) + || (fmt && WAVE_FORMAT_4S16)) { + freqz.append(44100); + } +#ifndef Q_OS_WINCE + if((fmt && WAVE_FORMAT_48M08) + || (fmt && WAVE_FORMAT_48S08) + || (fmt && WAVE_FORMAT_48M16) + || (fmt && WAVE_FORMAT_48S16)) { + freqz.append(48000); + } + if((fmt && WAVE_FORMAT_96M08) + || (fmt && WAVE_FORMAT_96S08) + || (fmt && WAVE_FORMAT_96M16) + || (fmt && WAVE_FORMAT_96S16)) { + freqz.append(96000); + } +#endif + channelz.append(1); + channelz.append(2); + + byteOrderz.append(QAudioFormat::LittleEndian); + + typez.append(QAudioFormat::SignedInt); + typez.append(QAudioFormat::UnSignedInt); + + codecz.append(tr("audio/pcm")); + } +} + +QList<QByteArray> QAudioDeviceInfoPrivate::deviceList(QAudio::Mode mode) +{ + Q_UNUSED(mode) + + QList<QByteArray> devices; + + devices.append("default"); + + if(mode == QAudio::AudioOutput) { + WAVEOUTCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveOutGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveOutGetDevCaps(i, &woc, sizeof(WAVEOUTCAPS)) + == MMSYSERR_NOERROR) { + devices.append(QString::fromUtf16((const unsigned short*)woc.szPname).toLocal8Bit().constData()); + } + } + } else { + WAVEINCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveInGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveInGetDevCaps(i, &woc, sizeof(WAVEINCAPS)) + == MMSYSERR_NOERROR) { + devices.append(QString::fromUtf16((const unsigned short*)woc.szPname).toLocal8Bit().constData()); + } + } + + } + return devices; +} + +QByteArray QAudioDeviceInfoPrivate::defaultOutputDevice() +{ + return QByteArray("default"); +} + +QByteArray QAudioDeviceInfoPrivate::defaultInputDevice() +{ + return QByteArray("default"); +} + diff --git a/src/multimedia/audio/qaudiodeviceinfo_win32_p.h b/src/multimedia/audio/qaudiodeviceinfo_win32_p.h new file mode 100644 index 0000000..140a741 --- /dev/null +++ b/src/multimedia/audio/qaudiodeviceinfo_win32_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIODEVICEINFOWIN_H +#define QAUDIODEVICEINFOWIN_H + +#include <QtCore/qbytearray.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +const unsigned int MAX_SAMPLE_RATES = 5; +const unsigned int SAMPLE_RATES[] = { 8000, 11025, 22050, 44100, 48000 }; + +class QAudioDeviceInfoPrivate : public QAbstractAudioDeviceInfo +{ + Q_OBJECT + +public: + QAudioDeviceInfoPrivate(QByteArray dev,QAudio::Mode mode); + ~QAudioDeviceInfoPrivate(); + + bool open(); + void close(); + + bool testSettings(const QAudioFormat& format) const; + void updateLists(); + QAudioFormat preferredFormat() const; + bool isFormatSupported(const QAudioFormat& format) const; + QAudioFormat nearestFormat(const QAudioFormat& format) const; + QString deviceName() const; + QStringList codecList(); + QList<int> frequencyList(); + QList<int> channelsList(); + QList<int> sampleSizeList(); + QList<QAudioFormat::Endian> byteOrderList(); + QList<QAudioFormat::SampleType> sampleTypeList(); + static QByteArray defaultInputDevice(); + static QByteArray defaultOutputDevice(); + static QList<QByteArray> deviceList(QAudio::Mode); + +private: + QAudio::Mode mode; + QString device; + QAudioFormat nearest; + QList<int> freqz; + QList<int> channelz; + QList<int> sizez; + QList<QAudioFormat::Endian> byteOrderz; + QStringList codecz; + QList<QAudioFormat::SampleType> typez; +}; + +#endif diff --git a/src/multimedia/audio/qaudioengine.cpp b/src/multimedia/audio/qaudioengine.cpp new file mode 100644 index 0000000..930977e --- /dev/null +++ b/src/multimedia/audio/qaudioengine.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractAudioDeviceInfo + \brief The QAbstractAudioDeviceInfo class provides access for QAudioDeviceInfo to access the audio + device provided by the plugin. + \internal + + \ingroup multimedia + + This class implements the audio functionality for + QAudioDeviceInfo, i.e., QAudioDeviceInfo keeps a + QAbstractAudioDeviceInfo and routes function calls to it. For a + description of the functionality that QAbstractAudioDeviceInfo + implements, you can read the class and functions documentation of + QAudioDeviceInfo. + + \sa QAudioDeviceInfo +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioDeviceInfo::preferredFormat() const + Returns the nearest settings. +*/ + +/*! + \fn virtual bool QAbstractAudioDeviceInfo::isFormatSupported(const QAudioFormat& format) const + Returns true if \a format is available from audio device. +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioDeviceInfo::nearestFormat(const QAudioFormat& format) const + Returns the nearest settings \a format. +*/ + +/*! + \fn virtual QString QAbstractAudioDeviceInfo::deviceName() const + Returns the audio device name. +*/ + +/*! + \fn virtual QStringList QAbstractAudioDeviceInfo::codecList() + Returns the list of currently available codecs. +*/ + +/*! + \fn virtual QList<int> QAbstractAudioDeviceInfo::frequencyList() + Returns the list of currently available frequencies. +*/ + +/*! + \fn virtual QList<int> QAbstractAudioDeviceInfo::channelsList() + Returns the list of currently available channels. +*/ + +/*! + \fn virtual QList<int> QAbstractAudioDeviceInfo::sampleSizeList() + Returns the list of currently available sample sizes. +*/ + +/*! + \fn virtual QList<QAudioFormat::Endian> QAbstractAudioDeviceInfo::byteOrderList() + Returns the list of currently available byte orders. +*/ + +/*! + \fn virtual QList<QAudioFormat::SampleType> QAbstractAudioDeviceInfo::sampleTypeList() + Returns the list of currently available sample types. +*/ + +/*! + \class QAbstractAudioOutput + \brief The QAbstractAudioOutput class provides access for QAudioOutput to access the audio + device provided by the plugin. + \internal + + \ingroup multimedia + + QAbstractAudioOutput implements audio functionality for + QAudioOutput, i.e., QAudioOutput routes function calls to + QAbstractAudioOutput. For a description of the functionality that + is implemented, see the QAudioOutput class and function + descriptions. + + \sa QAudioOutput +*/ + +/*! + \fn virtual QIODevice* QAbstractAudioOutput::start(QIODevice* device) + Uses the \a device as the QIODevice to transfer data. If \a device is null then the class + creates an internal QIODevice. Returns a pointer to the QIODevice being used to handle + the data transfer. This QIODevice can be used to write() audio data directly. Passing a + QIODevice allows the data to be transfered without any extra code. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::stop() + Stops the audio output. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::reset() + Drops all audio data in the buffers, resets buffers to zero. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::suspend() + Stops processing audio data, preserving buffered audio data. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::resume() + Resumes processing audio data after a suspend() +*/ + +/*! + \fn virtual int QAbstractAudioOutput::bytesFree() const + Returns the free space available in bytes in the audio buffer. +*/ + +/*! + \fn virtual int QAbstractAudioOutput::periodSize() const + Returns the period size in bytes. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::setBufferSize(int value) + Sets the audio buffer size to \a value in bytes. +*/ + +/*! + \fn virtual int QAbstractAudioOutput::bufferSize() const + Returns the audio buffer size in bytes. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::setNotifyInterval(int ms) + Sets the interval for notify() signal to be emitted. This is based on the \a ms + of audio data processed not on actual real-time. The resolution of the timer + is platform specific. +*/ + +/*! + \fn virtual int QAbstractAudioOutput::notifyInterval() const + Returns the notify interval in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioOutput::totalTime() const + Returns the amount of audio data processed since start() was called in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioOutput::clock() const + Returns the milliseconds since start() was called, including time in Idle and suspend states. +*/ + +/*! + \fn virtual QAudio::Error QAbstractAudioOutput::error() const + Returns the error state. +*/ + +/*! + \fn virtual QAudio::State QAbstractAudioOutput::state() const + Returns the state of audio processing. +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioOutput::format() const + Returns the QAudioFormat being used. +*/ + +/*! + \fn QAbstractAudioOutput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. +*/ + +/*! + \fn QAbstractAudioOutput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + + +/*! + \class QAbstractAudioInput + \brief The QAbstractAudioInput class provides access for QAudioInput to access the audio + device provided by the plugin. + \internal + + \ingroup multimedia + + QAudioDeviceInput keeps an instance of QAbstractAudioInput and + routes calls to functions of the same name to QAbstractAudioInput. + This means that it is QAbstractAudioInput that implements the + audio functionality. For a description of the functionality, see + the QAudioInput class description. + + \sa QAudioInput +*/ + +/*! + \fn virtual QIODevice* QAbstractAudioInput::start(QIODevice* device) + Uses the \a device as the QIODevice to transfer data. If \a device is null + then the class creates an internal QIODevice. Returns a pointer to the + QIODevice being used to handle the data transfer. This QIODevice can be used to + read() audio data directly. Passing a QIODevice allows the data to be transfered + without any extra code. +*/ + +/*! + \fn virtual void QAbstractAudioInput::stop() + Stops the audio input. +*/ + +/*! + \fn virtual void QAbstractAudioInput::reset() + Drops all audio data in the buffers, resets buffers to zero. +*/ + +/*! + \fn virtual void QAbstractAudioInput::suspend() + Stops processing audio data, preserving buffered audio data. +*/ + +/*! + \fn virtual void QAbstractAudioInput::resume() + Resumes processing audio data after a suspend(). +*/ + +/*! + \fn virtual int QAbstractAudioInput::bytesReady() const + Returns the amount of audio data available to read in bytes. +*/ + +/*! + \fn virtual int QAbstractAudioInput::periodSize() const + Returns the period size in bytes. +*/ + +/*! + \fn virtual void QAbstractAudioInput::setBufferSize(int value) + Sets the audio buffer size to \a value in milliseconds. +*/ + +/*! + \fn virtual int QAbstractAudioInput::bufferSize() const + Returns the audio buffer size in milliseconds. +*/ + +/*! + \fn virtual void QAbstractAudioInput::setNotifyInterval(int ms) + Sets the interval for notify() signal to be emitted. This is based + on the \a ms of audio data processed not on actual real-time. + The resolution of the timer is platform specific. +*/ + +/*! + \fn virtual int QAbstractAudioInput::notifyInterval() const + Returns the notify interval in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioInput::totalTime() const + Returns the amount of audio data processed since start() was called in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioInput::clock() const + Returns the milliseconds since start() was called, including time in Idle and suspend states. +*/ + +/*! + \fn virtual QAudio::Error QAbstractAudioInput::error() const + Returns the error state. +*/ + +/*! + \fn virtual QAudio::State QAbstractAudioInput::state() const + Returns the state of audio processing. +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioInput::format() const + Returns the QAudioFormat being used +*/ + +/*! + \fn QAbstractAudioInput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. +*/ + +/*! + \fn QAbstractAudioInput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + + +QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudioengine.h b/src/multimedia/audio/qaudioengine.h new file mode 100644 index 0000000..b0aa762 --- /dev/null +++ b/src/multimedia/audio/qaudioengine.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAUDIOENGINE_H +#define QAUDIOENGINE_H + +#include <QtCore/qglobal.h> +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class Q_MULTIMEDIA_EXPORT QAbstractAudioDeviceInfo : public QObject +{ + Q_OBJECT + +public: + virtual QAudioFormat preferredFormat() const = 0; + virtual bool isFormatSupported(const QAudioFormat &format) const = 0; + virtual QAudioFormat nearestFormat(const QAudioFormat &format) const = 0; + virtual QString deviceName() const = 0; + virtual QStringList codecList() = 0; + virtual QList<int> frequencyList() = 0; + virtual QList<int> channelsList() = 0; + virtual QList<int> sampleSizeList() = 0; + virtual QList<QAudioFormat::Endian> byteOrderList() = 0; + virtual QList<QAudioFormat::SampleType> sampleTypeList() = 0; +}; + +class Q_MULTIMEDIA_EXPORT QAbstractAudioOutput : public QObject +{ + Q_OBJECT + +public: + virtual QIODevice* start(QIODevice* device) = 0; + virtual void stop() = 0; + virtual void reset() = 0; + virtual void suspend() = 0; + virtual void resume() = 0; + virtual int bytesFree() const = 0; + virtual int periodSize() const = 0; + virtual void setBufferSize(int value) = 0; + virtual int bufferSize() const = 0; + virtual void setNotifyInterval(int milliSeconds) = 0; + virtual int notifyInterval() const = 0; + virtual qint64 totalTime() const = 0; + virtual qint64 clock() const = 0; + virtual QAudio::Error error() const = 0; + virtual QAudio::State state() const = 0; + virtual QAudioFormat format() const = 0; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); +}; + +class Q_MULTIMEDIA_EXPORT QAbstractAudioInput : public QObject +{ + Q_OBJECT + +public: + virtual QIODevice* start(QIODevice* device) = 0; + virtual void stop() = 0; + virtual void reset() = 0; + virtual void suspend() = 0; + virtual void resume() = 0; + virtual int bytesReady() const = 0; + virtual int periodSize() const = 0; + virtual void setBufferSize(int value) = 0; + virtual int bufferSize() const = 0; + virtual void setNotifyInterval(int milliSeconds) = 0; + virtual int notifyInterval() const = 0; + virtual qint64 totalTime() const = 0; + virtual qint64 clock() const = 0; + virtual QAudio::Error error() const = 0; + virtual QAudio::State state() const = 0; + virtual QAudioFormat format() const = 0; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOENGINE_H diff --git a/src/multimedia/audio/qaudioengineplugin.cpp b/src/multimedia/audio/qaudioengineplugin.cpp new file mode 100644 index 0000000..6e39004 --- /dev/null +++ b/src/multimedia/audio/qaudioengineplugin.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtMultimedia/qaudioengineplugin.h> + +QT_BEGIN_NAMESPACE + +QAudioEnginePlugin::QAudioEnginePlugin(QObject* parent) : + QObject(parent) +{} + +QAudioEnginePlugin::~QAudioEnginePlugin() +{} + +QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudioengineplugin.h b/src/multimedia/audio/qaudioengineplugin.h new file mode 100644 index 0000000..8eab2d7 --- /dev/null +++ b/src/multimedia/audio/qaudioengineplugin.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIOENGINEPLUGIN_H +#define QAUDIOENGINEPLUGIN_H + +#include <QtCore/qstring.h> +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +struct Q_MULTIMEDIA_EXPORT QAudioEngineFactoryInterface : public QFactoryInterface +{ + virtual QList<QByteArray> deviceList(QAudio::Mode) const = 0; + virtual QAbstractAudioInput* createInput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioOutput* createOutput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioDeviceInfo* createDeviceInfo(const QByteArray& device, QAudio::Mode mode) = 0; +}; + +#define QAudioEngineFactoryInterface_iid \ + "com.nokia.qt.QAudioEngineFactoryInterface" +Q_DECLARE_INTERFACE(QAudioEngineFactoryInterface, QAudioEngineFactoryInterface_iid) + +class Q_MULTIMEDIA_EXPORT QAudioEnginePlugin : public QObject, public QAudioEngineFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QAudioEngineFactoryInterface:QFactoryInterface) + +public: + QAudioEnginePlugin(QObject *parent = 0); + ~QAudioEnginePlugin(); + + virtual QStringList keys() const = 0; + virtual QList<QByteArray> deviceList(QAudio::Mode) const = 0; + virtual QAbstractAudioInput* createInput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioOutput* createOutput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioDeviceInfo* createDeviceInfo(const QByteArray& device, QAudio::Mode mode) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOENGINEPLUGIN_H diff --git a/src/multimedia/audio/qaudioformat.cpp b/src/multimedia/audio/qaudioformat.cpp new file mode 100644 index 0000000..6632c63 --- /dev/null +++ b/src/multimedia/audio/qaudioformat.cpp @@ -0,0 +1,335 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtMultimedia/qaudioformat.h> + + +QT_BEGIN_NAMESPACE + + +class QAudioFormatPrivate : public QSharedData +{ +public: + QAudioFormatPrivate() + { + frequency = -1; + channels = -1; + sampleSize = -1; + byteOrder = QAudioFormat::Endian(QSysInfo::ByteOrder); + sampleType = QAudioFormat::Unknown; + } + + QString codec; + QAudioFormat::Endian byteOrder; + QAudioFormat::SampleType sampleType; + + int frequency; + int channels; + int sampleSize; +}; + +/*! + \class QAudioFormat + \brief The QAudioFormat class stores audio parameter information. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + An audio format specifies how data in an audio stream is arranged, + i.e, how the stream is to be interpreted. The encoding itself is + specified by the codec() used for the stream. + + In addition to the encoding, QAudioFormat contains other + parameters that further specify how the audio data is arranged. + These are the frequency, the number of channels, the sample size, + the sample type, and the byte order. The following table describes + these in more detail. + + \table + \header + \o Parameter + \o Description + \row + \o Frequency + \o Samples per second of audio data in Hertz. + \row + \o Number of channels + \o The number of audio channels (typically one for mono + or two for stereo) + \row + \o Sample size + \o How much data is stored in each sample (typically 8 + or 16) + \row + \o Sample type + \o Numerical representation of sample (typically signed integer, + unsigned integer or float) + \row + \o Byte order + \o Byte ordering of sample (typically little endian, big endian) + \endtable + + You can obtain audio formats compatible with the audio device used + through functions in QAudioDeviceInfo. This class also lets you + query available parameter values for a device, so that you can set + the parameters yourself. See the QAudioDeviceInfo class + description for details. +*/ + +/*! + Construct a new audio format. + + Values are initialized as follows: + \list + \o frequency() = -1 + \o channels() = -1 + \o sampleSize() = -1 + \o byteOrder() = QAudioFormat::Endian(QSysInfo::ByteOrder) + \o sampleType() = QAudioFormat::Unknown + \c codec() = "" + \endlist +*/ + +QAudioFormat::QAudioFormat(): + d(new QAudioFormatPrivate) +{ +} + +/*! + Construct a new audio format using \a other. +*/ + +QAudioFormat::QAudioFormat(const QAudioFormat &other): + d(other.d) +{ +} + +/*! + Destroy this audio format. +*/ + +QAudioFormat::~QAudioFormat() +{ +} + +/*! + Assigns \a other to this QAudioFormat implementation. +*/ + +QAudioFormat& QAudioFormat::operator=(const QAudioFormat &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this QAudioFormat is equal to the \a other + QAudioFormat; otherwise returns false. + + All elements of QAudioFormat are used for the comparison. +*/ + +bool QAudioFormat::operator==(const QAudioFormat &other) const +{ + return d->frequency == other.d->frequency && + d->channels == other.d->channels && + d->sampleSize == other.d->sampleSize && + d->byteOrder == other.d->byteOrder && + d->codec == other.d->codec && + d->sampleType == other.d->sampleType; +} + +/*! + Returns true if this QAudioFormat is not equal to the \a other + QAudioFormat; otherwise returns false. + + All elements of QAudioFormat are used for the comparison. +*/ + +bool QAudioFormat::operator!=(const QAudioFormat& other) const +{ + return !(*this == other); +} + +/*! + Returns true if any of the parameters are invalid. +*/ + +bool QAudioFormat::isNull() const +{ + return d->frequency == -1 && d->channels == -1 && + d->sampleSize == -1 && + d->byteOrder == QAudioFormat::Endian(QSysInfo::ByteOrder) && + d->sampleType == QAudioFormat::Unknown && + d->codec.isNull(); +} + +/*! + Sets the frequency to \a frequency. +*/ + +void QAudioFormat::setFrequency(int frequency) +{ + d->frequency = frequency; +} + +/*! + Returns the current frequency value. +*/ + +int QAudioFormat::frequency() const +{ + return d->frequency; +} + +/*! + Sets the channels to \a channels. +*/ + +void QAudioFormat::setChannels(int channels) +{ + d->channels = channels; +} + +/*! + Returns the current channel value. +*/ + +int QAudioFormat::channels() const +{ + return d->channels; +} + +/*! + Sets the sampleSize to \a sampleSize. +*/ + +void QAudioFormat::setSampleSize(int sampleSize) +{ + d->sampleSize = sampleSize; +} + +/*! + Returns the current sampleSize value. +*/ + +int QAudioFormat::sampleSize() const +{ + return d->sampleSize; +} + +/*! + Sets the codec to \a codec. + + \sa QAudioDeviceInfo::supportedCodecs() +*/ + +void QAudioFormat::setCodec(QString codec) +{ + d->codec = codec; +} + +/*! + Returns the current codec value. + + \sa QAudioDeviceInfo::supportedCodecs() +*/ + +QString QAudioFormat::codec() const +{ + return d->codec; +} + +/*! + Sets the byteOrder to \a byteOrder. +*/ + +void QAudioFormat::setByteOrder(QAudioFormat::Endian byteOrder) +{ + d->byteOrder = byteOrder; +} + +/*! + Returns the current byteOrder value. +*/ + +QAudioFormat::Endian QAudioFormat::byteOrder() const +{ + return d->byteOrder; +} + +/*! + Sets the sampleType to \a sampleType. +*/ + +void QAudioFormat::setSampleType(QAudioFormat::SampleType sampleType) +{ + d->sampleType = sampleType; +} + +/*! + Returns the current SampleType value. +*/ + +QAudioFormat::SampleType QAudioFormat::sampleType() const +{ + return d->sampleType; +} + +/*! + \enum QAudioFormat::SampleType + + \value Unknown Not Set + \value SignedInt samples are signed integers + \value UnSignedInt samples are unsigned intergers + \value Float samples are floats +*/ + +/*! + \enum QAudioFormat::Endian + + \value BigEndian samples are big endian byte order + \value LittleEndian samples are little endian byte order +*/ + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudioformat.h b/src/multimedia/audio/qaudioformat.h new file mode 100644 index 0000000..c740969 --- /dev/null +++ b/src/multimedia/audio/qaudioformat.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIOFORMAT_H +#define QAUDIOFORMAT_H + +#include <QtCore/qobject.h> +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAudioFormatPrivate; + +class Q_MULTIMEDIA_EXPORT QAudioFormat +{ +public: + enum SampleType { Unknown, SignedInt, UnSignedInt, Float }; + enum Endian { BigEndian = QSysInfo::BigEndian, LittleEndian = QSysInfo::LittleEndian }; + + QAudioFormat(); + QAudioFormat(const QAudioFormat &other); + ~QAudioFormat(); + + QAudioFormat& operator=(const QAudioFormat &other); + bool operator==(const QAudioFormat &other) const; + bool operator!=(const QAudioFormat &other) const; + + bool isNull() const; + + void setFrequency(int frequency); + int frequency() const; + + void setChannels(int channels); + int channels() const; + + void setSampleSize(int sampleSize); + int sampleSize() const; + + void setCodec(QString codec); + QString codec() const; + + void setByteOrder(QAudioFormat::Endian byteOrder); + QAudioFormat::Endian byteOrder() const; + + void setSampleType(QAudioFormat::SampleType sampleType); + QAudioFormat::SampleType sampleType() const; + +private: + QSharedDataPointer<QAudioFormatPrivate> d; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOFORMAT_H diff --git a/src/multimedia/audio/qaudioinput.cpp b/src/multimedia/audio/qaudioinput.cpp new file mode 100644 index 0000000..17cacc6 --- /dev/null +++ b/src/multimedia/audio/qaudioinput.cpp @@ -0,0 +1,400 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudioinput.h> + +#include "qaudiodevicefactory_p.h" + + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioInput + \brief The QAudioInput class provides an interface for receiving audio data from an audio input device. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + You can construct an audio input with the system's + \l{QAudioDeviceInfo::defaultInputDevice()}{default audio input + device}. It is also possible to create QAudioInput with a + specific QAudioDeviceId. When you create the audio input, you + should also send in the QAudioFormat to be used for the recording + (see the QAudioFormat class description for details). + + To record to a file: + + QAudioInput lets you record audio with an audio input device. The + default constructor of this class will use the systems default + audio device, but you can also specify a QAudioDeviceId for a + specific device. You also need to pass in the QAudioFormat in + which you wish to record. + + Starting up the QAudioInput is simply a matter of calling start() + with a QIODevice opened for writing. For instance, to record to a + file, you can: + + \code + { + QFile outputFile; + outputFile.setFileName("/tmp/test.raw"); + outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate ); + + QAudioFormat format; + // set up the format you want, eg. + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::UnSignedInt); + + QAudioInput *audio = new QAudioInput(format, this); + QTimer::singleShot(3000, this, SLOT(stopRecording())); + audio->start(outputFile); + // Records audio for 3000ms + } + \endcode + + This will start recording if the format specified is supported by + the input device (you can check this with + QAudioDeviceInfo::isFormatSupported(). In case there are any + snags, use the error() function to check what went wrong. We stop + recording in the \c stopRecording() slot. + + \code + void stopRecording() + { + audio->stop(); + outputFile->close(); + } + \endcode + + At any point in time, QAudioInput will be in one of four states: + active, suspended, stopped, or idle. These states are specified by + the QAudio::State enum. You can request a state change directly through + suspend(), resume(), stop(), reset(), and start(). The current + state is reported by state(). QAudioOutput will also signal you + when the state changes (stateChanged()). + + QAudioInput provides several ways of measuring the time that has + passed since the start() of the recording. The \c totalTime() + function returns the length of the stream in microseconds written, + i.e., it leaves out the times the audio input was suspended or idle. + The clock() function returns the time elapsed since start() was called regardless of + which states the QAudioInput has been in. + + If an error should occur, you can fetch its reason with error(). + The possible error reasons are described by the QAudio::Error enum. + + \sa QAudioOutput, QAudioDeviceInfo +*/ + +/*! + Construct a new audio input and attach it to \a parent. + The default audio input device is used with the output + \a format parameters. +*/ + +QAudioInput::QAudioInput(const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createDefaultInputDevice(format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Construct a new audio input and attach it to \a parent. + The \a id of the audio input device is used with the input + \a format parameters. +*/ + +QAudioInput::QAudioInput(const QAudioDeviceId &id, const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createInputDevice(id, format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Destroy this audio input. +*/ + +QAudioInput::~QAudioInput() +{ + delete d; +} + +/*! + Uses the \a device as the QIODevice to transfer data. + If \a device is null then the class creates an internal QIODevice. + Returns a pointer to the QIODevice being used to handle the data + transfer. This QIODevice can be used to read() audio data + directly. + Passing a QIODevice allows the data to be transfered without any extra code. + All that is required is to open the QIODevice. + + /sa QIODevice +*/ + +QIODevice* QAudioInput::start(QIODevice* device) +{ + /* + PULL MODE (valid QIODevice) + -If currently not StopState, stop + -If previous start was push mode, delete internal QIODevice. + -open audio input. + If ok, NoError and ActiveState, else OpenError and StopState. + -emit stateChanged() + -return device + + PUSH MODE (device = 0) + -If currently not StopState, stop + -If no internal QIODevice, create one. + -open audio input. + -If ok, NoError and IdleState, else OpenError and StopState + -emit stateChanged() + -return internal QIODevice + */ + return d->start(device); +} + +/*! + Returns the QAudioFormat being used. +*/ + +QAudioFormat QAudioInput::format() const +{ + return d->format(); +} + +/*! + Stops the audio input. +*/ + +void QAudioInput::stop() +{ + /* + -If StopState, return + -set to StopState + -detach from audio device + -emit stateChanged() + */ + d->stop(); +} + +/*! + Drops all audio data in the buffers, resets buffers to zero. +*/ + +void QAudioInput::reset() +{ + /* + -drop all buffered audio, set buffers to zero. + -call stop() + */ + d->reset(); +} + +/*! + Stops processing audio data, preserving buffered audio data. +*/ + +void QAudioInput::suspend() +{ + /* + -If not ActiveState|IdleState, return + -stop processing audio, saving all buffered audio data + -set NoError and SuspendState + -emit stateChanged() + */ + d->suspend(); +} + +/*! + Resumes processing audio data after a suspend(). +*/ + +void QAudioInput::resume() +{ + /* + -If SuspendState, return + -resume audio + -(PULL MODE): set ActiveState, NoError + -(PUSH MODE): set IdleState, NoError + -kick start audio if needed + -emit stateChanged() + */ + d->resume(); +} + +/*! + Sets the audio buffer size to \a value milliseconds. + + Note: This function can be called anytime before start(), calls to this + are ignored after start(). It should not be assumed that the buffer size + set is the actual buffer size used, calling bufferSize() anytime after start() + will return the actual buffer size being used. + +*/ + +void QAudioInput::setBufferSize(int value) +{ + d->setBufferSize(value); +} + +/*! + Returns the audio buffer size in milliseconds. + + If called before start(), returns platform default value. + If called before start() but setBufferSize() was called prior, returns value set by setBufferSize(). + If called after start(), returns the actual buffer size being used. This may not be what was set previously + by setBufferSize(). + +*/ + +int QAudioInput::bufferSize() const +{ + return d->bufferSize(); +} + +/*! + Returns the amount of audio data available to read in bytes. +*/ + +int QAudioInput::bytesReady() const +{ + /* + -If not ActiveState|IdleState, return 0 + -return amount of audio data available to read + */ + return d->bytesReady(); +} + +/*! + Returns the period size in bytes. + + Note: This is the recommended read size in bytes. +*/ + +int QAudioInput::periodSize() const +{ + return d->periodSize(); +} + +/*! + Sets the interval for notify() signal to be emitted. + This is based on the \a ms of audio data processed + not on actual real-time. The resolution of the timer is platform specific. +*/ + +void QAudioInput::setNotifyInterval(int ms) +{ + d->setNotifyInterval(ms); +} + +/*! + Returns the notify interval in milliseconds. +*/ + +int QAudioInput::notifyInterval() const +{ + return d->notifyInterval(); +} + +/*! + Returns the amount of audio data processed since start() + was called in microseconds. +*/ + +qint64 QAudioInput::totalTime() const +{ + return d->totalTime(); +} + +/*! + Returns the microseconds since start() was called, including time in Idle and + Suspend states. +*/ + +qint64 QAudioInput::clock() const +{ + return d->clock(); +} + +/*! + Returns the error state. +*/ + +QAudio::Error QAudioInput::error() const +{ + return d->error(); +} + +/*! + Returns the state of audio processing. +*/ + +QAudio::State QAudioInput::state() const +{ + return d->state(); +} + +/*! + \fn QAudioInput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. +*/ + +/*! + \fn QAudioInput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + +QT_END_NAMESPACE + diff --git a/src/multimedia/audio/qaudioinput.h b/src/multimedia/audio/qaudioinput.h new file mode 100644 index 0000000..b0f1aed --- /dev/null +++ b/src/multimedia/audio/qaudioinput.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIOINPUT_H +#define QAUDIOINPUT_H + +#include <QtCore/qiodevice.h> +#include <QtCore/qglobal.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceid.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAbstractAudioInput; + +class Q_MULTIMEDIA_EXPORT QAudioInput : public QObject +{ + Q_OBJECT + +public: + explicit QAudioInput(const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + explicit QAudioInput(const QAudioDeviceId &id, const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + ~QAudioInput(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice *device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + + void setBufferSize(int bytes); + int bufferSize() const; + + int bytesReady() const; + int periodSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 totalTime() const; + qint64 clock() const; + + QAudio::Error error() const; + QAudio::State state() const; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); + +private: + Q_DISABLE_COPY(QAudioInput); + + QAbstractAudioInput* d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOINPUT_H diff --git a/src/multimedia/audio/qaudioinput_alsa_p.cpp b/src/multimedia/audio/qaudioinput_alsa_p.cpp new file mode 100644 index 0000000..6f00469 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_alsa_p.cpp @@ -0,0 +1,688 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qcoreapplication.h> +#include "qaudioinput_alsa_p.h" + +//#define DEBUG_AUDIO 1 + +QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + handle = 0; + ahandler = 0; + access = SND_PCM_ACCESS_RW_INTERLEAVED; + pcmformat = SND_PCM_FORMAT_S16; + buffer_size = 0; + period_size = 0; + buffer_time = 100000; + period_time = 20000; + totalTimeValue = 0; + intervalTime = 1000; + audioBuffer = 0; + errorState = QAudio::NoError; + deviceState = QAudio::StopState; + audioSource = 0; + pullMode = true; + resuming = false; + + QStringList list1 = QString(tr(device)).split(tr(":")); + m_device = QByteArray(list1.at(0).toLocal8Bit().constData()); + + timer = new QTimer(this); + connect(timer,SIGNAL(timeout()),SLOT(userFeed())); +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + close(); + disconnect(timer, SIGNAL(timeout())); + QCoreApplication::processEvents(); + delete timer; +} + +QAudio::Error QAudioInputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioInputPrivate::state() const +{ + return deviceState; +} + + +QAudioFormat QAudioInputPrivate::format() const +{ + return settings; +} + +int QAudioInputPrivate::xrun_recovery(int err) +{ + int count = 0; + bool reset = false; + + if(err == -EPIPE) { + errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + + } else if((err == -ESTRPIPE)||(err == -EIO)) { + errorState = QAudio::IOError; + while((err = snd_pcm_resume(handle)) == -EAGAIN){ + usleep(100); + count++; + if(count > 5) { + reset = true; + break; + } + } + if(err < 0) { + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + } + } + if(reset) { + close(); + open(); + snd_pcm_prepare(handle); + return 0; + } + return err; +} + +int QAudioInputPrivate::setFormat() +{ + snd_pcm_format_t format = SND_PCM_FORMAT_S16; + + if(settings.sampleSize() == 8) { + format = SND_PCM_FORMAT_U8; + } else if(settings.sampleSize() == 16) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_S16_LE; + else + format = SND_PCM_FORMAT_S16_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_U16_LE; + else + format = SND_PCM_FORMAT_U16_BE; + } + } else if(settings.sampleSize() == 24) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_S24_LE; + else + format = SND_PCM_FORMAT_S24_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_U24_LE; + else + format = SND_PCM_FORMAT_U24_BE; + } + } else if(settings.sampleSize() == 32) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_S32_LE; + else + format = SND_PCM_FORMAT_S32_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_U32_LE; + else + format = SND_PCM_FORMAT_U32_BE; + } else if(settings.sampleType() == QAudioFormat::Float) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_FLOAT_LE; + else + format = SND_PCM_FORMAT_FLOAT_BE; + } + } else if(settings.sampleSize() == 64) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_FLOAT64_LE; + else + format = SND_PCM_FORMAT_FLOAT64_BE; + } + + return snd_pcm_hw_params_set_format( handle, hwparams, format); +} + +QIODevice* QAudioInputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StopState) + close(); + + if(!pullMode && audioSource) { + delete audioSource; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + } else { + //set to push mode + pullMode = false; + audioSource = new InputPrivate(this); + audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + } + + if( !open() ) + return 0; + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioInputPrivate::stop() +{ + if(deviceState == QAudio::StopState) + return; + + deviceState = QAudio::StopState; + + close(); + emit stateChanged(deviceState); +} + +bool QAudioInputPrivate::open() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + timeStamp.restart(); + + int dir; + int err=-1; + int count=0; + unsigned int freakuency=settings.frequency(); + + QString dev = QString(tr(m_device.constData())); + if(!dev.contains(tr("default"))) { + dev = QString(tr("default:CARD=%1")).arg(tr(m_device.constData())); + } + + // Step 1: try and open the device + while((count < 5) && (err < 0)) { + err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); + if(err < 0) + count++; + } + if (( err < 0)||(handle == 0)) { + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + return false; + } + snd_pcm_nonblock( handle, 0 ); + + // Step 2: Set the desired HW parameters. + snd_pcm_hw_params_alloca( &hwparams ); + + bool fatal = false; + QString errMessage; + unsigned int chunks = 8; + + err = snd_pcm_hw_params_any( handle, hwparams ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_any: err = %1")).arg(err); + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_rate_resample: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_access( handle, hwparams, access ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_access: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = setFormat(); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_format: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_channels: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_rate_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_buffer_time_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_period_time_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params_set_periods_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params(handle, hwparams); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioInput: snd_pcm_hw_params: err = %1")).arg(err); + } + } + if( err < 0) { + qWarning()<<errMessage; + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + return false; + } + snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); + buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); + snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); + period_size = snd_pcm_frames_to_bytes(handle,period_frames); + snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); + snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); + + // Step 3: Set the desired SW parameters. + snd_pcm_sw_params_t *swparams; + snd_pcm_sw_params_alloca(&swparams); + snd_pcm_sw_params_current(handle, swparams); + snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); + snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); + snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); + snd_pcm_sw_params(handle, swparams); + + // Step 4: Prepare audio + if(audioBuffer == 0) + audioBuffer = new char[buffer_size]; + snd_pcm_prepare( handle ); + snd_pcm_start(handle); + + // Step 5: Setup timer + bytesAvailable = bytesReady(); + + if(pullMode) + connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed())); + + // Step 6: Start audio processing + chunks = buffer_size/period_size; + timer->start(period_time*chunks/2000); + + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + + totalTimeValue = 0; + + return true; +} + +void QAudioInputPrivate::close() +{ + deviceState = QAudio::StopState; + timer->stop(); + + if ( handle ) { + snd_pcm_drop( handle ); + snd_pcm_close( handle ); + handle = 0; + delete [] audioBuffer; + audioBuffer=0; + } +} + +int QAudioInputPrivate::bytesReady() const +{ + if(resuming) + return period_size; + + if(deviceState != QAudio::ActiveState) + return 0; + int frames = snd_pcm_avail_update(handle); + if((int)frames > (int)buffer_frames) + frames = buffer_frames; + + return snd_pcm_frames_to_bytes(handle, frames); +} + +qint64 QAudioInputPrivate::read(char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + // Read in some audio data and write it to QIODevice, pull mode + if ( !handle ) + return 0; + + bytesAvailable = bytesReady(); + + int count=0, err = 0; + while(count < 5) { + int chunks = bytesAvailable/period_size; + int frames = chunks*period_frames; + if(frames > (int)buffer_frames) + frames = buffer_frames; + int readFrames = snd_pcm_readi(handle, audioBuffer, frames); + if (readFrames >= 0) { + err = snd_pcm_frames_to_bytes(handle, readFrames); +#ifdef DEBUG_AUDIO + qDebug()<<QString(tr("PULL: read in bytes = %1 (frames=%2)")).arg(err).arg(readFrames).toLatin1().constData(); +#endif + break; + } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) { + errorState = QAudio::IOError; + err = 0; + break; + } else { + if(readFrames == -EPIPE) { + errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(handle); + } else if(readFrames == -ESTRPIPE) { + err = snd_pcm_prepare(handle); + } + if(err != 0) break; + } + count++; + } + if(err > 0) { + // got some send it onward +#ifdef DEBUG_AUDIO + qDebug()<<"PULL: frames to write to QIODevice = "<< + snd_pcm_bytes_to_frames( handle, (int)err )<<" ("<<err<<") bytes"; +#endif + if(deviceState != QAudio::ActiveState) + return 0; + + qint64 l = audioSource->write(audioBuffer,err); + if(l < 0) { + close(); + errorState = QAudio::IOError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } else if(l == 0) { + errorState = QAudio::NoError; + deviceState = QAudio::IdleState; + } else { + totalTimeValue += snd_pcm_bytes_to_frames(handle, err)*1000000/settings.frequency(); + resuming = false; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + } + return l; + } + return 0; +} + +void QAudioInputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + int err = 0; + + if(handle) { + err = snd_pcm_prepare( handle ); + if(err < 0) + xrun_recovery(err); + + err = snd_pcm_start(handle); + if(err < 0) + xrun_recovery(err); + + bytesAvailable = buffer_size; + } + resuming = true; + deviceState = QAudio::ActiveState; + int chunks = buffer_size/period_size; + timer->start(buffer_time*chunks/2000); + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::setBufferSize(int value) +{ + buffer_size = value; +} + +int QAudioInputPrivate::bufferSize() const +{ + return buffer_size; +} + +int QAudioInputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioInputPrivate::setNotifyInterval(int ms) +{ + intervalTime = ms; +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioInputPrivate::totalTime() const +{ + return totalTimeValue; +} + +void QAudioInputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState||resuming) { + timer->stop(); + deviceState = QAudio::SuspendState; + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::userFeed() +{ + if(deviceState == QAudio::StopState || deviceState == QAudio::SuspendState) + return; +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() IN"; +#endif + deviceReady(); +} + +bool QAudioInputPrivate::deviceReady() +{ + if(pullMode) { + // reads some audio data and writes it to QIODevice + read(0,0); + } else { + // emits readyRead() so user will call read() on QIODevice to get some audio data + InputPrivate* a = qobject_cast<InputPrivate*>(audioSource); + a->trigger(); + } + bytesAvailable = bytesReady(); + + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + return true; +} + +qint64 QAudioInputPrivate::clock() const +{ + if(!handle) + return 0; + + if(deviceState != QAudio::ActiveState) + return 0; + + snd_pcm_status_t* status; + snd_pcm_status_alloca(&status); + + snd_timestamp_t t1,t2; + if( snd_pcm_status(handle, status) >= 0) { + snd_pcm_status_get_tstamp(status,&t1); + snd_pcm_status_get_trigger_tstamp(status,&t2); + t1.tv_sec-=t2.tv_sec; + + signed long l = (signed long)t1.tv_usec - (signed long)t2.tv_usec; + if(l < 0) { + t1.tv_sec--; + l = -l; + l %= 1000000; + } + return ((t1.tv_sec * 1000)+l/1000); + } else + return 0; +} + +void QAudioInputPrivate::reset() +{ + if(handle) + snd_pcm_reset(handle); +} + +void QAudioInputPrivate::drain() +{ + if(handle) + snd_pcm_drain(handle); +} + +InputPrivate::InputPrivate(QAudioInputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioInputPrivate*>(audio); +} + +InputPrivate::~InputPrivate() +{ +} + +qint64 InputPrivate::readData( char* data, qint64 len) +{ + // push mode, user read() called + if((audioDevice->state() != QAudio::ActiveState) && !audioDevice->resuming) + return 0; + + int readFrames; + int count=0, err = 0; + + while(count < 5) { + int frames = snd_pcm_bytes_to_frames(audioDevice->handle, len); + readFrames = snd_pcm_readi(audioDevice->handle, data, frames); + if (readFrames >= 0) { + err = snd_pcm_frames_to_bytes(audioDevice->handle, readFrames); +#ifdef DEBUG_AUDIO + qDebug()<<QString(tr("PUSH: read in bytes = %1 (frames=%2)")).arg(err).arg(readFrames).toLatin1().constData(); +#endif + break; + } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) { + audioDevice->errorState = QAudio::IOError; + err = 0; + break; + } else { + if(readFrames == -EPIPE) { + audioDevice->errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(audioDevice->handle); + } else if(readFrames == -ESTRPIPE) { + err = snd_pcm_prepare(audioDevice->handle); + } + if(err != 0) break; + } + count++; + } + if(err > 0 && readFrames > 0) { + audioDevice->totalTimeValue += readFrames*1000/audioDevice->settings.frequency()*1000; + audioDevice->deviceState = QAudio::ActiveState; + return err; + } + return 0; +} + +qint64 InputPrivate::writeData(const char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + return 0; +} + +void InputPrivate::trigger() +{ + emit readyRead(); +} + diff --git a/src/multimedia/audio/qaudioinput_alsa_p.h b/src/multimedia/audio/qaudioinput_alsa_p.h new file mode 100644 index 0000000..21c8064 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_alsa_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIOINPUTALSA_H +#define QAUDIOINPUTALSA_H + +#include <alsa/asoundlib.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + +class InputPrivate; + +class QAudioInputPrivate : public QAbstractAudioInput +{ + Q_OBJECT +public: + QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioInputPrivate(); + + qint64 read(char* data, qint64 len); + + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesReady() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 totalTime() const; + qint64 clock() const; + QAudio::Error error() const; + QAudio::State state() const; + QAudioFormat format() const; + bool resuming; + snd_pcm_t* handle; + qint64 totalTimeValue; + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private slots: + void userFeed(); + bool deviceReady(); + +private: + int xrun_recovery(int err); + int setFormat(); + bool open(); + void close(); + void drain(); + + QTimer* timer; + QTime timeStamp; + int intervalTime; + char* audioBuffer; + int bytesAvailable; + QByteArray m_device; + bool pullMode; + int buffer_size; + int period_size; + unsigned int buffer_time; + unsigned int period_time; + snd_pcm_uframes_t buffer_frames; + snd_pcm_uframes_t period_frames; + snd_async_handler_t* ahandler; + snd_pcm_access_t access; + snd_pcm_format_t pcmformat; + snd_timestamp_t* timestamp; + snd_pcm_hw_params_t *hwparams; +}; + +class InputPrivate : public QIODevice +{ + Q_OBJECT +public: + InputPrivate(QAudioInputPrivate* audio); + ~InputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + + void trigger(); +private: + QAudioInputPrivate *audioDevice; +}; + +#endif diff --git a/src/multimedia/audio/qaudioinput_mac_p.cpp b/src/multimedia/audio/qaudioinput_mac_p.cpp new file mode 100644 index 0000000..5400c85 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_mac_p.cpp @@ -0,0 +1,930 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qendian.h> +#include <QtCore/qtimer.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioinput.h> + +#include "qaudio_mac_p.h" +#include "qaudioinput_mac_p.h" + + +QT_BEGIN_NAMESPACE + + +namespace +{ + +static const int default_buffer_size = 4 * 1024; + +class QAudioBufferList +{ +public: + QAudioBufferList(AudioStreamBasicDescription const& streamFormat): + owner(false), + sf(streamFormat) + { + const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; + const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame; + + dataSize = 0; + + bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) + + (sizeof(AudioBuffer) * numberOfBuffers))); + + bfs->mNumberBuffers = numberOfBuffers; + for (int i = 0; i < numberOfBuffers; ++i) { + bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1; + bfs->mBuffers[i].mDataByteSize = 0; + bfs->mBuffers[i].mData = 0; + } + } + + QAudioBufferList(AudioStreamBasicDescription const& streamFormat, char* buffer, int bufferSize): + owner(false), + sf(streamFormat), + bfs(0) + { + dataSize = bufferSize; + + bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) + sizeof(AudioBuffer))); + + bfs->mNumberBuffers = 1; + bfs->mBuffers[0].mNumberChannels = 1; + bfs->mBuffers[0].mDataByteSize = dataSize; + bfs->mBuffers[0].mData = buffer; + } + + QAudioBufferList(AudioStreamBasicDescription const& streamFormat, int framesToBuffer): + owner(true), + sf(streamFormat), + bfs(0) + { + const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; + const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame; + + dataSize = framesToBuffer * sf.mBytesPerFrame; + + bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) + + (sizeof(AudioBuffer) * numberOfBuffers))); + bfs->mNumberBuffers = numberOfBuffers; + for (int i = 0; i < numberOfBuffers; ++i) { + bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1; + bfs->mBuffers[i].mDataByteSize = dataSize; + bfs->mBuffers[i].mData = qMalloc(dataSize); + } + } + + ~QAudioBufferList() + { + if (owner) { + for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i) + qFree(bfs->mBuffers[i].mData); + } + + qFree(bfs); + } + + AudioBufferList* audioBufferList() const + { + return bfs; + } + + char* data(int buffer = 0) const + { + return static_cast<char*>(bfs->mBuffers[buffer].mData); + } + + qint64 bufferSize(int buffer = 0) const + { + return bfs->mBuffers[buffer].mDataByteSize; + } + + int frameCount(int buffer = 0) const + { + return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerFrame; + } + + int packetCount(int buffer = 0) const + { + return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerPacket; + } + + int packetSize() const + { + return sf.mBytesPerPacket; + } + + void reset() + { + for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i) + bfs->mBuffers[i].mDataByteSize = dataSize; + } + +private: + bool owner; + int dataSize; + AudioStreamBasicDescription sf; + AudioBufferList* bfs; +}; + +class QAudioPacketFeeder +{ +public: + QAudioPacketFeeder(QAudioBufferList* abl): + audioBufferList(abl) + { + totalPackets = audioBufferList->packetCount(); + position = 0; + } + + bool feed(AudioBufferList& dst, UInt32& packetCount) + { + if (position == totalPackets) { + dst.mBuffers[0].mDataByteSize = 0; + packetCount = 0; + return false; + } + + if (totalPackets - position < packetCount) + packetCount = totalPackets - position; + + dst.mBuffers[0].mDataByteSize = packetCount * audioBufferList->packetSize(); + dst.mBuffers[0].mData = audioBufferList->data() + (position * audioBufferList->packetSize()); + + position += packetCount; + + return true; + } + +private: + UInt32 totalPackets; + UInt32 position; + QAudioBufferList* audioBufferList; +}; + +class QAudioInputBuffer : public QObject +{ + Q_OBJECT + +public: + QAudioInputBuffer(int bufferSize, + int maxPeriodSize, + AudioStreamBasicDescription const& inputFormat, + AudioStreamBasicDescription const& outputFormat, + QObject* parent): + QObject(parent), + m_deviceError(false), + m_inputFormat(inputFormat), + m_outputFormat(outputFormat) + { + m_maxPeriodSize = maxPeriodSize; + m_periodTime = m_maxPeriodSize / m_outputFormat.mBytesPerFrame * 1000 / m_outputFormat.mSampleRate; + m_buffer = new QAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize))); + m_inputBufferList = new QAudioBufferList(m_inputFormat); + + m_flushTimer = new QTimer(this); + connect(m_flushTimer, SIGNAL(timeout()), SLOT(flushBuffer())); + + if (inputFormat.mSampleRate != outputFormat.mSampleRate) { + if (AudioConverterNew(&m_inputFormat, &m_outputFormat, &m_audioConverter) != noErr) { + qWarning() << "QAudioInput: Unable to create an Audio Converter"; + m_audioConverter = 0; + } + } + } + + ~QAudioInputBuffer() + { + delete m_buffer; + } + + qint64 renderFromDevice(AudioUnit audioUnit, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames) + { + const bool wasEmpty = m_buffer->used() == 0; + + OSStatus err; + qint64 framesRendered = 0; + + m_inputBufferList->reset(); + err = AudioUnitRender(audioUnit, + ioActionFlags, + inTimeStamp, + inBusNumber, + inNumberFrames, + m_inputBufferList->audioBufferList()); + + if (m_audioConverter != 0) { + QAudioPacketFeeder feeder(m_inputBufferList); + + bool wecan = true; + int copied = 0; + + const int available = m_buffer->free(); + + while (err == noErr && wecan) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available); + + if (region.second > 0) { + AudioBufferList output; + output.mNumberBuffers = 1; + output.mBuffers[0].mNumberChannels = 1; + output.mBuffers[0].mDataByteSize = region.second; + output.mBuffers[0].mData = region.first; + + UInt32 packetSize = region.second / m_outputFormat.mBytesPerPacket; + err = AudioConverterFillComplexBuffer(m_audioConverter, + converterCallback, + &feeder, + &packetSize, + &output, + 0); + + region.second = output.mBuffers[0].mDataByteSize; + copied += region.second; + + m_buffer->releaseWriteRegion(region); + } + else + wecan = false; + } + + framesRendered += copied / m_outputFormat.mBytesPerFrame; + } + else { + const int available = m_inputBufferList->bufferSize(); + bool wecan = true; + int copied = 0; + + while (wecan && copied < available) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied); + + if (region.second > 0) { + memcpy(region.first, m_inputBufferList->data() + copied, region.second); + copied += region.second; + } + else + wecan = false; + + m_buffer->releaseWriteRegion(region); + } + + framesRendered = copied / m_outputFormat.mBytesPerFrame; + } + + if (wasEmpty && framesRendered > 0) + emit readyRead(); + + return framesRendered; + } + + qint64 readBytes(char* data, qint64 len) + { + bool wecan = true; + qint64 bytesCopied = 0; + + len -= len % m_maxPeriodSize; + while (wecan && bytesCopied < len) { + QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(len - bytesCopied); + + if (region.second > 0) { + memcpy(data + bytesCopied, region.first, region.second); + bytesCopied += region.second; + } + else + wecan = false; + + m_buffer->releaseReadRegion(region); + } + + return bytesCopied; + } + + void setFlushDevice(QIODevice* device) + { + if (m_device != device) + m_device = device; + } + + void startFlushTimer() + { + if (m_device != 0) { + m_flushTimer->start((m_buffer->size() - (m_maxPeriodSize * 2)) / m_maxPeriodSize * m_periodTime); + } + } + + void stopFlushTimer() + { + m_flushTimer->stop(); + } + + void flush(bool all = false) + { + const int used = m_buffer->used(); + const int readSize = all ? used : used - (used % m_maxPeriodSize); + + if (readSize > 0) { + bool wecan = true; + int flushed = 0; + + while (!m_deviceError && wecan && flushed < readSize) { + QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(readSize - flushed); + + if (region.second > 0) { + int bytesWritten = m_device->write(region.first, region.second); + if (bytesWritten < 0) { + stopFlushTimer(); + m_deviceError = true; + } + else { + region.second = bytesWritten; + flushed += bytesWritten; + wecan = bytesWritten != 0; + } + } + else + wecan = false; + + m_buffer->releaseReadRegion(region); + } + } + } + + void reset() + { + m_buffer->reset(); + m_deviceError = false; + } + + int available() const + { + return m_buffer->free(); + } + + int used() const + { + return m_buffer->used(); + } + +signals: + void readyRead(); + +private slots: + void flushBuffer() + { + flush(); + } + +private: + bool m_deviceError; + int m_maxPeriodSize; + int m_periodTime; + QIODevice* m_device; + QTimer* m_flushTimer; + QAudioRingBuffer* m_buffer; + QAudioBufferList* m_inputBufferList; + AudioConverterRef m_audioConverter; + AudioStreamBasicDescription m_inputFormat; + AudioStreamBasicDescription m_outputFormat; + + const static OSStatus as_empty = 'qtem'; + + // Converter callback + static OSStatus converterCallback(AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData) + { + Q_UNUSED(inAudioConverter); + Q_UNUSED(outDataPacketDescription); + + QAudioPacketFeeder* feeder = static_cast<QAudioPacketFeeder*>(inUserData); + + if (!feeder->feed(*ioData, *ioNumberDataPackets)) + return as_empty; + + return noErr; + } +}; + + +class MacInputDevice : public QIODevice +{ + Q_OBJECT + +public: + MacInputDevice(QAudioInputBuffer* audioBuffer, QObject* parent): + QIODevice(parent), + m_audioBuffer(audioBuffer) + { + open(QIODevice::ReadOnly | QIODevice::Unbuffered); + connect(m_audioBuffer, SIGNAL(readyRead()), SIGNAL(readyRead())); + } + + qint64 readData(char* data, qint64 len) + { + return m_audioBuffer->readBytes(data, len); + } + + qint64 writeData(const char* data, qint64 len) + { + Q_UNUSED(data); + Q_UNUSED(len); + + return 0; + } + + bool isSequential() const + { + return true; + } + +private: + QAudioInputBuffer* m_audioBuffer; +}; + +} + + +QAudioInputPrivate::QAudioInputPrivate(const QByteArray& device, QAudioFormat const& format): + audioFormat(format) +{ + QDataStream ds(device); + quint32 did, mode; + + ds >> did >> mode; + + if (QAudio::Mode(mode) == QAudio::AudioOutput) + errorCode = QAudio::OpenError; + else { + isOpen = false; + audioDeviceId = AudioDeviceID(did); + audioUnit = 0; + startTime = 0; + totalFrames = 0; + audioBuffer = 0; + internalBufferSize = default_buffer_size; + clockFrequency = AudioGetHostClockFrequency() / 1000; + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + + intervalTimer = new QTimer(this); + intervalTimer->setInterval(1000); + connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify())); + } +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + close(); +} + +bool QAudioInputPrivate::open() +{ + UInt32 size = 0; + + if (isOpen) + return true; + + ComponentDescription cd; + cd.componentType = kAudioUnitType_Output; + cd.componentSubType = kAudioUnitSubType_HALOutput; + cd.componentManufacturer = kAudioUnitManufacturer_Apple; + cd.componentFlags = 0; + cd.componentFlagsMask = 0; + + // Open + Component cp = FindNextComponent(NULL, &cd); + if (cp == 0) { + qWarning() << "QAudioInput: Failed to find HAL Output component"; + return false; + } + + if (OpenAComponent(cp, &audioUnit) != noErr) { + qWarning() << "QAudioInput: Unable to Open Output Component"; + return false; + } + + // Set mode + // switch to input mode + UInt32 enable = 1; + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Input, + 1, + &enable, + sizeof(enable)) != noErr) { + qWarning() << "QAudioInput: Unabled to switch to input mode (Enable Input)"; + return false; + } + + enable = 0; + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Output, + 0, + &enable, + sizeof(enable)) != noErr) { + qWarning() << "QAudioInput: Unabled to switch to input mode (Disable output)"; + return false; + } + + // register callback + AURenderCallbackStruct cb; + cb.inputProc = inputCallback; + cb.inputProcRefCon = this; + + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Global, + 0, + &cb, + sizeof(cb)) != noErr) { + qWarning() << "QAudioInput: Failed to set AudioUnit callback"; + return false; + } + + // Set Audio Device + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 0, + &audioDeviceId, + sizeof(audioDeviceId)) != noErr) { + qWarning() << "QAudioInput: Unable to use configured device"; + return false; + } + + // Set format + streamFormat = toAudioStreamBasicDescription(audioFormat); + + size = sizeof(deviceFormat); + if (AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 1, + &deviceFormat, + &size) != noErr) { + qWarning() << "QAudioInput: Unable to retrieve device format"; + return false; + } + + // If the device frequency is different to the requested use a converter + if (deviceFormat.mSampleRate != streamFormat.mSampleRate) { + AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 1, + &deviceFormat, + sizeof(streamFormat)); + } + else { + AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 1, + &streamFormat, + sizeof(streamFormat)); + } + + // Setup buffers + UInt32 numberOfFrames; + size = sizeof(UInt32); + if (AudioUnitGetProperty(audioUnit, + kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Global, + 0, + &numberOfFrames, + &size) != noErr) { + qWarning() << "QAudioInput: Failed to get audio period size"; + return false; + } + + // Allocate buffer + periodSizeBytes = (numberOfFrames * streamFormat.mSampleRate / deviceFormat.mSampleRate) * + streamFormat.mBytesPerFrame; + if (internalBufferSize < periodSizeBytes * 2) + internalBufferSize = periodSizeBytes * 2; + else + internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame; + + audioBuffer = new QAudioInputBuffer(internalBufferSize, + periodSizeBytes, + deviceFormat, + streamFormat, + this); + + audioIO = new MacInputDevice(audioBuffer, this); + + // Init + if (AudioUnitInitialize(audioUnit) != noErr) { + qWarning() << "QAudioInput: Failed to initialize AudioUnit"; + return false; + } + + isOpen = true; + + return isOpen; +} + +void QAudioInputPrivate::close() +{ + if (audioUnit != 0) { + AudioOutputUnitStop(audioUnit); + AudioUnitUninitialize(audioUnit); + CloseComponent(audioUnit); + } + + delete audioBuffer; +} + +QAudioFormat QAudioInputPrivate::format() const +{ + return audioFormat; +} + +QIODevice* QAudioInputPrivate::start(QIODevice* device) +{ + QIODevice* op = device; + + if (!open()) { + stateCode = QAudio::StopState; + errorCode = QAudio::OpenError; + return audioIO; + } + + reset(); + audioBuffer->reset(); + audioBuffer->setFlushDevice(op); + + if (op == 0) + op = audioIO; + + // Start + startTime = AudioGetCurrentHostTime(); + totalFrames = 0; + + audioThreadStart(); + + return op; +} + +void QAudioInputPrivate::stop() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadStop(); + audioBuffer->flush(true); + + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioInputPrivate::reset() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadStop(); + + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioInputPrivate::suspend() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) { + audioThreadStop(); + + errorCode = QAudio::NoError; + stateCode = QAudio::SuspendState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioInputPrivate::resume() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::SuspendState) { + audioThreadStart(); + + errorCode = QAudio::NoError; + stateCode = QAudio::ActiveState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +int QAudioInputPrivate::bytesReady() const +{ + return audioBuffer->used(); +} + +int QAudioInputPrivate::periodSize() const +{ + return periodSizeBytes; +} + +void QAudioInputPrivate::setBufferSize(int bs) +{ + internalBufferSize = bs; +} + +int QAudioInputPrivate::bufferSize() const +{ + return internalBufferSize; +} + +void QAudioInputPrivate::setNotifyInterval(int milliSeconds) +{ + intervalTimer->setInterval(milliSeconds); +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTimer->interval(); +} + +qint64 QAudioInputPrivate::totalTime() const +{ + return totalFrames * 1000000 / audioFormat.frequency(); +} + +qint64 QAudioInputPrivate::clock() const +{ + return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000); +} + +QAudio::Error QAudioInputPrivate::error() const +{ + return errorCode; +} + +QAudio::State QAudioInputPrivate::state() const +{ + return stateCode; +} + +void QAudioInputPrivate::audioThreadStop() +{ + stopTimers(); + if (audioThreadState.testAndSetAcquire(Running, Stopped)) + threadFinished.wait(&mutex); +} + +void QAudioInputPrivate::audioThreadStart() +{ + startTimers(); + audioThreadState = Running; + AudioOutputUnitStart(audioUnit); +} + +void QAudioInputPrivate::audioDeviceStop() +{ + AudioOutputUnitStop(audioUnit); + audioThreadState = Stopped; + threadFinished.wakeOne(); +} + +void QAudioInputPrivate::audioDeviceFull() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::UnderrunError; + stateCode = QAudio::IdleState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioInputPrivate::audioDeviceError() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::IOError; + stateCode = QAudio::StopState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioInputPrivate::startTimers() +{ + audioBuffer->startFlushTimer(); + intervalTimer->start(); +} + +void QAudioInputPrivate::stopTimers() +{ + audioBuffer->stopFlushTimer(); + intervalTimer->stop(); +} + +void QAudioInputPrivate::deviceStopped() +{ + stopTimers(); + emit stateChanged(stateCode); +} + +// Input callback +OSStatus QAudioInputPrivate::inputCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) +{ + Q_UNUSED(ioData); + + QAudioInputPrivate* d = static_cast<QAudioInputPrivate*>(inRefCon); + + const int threadState = d->audioThreadState.fetchAndAddAcquire(0); + if (threadState == Stopped) + d->audioDeviceStop(); + else { + qint64 framesWritten; + + framesWritten = d->audioBuffer->renderFromDevice(d->audioUnit, + ioActionFlags, + inTimeStamp, + inBusNumber, + inNumberFrames); + + if (framesWritten > 0) + d->totalFrames += framesWritten; + else if (framesWritten == 0) + d->audioDeviceFull(); + else if (framesWritten < 0) + d->audioDeviceError(); + } + + return noErr; +} + + +QT_END_NAMESPACE + +#include "qaudioinput_mac_p.moc" + diff --git a/src/multimedia/audio/qaudioinput_mac_p.h b/src/multimedia/audio/qaudioinput_mac_p.h new file mode 100644 index 0000000..98ef9ce --- /dev/null +++ b/src/multimedia/audio/qaudioinput_mac_p.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIOINPUT_MAC_P_H +#define QAUDIOINPUT_MAC_P_H + +#include <CoreServices/CoreServices.h> +#include <CoreAudio/CoreAudio.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <QtCore/qobject.h> +#include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> +#include <QtCore/qatomic.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QTimer; +class QIODevice; + +namespace +{ +class QAudioInputBuffer; +} + +class QAudioInputPrivate : public QAbstractAudioInput +{ + Q_OBJECT + +public: + bool isOpen; + int periodSizeBytes; + int internalBufferSize; + qint64 totalFrames; + QAudioFormat audioFormat; + QIODevice* audioIO; + AudioUnit audioUnit; + AudioDeviceID audioDeviceId; + Float64 clockFrequency; + UInt64 startTime; + QAudio::Error errorCode; + QAudio::State stateCode; + QAudioInputBuffer* audioBuffer; + QMutex mutex; + QWaitCondition threadFinished; + QAtomicInt audioThreadState; + QTimer* intervalTimer; + AudioStreamBasicDescription streamFormat; + AudioStreamBasicDescription deviceFormat; + + QAudioInputPrivate(const QByteArray& device, QAudioFormat const& format); + ~QAudioInputPrivate(); + + bool open(); + void close(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice* device); + void stop(); + void reset(); + void suspend(); + void resume(); + void idle(); + + int bytesReady() const; + int periodSize() const; + + void setBufferSize(int value); + int bufferSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 totalTime() const; + qint64 clock() const; + + QAudio::Error error() const; + QAudio::State state() const; + + void audioThreadStart(); + void audioThreadStop(); + + void audioDeviceStop(); + void audioDeviceFull(); + void audioDeviceError(); + + void startTimers(); + void stopTimers(); + +signals: + void stateChanged(QAudio::State); + void notify(); + +private slots: + void deviceStopped(); + +private: + enum { Running, Stopped }; + + // Input callback + static OSStatus inputCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOINPUT_MAC_P_H diff --git a/src/multimedia/audio/qaudioinput_win32_p.cpp b/src/multimedia/audio/qaudioinput_win32_p.cpp new file mode 100644 index 0000000..e5b6e0d --- /dev/null +++ b/src/multimedia/audio/qaudioinput_win32_p.cpp @@ -0,0 +1,540 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include "qaudioinput_win32_p.h" + +//#define DEBUG_AUDIO 1 + + +QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + buffer_size = 0; + period_size = 0; + m_device = device; + totalTimeValue = 0; + intervalTime = 1000; + errorState = QAudio::NoError; + deviceState = QAudio::StopState; + audioSource = 0; + pullMode = true; + resuming = false; + + connect(this,SIGNAL(processMore()),SLOT(deviceReady())); + + InitializeCriticalSection(&waveInCriticalSection); +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + close(); + DeleteCriticalSection(&waveInCriticalSection); +} + +void CALLBACK QAudioInputPrivate::waveInProc( HWAVEIN hWaveIn, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) +{ + Q_UNUSED(dwParam1) + Q_UNUSED(dwParam2) + Q_UNUSED(hWaveIn) + + QAudioInputPrivate* qAudio; + qAudio = (QAudioInputPrivate*)(dwInstance); + if(!qAudio) + return; + + switch(uMsg) { + case WIM_OPEN: + break; + case WIM_DATA: + EnterCriticalSection(&waveInCriticalSection); + if(qAudio->waveFreeBlockCount > 0) + qAudio->waveFreeBlockCount--; + LeaveCriticalSection(&waveInCriticalSection); + qAudio->feedback(); + break; + case WIM_CLOSE: + break; + default: + return; + } +} + +WAVEHDR* QAudioInputPrivate::allocateBlocks(int size, int count) +{ + int i; + unsigned char* buffer; + WAVEHDR* blocks; + DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count; + + if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, + totalBufferSize)) == 0) { + qWarning("QAudioInput: Memory allocation error"); + return 0; + } + blocks = (WAVEHDR*)buffer; + buffer += sizeof(WAVEHDR)*count; + for(i = 0; i < count; i++) { + blocks[i].dwBufferLength = size; + blocks[i].lpData = (LPSTR)buffer; + blocks[i].dwBytesRecorded=0; + blocks[i].dwUser = 0L; + blocks[i].dwFlags = 0L; + blocks[i].dwLoops = 0L; + result = waveInPrepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: Can't prepare block %d",i); + return 0; + } + buffer += size; + } + return blocks; +} + +void QAudioInputPrivate::freeBlocks(WAVEHDR* blockArray) +{ + HeapFree(GetProcessHeap(), 0, blockArray); +} + +QAudio::Error QAudioInputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioInputPrivate::state() const +{ + return deviceState; +} + +QAudioFormat QAudioInputPrivate::format() const +{ + return settings; +} + +QIODevice* QAudioInputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StopState) + close(); + + if(!pullMode && audioSource) { + delete audioSource; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + } else { + //set to push mode + pullMode = false; + audioSource = new InputPrivate(this); + audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + } + + if( !open() ) + return 0; + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioInputPrivate::stop() +{ + if(deviceState == QAudio::StopState) + return; + + deviceState = QAudio::StopState; + + close(); + emit stateChanged(deviceState); +} + +bool QAudioInputPrivate::open() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + header = 0; + if(buffer_size == 0) { + // Default buffer size, 100ms, default period size is 20ms + buffer_size = settings.frequency()*settings.channels()*(settings.sampleSize()/8)*0.1; + period_size = buffer_size/5; + } else { + period_size = buffer_size/5; + } + timeStamp.restart(); + wfx.nSamplesPerSec = settings.frequency(); + wfx.wBitsPerSample = settings.sampleSize(); + wfx.nChannels = settings.channels(); + wfx.cbSize = 0; + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; + + UINT_PTR devId = WAVE_MAPPER; + + WAVEINCAPS wic; + unsigned long iNumDevs,ii; + iNumDevs = waveInGetNumDevs(); + for(ii=0;ii<iNumDevs;ii++) { + if(waveInGetDevCaps(ii, &wic, sizeof(WAVEINCAPS)) + == MMSYSERR_NOERROR) { + QString tmp; + tmp = QString::fromUtf16((const unsigned short*)wic.szPname); + if(tmp.compare(tr(m_device)) == 0) { + devId = ii; + break; + } + } + } + + if(waveInOpen(&hWaveIn, devId, &wfx, + (DWORD_PTR)&waveInProc, + (DWORD_PTR) this, + CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + qWarning("QAudioInput: failed to open audio device"); + return false; + } + waveBlocks = allocateBlocks(period_size, buffer_size/period_size); + waveFreeBlockCount = buffer_size/period_size; + waveCurrentBlock = 0; + + for(int i=0; i<buffer_size/period_size; i++) { + result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to setup block %d,err=%d",i,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + return false; + } + } + result = waveInStart(hWaveIn); + if(result) { + qWarning("QAudioInput: failed to start audio input"); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + return false; + } + timeStampOpened.restart(); + totalTimeValue = 0; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + return true; +} + +void QAudioInputPrivate::close() +{ + deviceState = QAudio::StopState; + int delay = (buffer_size-bytesReady())*1000/(settings.frequency() + *settings.channels()*(settings.sampleSize()/8)); + waveInReset(hWaveIn); + Sleep(delay+10); + + for(int i=0; i<waveFreeBlockCount; i++) { + if(waveBlocks[i].dwFlags & WHDR_PREPARED) + waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR)); + } + freeBlocks(waveBlocks); + waveInClose(hWaveIn); +} + +int QAudioInputPrivate::bytesReady() const +{ + int buf = ((buffer_size/period_size)-waveFreeBlockCount)*period_size; + if(buf < 0) + buf = 0; + return buf; +} + +qint64 QAudioInputPrivate::read(char* data, qint64 len) +{ + bool done = false; + + char* p = data; + qint64 l = 0; + qint64 written = 0; + while(!done) { + // Read in some audio data + if(waveBlocks[header].dwBytesRecorded > 0) { + if(pullMode) { + l = audioSource->write(waveBlocks[header].lpData, + waveBlocks[header].dwBytesRecorded); +#ifdef DEBUG_AUDIO + qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l; +#endif + if(l < 0) { + // error + qWarning("QAudioInput: IOError"); + errorState = QAudio::IOError; + + } else if(l == 0) { + // cant write to IODevice + qWarning("QAudioInput: IOError, can't write to QIODevice"); + errorState = QAudio::IOError; + + } else { + totalTimeValue += waveBlocks[header].dwBytesRecorded + /((settings.channels()*settings.sampleSize()/8)) + *10000/settings.frequency()*100; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + resuming = false; + } + } else { + // push mode + memcpy(p,waveBlocks[header].lpData,waveBlocks[header].dwBytesRecorded); + l = waveBlocks[header].dwBytesRecorded; +#ifdef DEBUG_AUDIO + qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l; +#endif + totalTimeValue += waveBlocks[header].dwBytesRecorded + /((settings.channels()*settings.sampleSize()/8)) + *10000/settings.frequency()*100; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + resuming = false; + } + } else { + //no data, not ready yet, next time + return 0; + } + EnterCriticalSection(&waveInCriticalSection); + waveFreeBlockCount++; + LeaveCriticalSection(&waveInCriticalSection); + waveBlocks[header].dwBytesRecorded=0; + waveBlocks[header].dwFlags = 0L; + result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to prepare block %d,err=%d",header,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } + result = waveInAddBuffer(hWaveIn, &waveBlocks[header], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to setup block %d,err=%d",header,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } + header++; + if(header >= buffer_size/period_size) + header = 0; + p+=l; + + if(!pullMode) { + if(l+period_size > len && waveFreeBlockCount == buffer_size/period_size) + done = true; + } else { + if(waveFreeBlockCount == buffer_size/period_size) + done = true; + } + written+=l; + } +#ifdef DEBUG_AUDIO + qDebug()<<"read in len="<<written; +#endif + return written; +} + +void QAudioInputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + deviceState = QAudio::ActiveState; + for(int i=0; i<buffer_size/period_size; i++) { + result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to setup block %d,err=%d",i,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + return; + } + } + waveFreeBlockCount = buffer_size/period_size; + waveCurrentBlock = 0; + header = 0; + resuming = true; + waveInStart(hWaveIn); + QTimer::singleShot(20,this,SLOT(feedback())); + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::setBufferSize(int value) +{ + buffer_size = value; +} + +int QAudioInputPrivate::bufferSize() const +{ + return buffer_size; +} + +int QAudioInputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioInputPrivate::setNotifyInterval(int ms) +{ + intervalTime = ms; +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioInputPrivate::totalTime() const +{ + return totalTimeValue; +} + +void QAudioInputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState) { + waveInReset(hWaveIn); + deviceState = QAudio::SuspendState; + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::feedback() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback() INPUT"; +#endif + bytesAvailable = bytesReady(); + + if(!(deviceState==QAudio::StopState||deviceState==QAudio::SuspendState)) + emit processMore(); +} + +bool QAudioInputPrivate::deviceReady() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT"; +#endif + if(pullMode) { + // reads some audio data and writes it to QIODevice + read(0,0); + } else { + // emits readyRead() so user will call read() on QIODevice to get some audio data + InputPrivate* a = qobject_cast<InputPrivate*>(audioSource); + a->trigger(); + } + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + return true; +} + +qint64 QAudioInputPrivate::clock() const +{ + if(deviceState != QAudio::ActiveState) + return 0; + + return timeStampOpened.elapsed(); +} + +void QAudioInputPrivate::reset() +{ + close(); +} + +InputPrivate::InputPrivate(QAudioInputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioInputPrivate*>(audio); +} + +InputPrivate::~InputPrivate() {} + +qint64 InputPrivate::readData( char* data, qint64 len) +{ + // push mode, user read() called + if(audioDevice->deviceState != QAudio::ActiveState) + return 0; + // Read in some audio data + return audioDevice->read(data,len); +} + +qint64 InputPrivate::writeData(const char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + + emit readyRead(); + return 0; +} + +void InputPrivate::trigger() +{ + emit readyRead(); +} + diff --git a/src/multimedia/audio/qaudioinput_win32_p.h b/src/multimedia/audio/qaudioinput_win32_p.h new file mode 100644 index 0000000..32464f0 --- /dev/null +++ b/src/multimedia/audio/qaudioinput_win32_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOINPUTWIN_H +#define QAUDIOINPUTWIN_H + +#include <windows.h> +#include <mmsystem.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +static CRITICAL_SECTION waveInCriticalSection; + +class QAudioInputPrivate : public QAbstractAudioInput +{ + Q_OBJECT +public: + QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioInputPrivate(); + + qint64 read(char* data, qint64 len); + + QAudioFormat format() const; + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesReady() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 totalTime() const; + qint64 clock() const; + QAudio::Error error() const; + QAudio::State state() const; + + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private: + qint32 buffer_size; + qint32 period_size; + qint32 header; + QByteArray m_device; + int bytesAvailable; + int intervalTime; + QTime timeStamp; + QTime timeStampOpened; + qint64 totalTimeValue; + bool pullMode; + bool resuming; + WAVEFORMATEX wfx; + HWAVEIN hWaveIn; + MMRESULT result; + WAVEHDR* waveBlocks; + volatile int waveFreeBlockCount; + int waveCurrentBlock; + + static void CALLBACK waveInProc( HWAVEIN hWaveIn, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ); + + WAVEHDR* allocateBlocks(int size, int count); + void freeBlocks(WAVEHDR* blockArray); + bool open(); + void close(); + +private slots: + void feedback(); + bool deviceReady(); + +signals: + void processMore(); +}; + +class InputPrivate : public QIODevice +{ + Q_OBJECT +public: + InputPrivate(QAudioInputPrivate* audio); + ~InputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + + void trigger(); +private: + QAudioInputPrivate *audioDevice; +}; + +#endif diff --git a/src/multimedia/audio/qaudiooutput.cpp b/src/multimedia/audio/qaudiooutput.cpp new file mode 100644 index 0000000..785da61 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudiooutput.h> + +#include "qaudiodevicefactory_p.h" + + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioOutput + \brief The QAudioOutput class provides an interface for sending audio data to an audio output device. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + You can construct an audio output with the system's + \l{QAudioDeviceInfo::defaultOutputDevice()}{default audio output + device}. It is also possible to create QAudioOutput with a + specific QAudioDeviceId. When you create the audio output, you + should also send in the QAudioFormat to be used for the playback + (see the QAudioFormat class description for details). + + To play a file: + + Starting to play an audio stream is simply a matter of calling + start() with a QIODevice. QAudioOutput will then fetch the data it + needs from the io device. So playing back an audio file is as + simple as: + + \code + QFile inputFile; + inputFile.setFileName("/tmp/test.raw"); + inputFile.open(QIODevice::ReadOnly); + + QAudioFormat format; + // Set up the format, eg. + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::UnSignedInt); + + QAudioOutput *audio = new QAudioOutput(format, this); + connect(audio,SIGNAL(stateChanged(QAudio::State)),SLOT(finishedPlaying(QAudio::State))); + audio->start(inputFile); + + \endcode + + The file will start playing assuming that the audio system and + output device support it. If you run out of luck, check what's + up with the error() function. + + After the file has finished playing, we need to stop the device: + + \code + void finishedPlaying(QAudio::State state) + { + if(state == QAudio::IdleState) { + audio->stop(); + inputFile.close(); + } + } + \endcode + + At any given time, the QAudioOutput will be in one of four states: + active, suspended, stopped, or idle. These states are described + by the QAudio::State enum. + State changes are reported through the stateChanged() signal. You + can use this signal to, for instance, update the GUI of the + application; the mundane example here being changing the state of + a \c { play/pause } button. You request a state change directly + with suspend(), stop(), reset(), resume(), and start(). + + While the stream is playing, you can set a notify interval in + milliseconds with setNotifyInterval(). This interval specifies the + time between two emissions of the notify() signal. This is + relative to the position in the stream, i.e., if the QAudioOutput + is in the SuspendedState or the IdleState, the notify() signal is + not emitted. A typical use-case would be to update a + \l{QSlider}{slider} that allows seeking in the stream. + If you want the time since playback started regardless of which + states the audio output has been in, clock() is the function for you. + + If an error occurs, you can fetch the \l{QAudio::Error}{error + type} with the error() function. Please see the QAudio::Error enum + for a description of the possible errors that are reported. + + If an error is encountered state changes to QAudio::StopState. + + \sa QAudioInput, QAudioDeviceInfo +*/ + +/*! + Construct a new audio output and attach it to \a parent. + The default audio output device is used with the output + \a format parameters. +*/ + +QAudioOutput::QAudioOutput(const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createDefaultOutputDevice(format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Construct a new audio output and attach it to \a parent. + The \a id of the audio output device is used with the output + \a format parameters. +*/ + +QAudioOutput::QAudioOutput(const QAudioDeviceId &id, const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createOutputDevice(id, format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Destroys this audio output. +*/ + +QAudioOutput::~QAudioOutput() +{ + delete d; +} + +/*! + Returns the QAudioFormat being used. + +*/ + +QAudioFormat QAudioOutput::format() const +{ + return d->format(); +} + +/*! + Uses the \a device as the QIODevice to transfer data. + If \a device is null then the class creates an internal QIODevice. + Returns a pointer to the QIODevice being used to handle the data + transfer. This QIODevice can be used to write() audio data + directly. + Passing a QIODevice allows the data to be transfered without any extra code. + All that is required is to open the QIODevice. + + /sa QIODevice +*/ + +QIODevice* QAudioOutput::start(QIODevice* device) +{ + /* + PULL MODE (valid QIODevice) + -If currently not StopState, stop. + -If previous start was push mode, delete internal QIODevice. + -open audio output. + -If ok, NoError and ActiveState, else OpenError and StopState + -emit stateChanged() + -return device + + PUSH MODE (device = 0) + -If currently not StopState, stop. + -If no internal QIODevice, create one. + -open audio output. + -If ok, NoError and IdleState, else OpenError and StopState + -emit stateChanged() + -return internal QIODevice + */ + return d->start(device); +} + +/*! + Stops the audio output. +*/ + +void QAudioOutput::stop() +{ + /* + -If StopState, return + -set to StopState + -detach from audio device + -emit stateChanged() + */ + d->stop(); +} + +/*! + Drops all audio data in the buffers, resets buffers to zero. +*/ + +void QAudioOutput::reset() +{ + /* + -drop all buffered audio, set buffers to zero. + -call stop() + */ + d->reset(); +} + +/*! + Stops processing audio data, preserving buffered audio data. +*/ + +void QAudioOutput::suspend() +{ + /* + -If not ActiveState|IdleState, return + -stop processing audio, saving all buffered audio data + -set NoError and SuspendState + -emit stateChanged() + */ + d->suspend(); +} + +/*! + Resumes processing audio data after a suspend(). +*/ + +void QAudioOutput::resume() +{ + /* + -If SuspendState, return + -resume audio + -(PULL MODE): set ActiveState, NoError + -(PUSH MODE): set IdleState, NoError + -kick start audio if needed + -emit stateChanged() + */ + d->resume(); +} + +/*! + Returns the free space available in bytes in the audio buffer. +*/ + +int QAudioOutput::bytesFree() const +{ + /* + -If not ActiveState|IdleState, return 0 + -return space available in audio buffer in bytes + */ + return d->bytesFree(); +} + +/*! + Returns the period size in bytes. + + Note: This is the recommended write size in bytes. +*/ + +int QAudioOutput::periodSize() const +{ + return d->periodSize(); +} + +/*! + Sets the audio buffer size to \a value in bytes. + + Note: This function can be called anytime before start(), calls to this + are ignored after start(). It should not be assumed that the buffer size + set is the actual buffer size used, calling bufferSize() anytime after start() + will return the actual buffer size being used. +*/ + +void QAudioOutput::setBufferSize(int value) +{ + d->setBufferSize(value); +} + +/*! + Returns the audio buffer size in bytes. + + If called before start(), returns platform default value. + If called before start() but setBufferSize() was called prior, returns value set by setBufferSize(). + If called after start(), returns the actual buffer size being used. This may not be what was set previously + by setBufferSize(). + +*/ + +int QAudioOutput::bufferSize() const +{ + return d->bufferSize(); +} + +/*! + Sets the interval for notify() signal to be emitted. + This is based on the \a ms of audio data processed + not on actual real-time. The resolution of the timer is platform specific. +*/ + +void QAudioOutput::setNotifyInterval(int ms) +{ + d->setNotifyInterval(ms); +} + +/*! + Returns the notify interval in milliseconds. +*/ + +int QAudioOutput::notifyInterval() const +{ + return d->notifyInterval(); +} + +/*! + Returns the amount of audio data processed since start() + was called in microseconds. +*/ + +qint64 QAudioOutput::totalTime() const +{ + return d->totalTime(); +} + +/*! + Returns the microseconds since start() was called, including time in Idle and + Suspend states. +*/ + +qint64 QAudioOutput::clock() const +{ + return d->clock(); +} + +/*! + Returns the error state. +*/ + +QAudio::Error QAudioOutput::error() const +{ + return d->error(); +} + +/*! + Returns the state of audio processing. +*/ + +QAudio::State QAudioOutput::state() const +{ + return d->state(); +} + +/*! + \fn QAudioOutput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. + This is the current state of the audio output. +*/ + +/*! + \fn QAudioOutput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + +QT_END_NAMESPACE diff --git a/src/multimedia/audio/qaudiooutput.h b/src/multimedia/audio/qaudiooutput.h new file mode 100644 index 0000000..95e28ea --- /dev/null +++ b/src/multimedia/audio/qaudiooutput.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QAUDIOOUTPUT_H +#define QAUDIOOUTPUT_H + +#include <QtCore/qiodevice.h> +#include <QtCore/qglobal.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceid.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAbstractAudioOutput; + +class Q_MULTIMEDIA_EXPORT QAudioOutput : public QObject +{ + Q_OBJECT + +public: + explicit QAudioOutput(const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + explicit QAudioOutput(const QAudioDeviceId &id, const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + ~QAudioOutput(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice *device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + + void setBufferSize(int bytes); + int bufferSize() const; + + int bytesFree() const; + int periodSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 totalTime() const; + qint64 clock() const; + + QAudio::Error error() const; + QAudio::State state() const; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); + +private: + Q_DISABLE_COPY(QAudioOutput) + + QAbstractAudioOutput* d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOOUTPUT_H diff --git a/src/multimedia/audio/qaudiooutput_alsa_p.cpp b/src/multimedia/audio/qaudiooutput_alsa_p.cpp new file mode 100644 index 0000000..d41c449 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_alsa_p.cpp @@ -0,0 +1,706 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qcoreapplication.h> +#include "qaudiooutput_alsa_p.h" + +//#define DEBUG_AUDIO 1 + +QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + handle = 0; + ahandler = 0; + access = SND_PCM_ACCESS_RW_INTERLEAVED; + pcmformat = SND_PCM_FORMAT_S16; + buffer_frames = 0; + period_frames = 0; + buffer_size = 0; + period_size = 0; + buffer_time = 100000; + period_time = 20000; + totalTimeValue = 0; + intervalTime = 1000; + audioBuffer = 0; + errorState = QAudio::NoError; + deviceState = QAudio::StopState; + audioSource = 0; + pullMode = true; + resuming = false; + opened = false; + + QStringList list1 = QString(tr(device)).split(tr(":")); + m_device = QByteArray(list1.at(0).toLocal8Bit().constData()); + + timer = new QTimer(this); + connect(timer,SIGNAL(timeout()),SLOT(userFeed())); +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + close(); + disconnect(timer, SIGNAL(timeout())); + QCoreApplication::processEvents(); + delete timer; +} + +QAudio::Error QAudioOutputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioOutputPrivate::state() const +{ + return deviceState; +} + +void QAudioOutputPrivate::async_callback(snd_async_handler_t *ahandler) +{ + QAudioOutputPrivate* audioOut; + + audioOut = static_cast<QAudioOutputPrivate*> + (snd_async_handler_get_callback_private(ahandler)); + + if((audioOut->deviceState==QAudio::ActiveState)||(audioOut->resuming)) + audioOut->feedback(); +} + +int QAudioOutputPrivate::xrun_recovery(int err) +{ + int count = 0; + bool reset = false; + + if(err == -EPIPE) { + errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + + } else if((err == -ESTRPIPE)||(err == -EIO)) { + errorState = QAudio::IOError; + while((err = snd_pcm_resume(handle)) == -EAGAIN){ + usleep(100); + count++; + if(count > 5) { + reset = true; + break; + } + } + if(err < 0) { + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + } + } + if(reset) { + close(); + open(); + snd_pcm_prepare(handle); + return 0; + } + return err; +} + +int QAudioOutputPrivate::setFormat() +{ + snd_pcm_format_t pcmformat = SND_PCM_FORMAT_S16; + + if(settings.sampleSize() == 8) { + pcmformat = SND_PCM_FORMAT_U8; + + } else if(settings.sampleSize() == 16) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_S16_LE; + else + pcmformat = SND_PCM_FORMAT_S16_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_U16_LE; + else + pcmformat = SND_PCM_FORMAT_U16_BE; + } + } else if(settings.sampleSize() == 24) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_S24_LE; + else + pcmformat = SND_PCM_FORMAT_S24_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_U24_LE; + else + pcmformat = SND_PCM_FORMAT_U24_BE; + } + } else if(settings.sampleSize() == 32) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_S32_LE; + else + pcmformat = SND_PCM_FORMAT_S32_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_U32_LE; + else + pcmformat = SND_PCM_FORMAT_U32_BE; + } else if(settings.sampleType() == QAudioFormat::Float) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_FLOAT_LE; + else + pcmformat = SND_PCM_FORMAT_FLOAT_BE; + } + } else if(settings.sampleSize() == 64) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_FLOAT64_LE; + else + pcmformat = SND_PCM_FORMAT_FLOAT64_BE; + } + + return snd_pcm_hw_params_set_format( handle, hwparams, pcmformat); +} + +QIODevice* QAudioOutputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StopState) + deviceState = QAudio::StopState; + + errorState = QAudio::NoError; + + // Handle change of mode + if(audioSource && pullMode && !device) { + // pull -> push + close(); + audioSource = 0; + } else if(audioSource && !pullMode && device) { + // push -> pull + close(); + delete audioSource; + audioSource = 0; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + deviceState = QAudio::ActiveState; + } else { + //set to push mode + if(!audioSource) { + audioSource = new OutputPrivate(this); + audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); + } + pullMode = false; + deviceState = QAudio::IdleState; + } + + open(); + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioOutputPrivate::stop() +{ + if(deviceState == QAudio::StopState) + return; + deviceState = QAudio::StopState; + close(); + emit stateChanged(deviceState); +} + +bool QAudioOutputPrivate::open() +{ + if(opened) + return true; + +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + timeStamp.restart(); + + int dir; + int err=-1; + int count=0; + unsigned int freakuency=settings.frequency(); + + QString dev = tr(m_device.constData()); + if(!dev.contains(tr("default"))) { + dev = QString(tr("default:CARD=%1")).arg(tr(m_device.constData())); + } + // Step 1: try and open the device + while((count < 5) && (err < 0)) { + err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); + if(err < 0) + count++; + } + if (( err < 0)||(handle == 0)) { + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + return false; + } + snd_pcm_nonblock( handle, 0 ); + + // Step 2: Set the desired HW parameters. + snd_pcm_hw_params_alloca( &hwparams ); + + bool fatal = false; + QString errMessage; + unsigned int chunks = 8; + + err = snd_pcm_hw_params_any( handle, hwparams ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_any: err = %1")).arg(err); + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_rate_resample: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_access( handle, hwparams, access ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_access: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = setFormat(); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_format: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_channels: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_rate_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_buffer_time_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_period_time_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params_set_periods_near: err = %1")).arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params(handle, hwparams); + if ( err < 0 ) { + fatal = true; + errMessage = QString(tr("QAudioOutput: snd_pcm_hw_params: err = %1")).arg(err); + } + } + if( err < 0) { + qWarning()<<errMessage; + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + return false; + } + snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); + buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); + snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); + period_size = snd_pcm_frames_to_bytes(handle,period_frames); + snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); + snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); + + // Step 3: Set the desired SW parameters. + snd_pcm_sw_params_t *swparams; + snd_pcm_sw_params_alloca(&swparams); + snd_pcm_sw_params_current(handle, swparams); + snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); + snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); + snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); + snd_pcm_sw_params(handle, swparams); + + // Step 4: Prepare audio + if(audioBuffer == 0) + audioBuffer = new char[snd_pcm_frames_to_bytes(handle,buffer_frames)]; + snd_pcm_prepare( handle ); + snd_pcm_start(handle); + + // Step 5: Setup callback and timer fallback + snd_async_add_pcm_handler(&ahandler, handle, async_callback, this); + bytesAvailable = bytesFree(); + + // Step 6: Start audio processing + timer->start(period_time/1000); + + errorState = QAudio::NoError; + totalTimeValue = 0; + opened = true; + + return true; +} + +void QAudioOutputPrivate::close() +{ + deviceState = QAudio::StopState; + timer->stop(); + + if ( handle ) { + snd_pcm_drain( handle ); + snd_pcm_close( handle ); + handle = 0; + delete [] audioBuffer; + audioBuffer=0; + } + if(!pullMode && audioSource) { + delete audioSource; + audioSource = 0; + } + opened = false; +} + +int QAudioOutputPrivate::bytesFree() const +{ + if(resuming) + return period_size; + + if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) + return 0; + int frames = snd_pcm_avail_update(handle); + if((int)frames > (int)buffer_frames) + frames = buffer_frames; + + return snd_pcm_frames_to_bytes(handle, frames); +} + +qint64 QAudioOutputPrivate::write( const char *data, qint64 len ) +{ + // Write out some audio data + if ( !handle ) + return 0; +#ifdef DEBUG_AUDIO + qDebug()<<"frames to write out = "<< + snd_pcm_bytes_to_frames( handle, (int)len )<<" ("<<len<<") bytes"; +#endif + int frames, err; + int space = bytesFree(); + if(len < space) { + // Just write it + frames = snd_pcm_bytes_to_frames( handle, (int)len ); + err = snd_pcm_writei( handle, data, frames ); + } else { + // Only write space worth + frames = snd_pcm_bytes_to_frames( handle, (int)space ); + err = snd_pcm_writei( handle, data, frames ); + } + if(err > 0) { + totalTimeValue += err*1000000/settings.frequency(); + resuming = false; + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + return snd_pcm_frames_to_bytes( handle, err ); + } else + err = xrun_recovery(err); + + if(err < 0) { + close(); + errorState = QAudio::FatalError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + } + return 0; +} + +int QAudioOutputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioOutputPrivate::setBufferSize(int value) +{ + if(deviceState == QAudio::StopState) + buffer_size = value; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return buffer_size; +} + +void QAudioOutputPrivate::setNotifyInterval(int ms) +{ + intervalTime = ms; +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioOutputPrivate::totalTime() const +{ + return totalTimeValue; +} + +void QAudioOutputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + int err = 0; + + if(handle) { + err = snd_pcm_prepare( handle ); + if(err < 0) + xrun_recovery(err); + + err = snd_pcm_start(handle); + if(err < 0) + xrun_recovery(err); + + bytesAvailable = (int)snd_pcm_frames_to_bytes(handle, buffer_frames); + } + resuming = true; + if(pullMode) + deviceState = QAudio::ActiveState; + else + deviceState = QAudio::IdleState; + + errorState = QAudio::NoError; + timer->start(period_time/1000); + emit stateChanged(deviceState); + } +} + +QAudioFormat QAudioOutputPrivate::format() const +{ + return settings; +} + +void QAudioOutputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState || resuming) { + timer->stop(); + deviceState = QAudio::SuspendState; + errorState = QAudio::NoError; + emit stateChanged(deviceState); + } +} + +void QAudioOutputPrivate::userFeed() +{ + if(deviceState == QAudio::StopState || deviceState == QAudio::SuspendState) + return; +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() OUT"; +#endif + if(deviceState == QAudio::IdleState) + bytesAvailable = bytesFree(); + + deviceReady(); +} + +void QAudioOutputPrivate::feedback() +{ + QMetaObject::invokeMethod(this, SLOT(updateAvailable()), Qt::QueuedConnection); +} + +void QAudioOutputPrivate::updateAvailable() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :updateAvailable()"; +#endif + bytesAvailable = bytesFree(); +} + +bool QAudioOutputPrivate::deviceReady() +{ + if(pullMode) { + int l = 0; + int chunks = bytesAvailable/period_size; + if(chunks==0) { + bytesAvailable = bytesFree(); + return false; + } +#ifdef DEBUG_AUDIO + qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes"; + qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<period_size*chunks; +#endif + int input = period_frames*chunks; + if(input > (int)buffer_frames) + input = buffer_frames; + l = audioSource->read(audioBuffer,snd_pcm_frames_to_bytes(handle, input)); + if(l > 0) { + // Got some data to output + if(deviceState != QAudio::ActiveState) + return true; + write(audioBuffer,l); + bytesAvailable = bytesFree(); + + } else if(l == 0) { + // Did not get any data to output + bytesAvailable = bytesFree(); + if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) { + // Underrun + errorState = QAudio::UnderrunError; + deviceState = QAudio::IdleState; + emit stateChanged(deviceState); + } + + } else if(l < 0) { + close(); + errorState = QAudio::IOError; + emit stateChanged(deviceState); + } + } else + bytesAvailable = bytesFree(); + + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + return true; +} + +qint64 QAudioOutputPrivate::clock() const +{ + if(!handle) + return 0; + + if(deviceState != QAudio::ActiveState) + return 0; + + snd_pcm_status_t* status; + snd_pcm_status_alloca(&status); + + snd_timestamp_t t1,t2; + if( snd_pcm_status(handle, status) >= 0) { + snd_pcm_status_get_tstamp(status,&t1); + snd_pcm_status_get_trigger_tstamp(status,&t2); + t1.tv_sec-=t2.tv_sec; + + signed long l = (signed long)t1.tv_usec - (signed long)t2.tv_usec; + if(l < 0) { + t1.tv_sec--; + l = -l; + l %= 1000000; + } + return ((t1.tv_sec * 1000)+l/1000); + } else + return 0; + return 0; +} + +void QAudioOutputPrivate::reset() +{ + if(handle) + snd_pcm_reset(handle); + + stop(); +} + +OutputPrivate::OutputPrivate(QAudioOutputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioOutputPrivate*>(audio); +} + +OutputPrivate::~OutputPrivate() {} + +qint64 OutputPrivate::readData( char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + + return 0; +} + +qint64 OutputPrivate::writeData(const char* data, qint64 len) +{ + int retry = 0; + qint64 written = 0; + if((audioDevice->deviceState == QAudio::ActiveState) + ||(audioDevice->deviceState == QAudio::IdleState)) { + while(written < len) { + int chunk = audioDevice->write(data+written,(len-written)); + if(chunk <= 0) + retry++; + written+=chunk; + if(retry > 10) + return written; + } + } + return written; + +} diff --git a/src/multimedia/audio/qaudiooutput_alsa_p.h b/src/multimedia/audio/qaudiooutput_alsa_p.h new file mode 100644 index 0000000..f243bad --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_alsa_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOOUTPUTALSA_H +#define QAUDIOOUTPUTALSA_H + +#include <alsa/asoundlib.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + +class OutputPrivate; + +class QAudioOutputPrivate : public QAbstractAudioOutput +{ + friend class OutputPrivate; + Q_OBJECT +public: + QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioOutputPrivate(); + + qint64 write( const char *data, qint64 len ); + + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesFree() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 totalTime() const; + qint64 clock() const; + QAudio::Error error() const; + QAudio::State state() const; + QAudioFormat format() const; + + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private slots: + void userFeed(); + void feedback(); + void updateAvailable(); + bool deviceReady(); + +signals: + void processMore(); + +private: + bool opened; + bool pullMode; + bool resuming; + int buffer_size; + int period_size; + int intervalTime; + qint64 totalTimeValue; + unsigned int buffer_time; + unsigned int period_time; + snd_pcm_uframes_t buffer_frames; + snd_pcm_uframes_t period_frames; + static void async_callback(snd_async_handler_t *ahandler); + int xrun_recovery(int err); + + int setFormat(); + bool open(); + void close(); + + QTimer* timer; + QByteArray m_device; + int bytesAvailable; + QTime timeStamp; + char* audioBuffer; + snd_pcm_t* handle; + snd_async_handler_t* ahandler; + snd_pcm_access_t access; + snd_pcm_format_t pcmformat; + snd_timestamp_t* timestamp; + snd_pcm_hw_params_t *hwparams; +}; + +class OutputPrivate : public QIODevice +{ + friend class QAudioOutputPrivate; + Q_OBJECT +public: + OutputPrivate(QAudioOutputPrivate* audio); + ~OutputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + +private: + QAudioOutputPrivate *audioDevice; +}; + +#endif diff --git a/src/multimedia/audio/qaudiooutput_mac_p.cpp b/src/multimedia/audio/qaudiooutput_mac_p.cpp new file mode 100644 index 0000000..0d6e615 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_mac_p.cpp @@ -0,0 +1,700 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <CoreServices/CoreServices.h> +#include <CoreAudio/CoreAudio.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <QtCore/qendian.h> +#include <QtCore/qbuffer.h> +#include <QtCore/qtimer.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudiooutput.h> + +#include "qaudio_mac_p.h" +#include "qaudiooutput_mac_p.h" + + +QT_BEGIN_NAMESPACE + + +namespace +{ + +static const int default_buffer_size = 8 * 1024; + + +class QAudioOutputBuffer : public QObject +{ + Q_OBJECT + +public: + QAudioOutputBuffer(int bufferSize, int maxPeriodSize, QAudioFormat const& audioFormat): + m_deviceError(false), + m_maxPeriodSize(maxPeriodSize), + m_device(0) + { + m_buffer = new QAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize))); + m_bytesPerFrame = (audioFormat.sampleSize() / 8) * audioFormat.channels(); + m_periodTime = m_maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.frequency(); + + m_fillTimer = new QTimer(this); + connect(m_fillTimer, SIGNAL(timeout()), SLOT(fillBuffer())); + } + + ~QAudioOutputBuffer() + { + delete m_buffer; + } + + qint64 readFrames(char* data, qint64 maxFrames) + { + bool wecan = true; + qint64 framesRead = 0; + + while (wecan && framesRead < maxFrames) { + QAudioRingBuffer::Region region = m_buffer->acquireReadRegion((maxFrames - framesRead) * m_bytesPerFrame); + + if (region.second > 0) { + region.second -= region.second % m_bytesPerFrame; + memcpy(data + (framesRead * m_bytesPerFrame), region.first, region.second); + framesRead += region.second / m_bytesPerFrame; + } + else + wecan = false; + + m_buffer->releaseReadRegion(region); + } + + if (framesRead == 0 && m_deviceError) + framesRead = -1; + + return framesRead; + } + + qint64 writeBytes(const char* data, qint64 maxSize) + { + bool wecan = true; + qint64 bytesWritten = 0; + + maxSize -= maxSize % m_bytesPerFrame; + while (wecan && bytesWritten < maxSize) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(maxSize - bytesWritten); + + if (region.second > 0) { + memcpy(region.first, data + bytesWritten, region.second); + bytesWritten += region.second; + } + else + wecan = false; + + m_buffer->releaseWriteRegion(region); + } + + if (bytesWritten > 0) + emit readyRead(); + + return bytesWritten; + } + + int available() const + { + return m_buffer->free(); + } + + void reset() + { + m_buffer->reset(); + m_deviceError = false; + } + + void setPrefetchDevice(QIODevice* device) + { + if (m_device != device) { + m_device = device; + if (m_device != 0) + fillBuffer(); + } + } + + void startFillTimer() + { + if (m_device != 0) + m_fillTimer->start(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime); + } + + void stopFillTimer() + { + m_fillTimer->stop(); + } + +signals: + void readyRead(); + +private slots: + void fillBuffer() + { + const int free = m_buffer->free(); + const int writeSize = free - (free % m_maxPeriodSize); + + if (writeSize > 0) { + bool wecan = true; + int filled = 0; + + while (!m_deviceError && wecan && filled < writeSize) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(writeSize - filled); + + if (region.second > 0) { + region.second = m_device->read(region.first, region.second); + if (region.second > 0) + filled += region.second; + else if (region.second == 0) + wecan = false; + else if (region.second < 0) { + m_fillTimer->stop(); + region.second = 0; + m_deviceError = true; + } + } + else + wecan = false; + + m_buffer->releaseWriteRegion(region); + } + + if (filled > 0) + emit readyRead(); + } + } + +private: + bool m_deviceError; + int m_maxPeriodSize; + int m_bytesPerFrame; + int m_periodTime; + QIODevice* m_device; + QTimer* m_fillTimer; + QAudioRingBuffer* m_buffer; +}; + + +} + +class MacOutputDevice : public QIODevice +{ + Q_OBJECT + +public: + MacOutputDevice(QAudioOutputBuffer* audioBuffer, QObject* parent): + QIODevice(parent), + m_audioBuffer(audioBuffer) + { + open(QIODevice::WriteOnly | QIODevice::Unbuffered); + } + + qint64 readData(char* data, qint64 len) + { + Q_UNUSED(data); + Q_UNUSED(len); + + return 0; + } + + qint64 writeData(const char* data, qint64 len) + { + return m_audioBuffer->writeBytes(data, len); + } + + bool isSequential() const + { + return true; + } + +private: + QAudioOutputBuffer* m_audioBuffer; +}; + + +QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray& device, const QAudioFormat& format): + audioFormat(format) +{ + QDataStream ds(device); + quint32 did, mode; + + ds >> did >> mode; + + if (QAudio::Mode(mode) == QAudio::AudioInput) + errorCode = QAudio::OpenError; + else { + isOpen = false; + audioDeviceId = AudioDeviceID(did); + audioUnit = 0; + audioIO = 0; + startTime = 0; + totalFrames = 0; + audioBuffer = 0; + internalBufferSize = default_buffer_size; + clockFrequency = AudioGetHostClockFrequency() / 1000; + errorCode = QAudio::NoError; + stateCode = QAudio::StopState; + audioThreadState = Stopped; + + intervalTimer = new QTimer(this); + intervalTimer->setInterval(1000); + connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify())); + } +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + close(); +} + +bool QAudioOutputPrivate::open() +{ + if (errorCode != QAudio::NoError) + return false; + + if (isOpen) + return true; + + ComponentDescription cd; + cd.componentType = kAudioUnitType_Output; + cd.componentSubType = kAudioUnitSubType_HALOutput; + cd.componentManufacturer = kAudioUnitManufacturer_Apple; + cd.componentFlags = 0; + cd.componentFlagsMask = 0; + + // Open + Component cp = FindNextComponent(NULL, &cd); + if (cp == 0) { + qWarning() << "QAudioOutput: Failed to find HAL Output component"; + return false; + } + + if (OpenAComponent(cp, &audioUnit) != noErr) { + qWarning() << "QAudioOutput: Unable to Open Output Component"; + return false; + } + + // register callback + AURenderCallbackStruct cb; + cb.inputProc = renderCallback; + cb.inputProcRefCon = this; + + if (AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Global, + 0, + &cb, + sizeof(cb)) != noErr) { + qWarning() << "QAudioOutput: Failed to set AudioUnit callback"; + return false; + } + + // Set Audio Device + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 0, + &audioDeviceId, + sizeof(audioDeviceId)) != noErr) { + qWarning() << "QAudioOutput: Unable to use configured device"; + return false; + } + + // Set stream format + streamFormat = toAudioStreamBasicDescription(audioFormat); + + UInt32 size = sizeof(deviceFormat); + if (AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &deviceFormat, + &size) != noErr) { + qWarning() << "QAudioOutput: Unable to retrieve device format"; + return false; + } + + if (AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &streamFormat, + sizeof(streamFormat)) != noErr) { + qWarning() << "QAudioOutput: Unable to Set Stream information"; + return false; + } + + // Allocate buffer + UInt32 numberOfFrames = 0; + size = sizeof(UInt32); + if (AudioUnitGetProperty(audioUnit, + kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Global, + 0, + &numberOfFrames, + &size) != noErr) { + qWarning() << "QAudioInput: Failed to get audio period size"; + return false; + } + + periodSizeBytes = (numberOfFrames * streamFormat.mSampleRate / deviceFormat.mSampleRate) * + streamFormat.mBytesPerFrame; + if (internalBufferSize < periodSizeBytes * 2) + internalBufferSize = periodSizeBytes * 2; + else + internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame; + + audioBuffer = new QAudioOutputBuffer(internalBufferSize, periodSizeBytes, audioFormat); + connect(audioBuffer, SIGNAL(readyRead()), SLOT(inputReady())); // Pull + + audioIO = new MacOutputDevice(audioBuffer, this); + + // Init + if (AudioUnitInitialize(audioUnit)) { + qWarning() << "QAudioOutput: Failed to initialize AudioUnit"; + return false; + } + + isOpen = true; + + return true; +} + +void QAudioOutputPrivate::close() +{ + if (audioUnit != 0) { + AudioOutputUnitStop(audioUnit); + AudioUnitUninitialize(audioUnit); + CloseComponent(audioUnit); + } + + delete audioBuffer; +} + +QAudioFormat QAudioOutputPrivate::format() const +{ + return audioFormat; +} + +QIODevice* QAudioOutputPrivate::start(QIODevice* device) +{ + QIODevice* op = device; + + if (!open()) { + stateCode = QAudio::StopState; + errorCode = QAudio::OpenError; + return audioIO; + } + + reset(); + audioBuffer->reset(); + audioBuffer->setPrefetchDevice(op); + + if (op == 0) { + op = audioIO; + stateCode = QAudio::IdleState; + } + else + stateCode = QAudio::ActiveState; + + // Start + errorCode = QAudio::NoError; + totalFrames = 0; + startTime = AudioGetCurrentHostTime(); + + if (stateCode == QAudio::ActiveState) + audioThreadStart(); + + return op; +} + +void QAudioOutputPrivate::stop() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadDrain(); + + stateCode = QAudio::StopState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioOutputPrivate::reset() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StopState) { + audioThreadStop(); + + stateCode = QAudio::StopState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioOutputPrivate::suspend() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) { + audioThreadStop(); + + stateCode = QAudio::SuspendState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioOutputPrivate::resume() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::SuspendState) { + audioThreadStart(); + + stateCode = QAudio::ActiveState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +int QAudioOutputPrivate::bytesFree() const +{ + return audioBuffer->available(); +} + +int QAudioOutputPrivate::periodSize() const +{ + return periodSizeBytes; +} + +void QAudioOutputPrivate::setBufferSize(int bs) +{ + if (stateCode == QAudio::StopState) + internalBufferSize = bs; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return internalBufferSize; +} + +void QAudioOutputPrivate::setNotifyInterval(int milliSeconds) +{ + intervalTimer->setInterval(milliSeconds); +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTimer->interval(); +} + +qint64 QAudioOutputPrivate::totalTime() const +{ + return totalFrames * 1000000 / audioFormat.frequency(); +} + +qint64 QAudioOutputPrivate::clock() const +{ + return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000); +} + +QAudio::Error QAudioOutputPrivate::error() const +{ + return errorCode; +} + +QAudio::State QAudioOutputPrivate::state() const +{ + return stateCode; +} + +void QAudioOutputPrivate::audioThreadStart() +{ + startTimers(); + audioThreadState = Running; + AudioOutputUnitStart(audioUnit); +} + +void QAudioOutputPrivate::audioThreadStop() +{ + stopTimers(); + if (audioThreadState.testAndSetAcquire(Running, Stopped)) + threadFinished.wait(&mutex); +} + +void QAudioOutputPrivate::audioThreadDrain() +{ + stopTimers(); + if (audioThreadState.testAndSetAcquire(Running, Draining)) + threadFinished.wait(&mutex); +} + +void QAudioOutputPrivate::audioDeviceStop() +{ + AudioOutputUnitStop(audioUnit); + audioThreadState = Stopped; + threadFinished.wakeOne(); +} + +void QAudioOutputPrivate::audioDeviceIdle() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::UnderrunError; + stateCode = QAudio::IdleState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioOutputPrivate::audioDeviceError() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::IOError; + stateCode = QAudio::StopState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioOutputPrivate::startTimers() +{ + audioBuffer->startFillTimer(); + intervalTimer->start(); +} + +void QAudioOutputPrivate::stopTimers() +{ + audioBuffer->stopFillTimer(); + intervalTimer->stop(); +} + + +void QAudioOutputPrivate::deviceStopped() +{ + intervalTimer->stop(); + emit stateChanged(stateCode); +} + +void QAudioOutputPrivate::inputReady() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::IdleState) { + audioThreadStart(); + + stateCode = QAudio::ActiveState; + errorCode = QAudio::NoError; + + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + + +OSStatus QAudioOutputPrivate::renderCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) +{ + Q_UNUSED(ioActionFlags) + Q_UNUSED(inTimeStamp) + Q_UNUSED(inBusNumber) + Q_UNUSED(inNumberFrames) + + QAudioOutputPrivate* d = static_cast<QAudioOutputPrivate*>(inRefCon); + + const int threadState = d->audioThreadState.fetchAndAddAcquire(0); + if (threadState == Stopped) { + ioData->mBuffers[0].mDataByteSize = 0; + d->audioDeviceStop(); + } + else { + const UInt32 bytesPerFrame = d->streamFormat.mBytesPerFrame; + qint64 framesRead; + + framesRead = d->audioBuffer->readFrames((char*)ioData->mBuffers[0].mData, + ioData->mBuffers[0].mDataByteSize / bytesPerFrame); + + if (framesRead > 0) { + ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame; + d->totalFrames += framesRead; + } + else { + ioData->mBuffers[0].mDataByteSize = 0; + if (framesRead == 0) { + if (threadState == Draining) + d->audioDeviceStop(); + else + d->audioDeviceIdle(); + } + else + d->audioDeviceError(); + } + } + + return noErr; +} + + +QT_END_NAMESPACE + +#include "qaudiooutput_mac_p.moc" + diff --git a/src/multimedia/audio/qaudiooutput_mac_p.h b/src/multimedia/audio/qaudiooutput_mac_p.h new file mode 100644 index 0000000..c85cab4 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_mac_p.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOOUTPUT_MAC_P_H +#define QAUDIOOUTPUT_MAC_P_H + +#include <CoreServices/CoreServices.h> +#include <CoreAudio/CoreAudio.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <QtCore/qobject.h> +#include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> +#include <QtCore/qtimer.h> +#include <QtCore/qatomic.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QIODevice; + +namespace +{ +class QAudioOutputBuffer; +} + +class QAudioOutputPrivate : public QAbstractAudioOutput +{ + Q_OBJECT + +public: + bool isOpen; + int internalBufferSize; + int periodSizeBytes; + qint64 totalFrames; + QAudioFormat audioFormat; + QIODevice* audioIO; + AudioDeviceID audioDeviceId; + AudioUnit audioUnit; + Float64 clockFrequency; + UInt64 startTime; + AudioStreamBasicDescription deviceFormat; + AudioStreamBasicDescription streamFormat; + QAudioOutputBuffer* audioBuffer; + QAtomicInt audioThreadState; + QWaitCondition threadFinished; + QMutex mutex; + QTimer* intervalTimer; + + QAudio::Error errorCode; + QAudio::State stateCode; + + QAudioOutputPrivate(const QByteArray& device, const QAudioFormat& format); + ~QAudioOutputPrivate(); + + bool open(); + void close(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice* device); + void stop(); + void reset(); + void suspend(); + void resume(); + + int bytesFree() const; + int periodSize() const; + + void setBufferSize(int value); + int bufferSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 totalTime() const; + qint64 clock() const; + + QAudio::Error error() const; + QAudio::State state() const; + + void audioThreadStart(); + void audioThreadStop(); + void audioThreadDrain(); + + void audioDeviceStop(); + void audioDeviceIdle(); + void audioDeviceError(); + + void startTimers(); + void stopTimers(); + +signals: + void stateChanged(QAudio::State); + void notify(); + +private slots: + void deviceStopped(); + void inputReady(); + +private: + enum { Running, Draining, Stopped }; + + static OSStatus renderCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/multimedia/audio/qaudiooutput_win32_p.cpp b/src/multimedia/audio/qaudiooutput_win32_p.cpp new file mode 100644 index 0000000..f681936 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_win32_p.cpp @@ -0,0 +1,502 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qaudiooutput_win32_p.h" + +//#define DEBUG_AUDIO 1 + +QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + buffer_size = 0; + period_size = 0; + m_device = device; + totalTimeValue = 0; + intervalTime = 1000; + audioBuffer = 0; + errorState = QAudio::NoError; + deviceState = QAudio::StopState; + audioSource = 0; + pullMode = true; + InitializeCriticalSection(&waveOutCriticalSection); +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + close(); + DeleteCriticalSection(&waveOutCriticalSection); +} + +void CALLBACK QAudioOutputPrivate::waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) +{ + Q_UNUSED(dwParam1) + Q_UNUSED(dwParam2) + Q_UNUSED(hWaveOut) + + QAudioOutputPrivate* qAudio; + qAudio = (QAudioOutputPrivate*)(dwInstance); + if(!qAudio) + return; + + switch(uMsg) { + case WOM_OPEN: + qAudio->feedback(); + break; + case WOM_CLOSE: + return; + case WOM_DONE: + EnterCriticalSection(&waveOutCriticalSection); + qAudio->waveFreeBlockCount++; + if(qAudio->waveFreeBlockCount >= qAudio->buffer_size/qAudio->period_size) + qAudio->waveFreeBlockCount = qAudio->buffer_size/qAudio->period_size; + LeaveCriticalSection(&waveOutCriticalSection); + qAudio->feedback(); + break; + default: + return; + } +} + +WAVEHDR* QAudioOutputPrivate::allocateBlocks(int size, int count) +{ + int i; + unsigned char* buffer; + WAVEHDR* blocks; + DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count; + + if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, + totalBufferSize)) == 0) { + qWarning("QAudioOutput: Memory allocation error"); + return 0; + } + blocks = (WAVEHDR*)buffer; + buffer += sizeof(WAVEHDR)*count; + for(i = 0; i < count; i++) { + blocks[i].dwBufferLength = size; + blocks[i].lpData = (LPSTR)buffer; + buffer += size; + } + return blocks; +} + +void QAudioOutputPrivate::freeBlocks(WAVEHDR* blockArray) +{ + HeapFree(GetProcessHeap(), 0, blockArray); +} + +QAudioFormat QAudioOutputPrivate::format() const +{ + return settings; +} + +QIODevice* QAudioOutputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StopState) + close(); + + if(!pullMode && audioSource) { + delete audioSource; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + deviceState = QAudio::ActiveState; + } else { + //set to push mode + pullMode = false; + audioSource = new OutputPrivate(this); + audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); + deviceState = QAudio::IdleState; + } + + if( !open() ) + return 0; + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioOutputPrivate::stop() +{ + if(deviceState == QAudio::StopState) + return; + deviceState = QAudio::StopState; + close(); + if(!pullMode && audioSource) { + delete audioSource; + audioSource = 0; + } + emit stateChanged(deviceState); +} + +bool QAudioOutputPrivate::open() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + if(buffer_size == 0) { + // Default buffer size, 200ms, default period size is 40ms + buffer_size = settings.frequency()*settings.channels()*(settings.sampleSize()/8)*0.2; + period_size = buffer_size/5; + } else { + period_size = buffer_size/5; + } + waveBlocks = allocateBlocks(period_size, buffer_size/period_size); + waveFreeBlockCount = buffer_size/period_size; + waveCurrentBlock = 0; + + if(audioBuffer == 0) + audioBuffer = new char[buffer_size]; + + timeStamp.restart(); + + wfx.nSamplesPerSec = settings.frequency(); + wfx.wBitsPerSample = settings.sampleSize(); + wfx.nChannels = settings.channels(); + wfx.cbSize = 0; + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; + + UINT_PTR devId = WAVE_MAPPER; + + WAVEOUTCAPS woc; + unsigned long iNumDevs,ii; + iNumDevs = waveOutGetNumDevs(); + for(ii=0;ii<iNumDevs;ii++) { + if(waveOutGetDevCaps(ii, &woc, sizeof(WAVEOUTCAPS)) + == MMSYSERR_NOERROR) { + QString tmp; + tmp = QString::fromUtf16((const unsigned short*)woc.szPname); + if(tmp.compare(tr(m_device)) == 0) { + devId = ii; + break; + } + } + } + + if(waveOutOpen(&hWaveOut, devId, &wfx, + (DWORD_PTR)&waveOutProc, + (DWORD_PTR) this, + CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { + errorState = QAudio::OpenError; + deviceState = QAudio::StopState; + emit stateChanged(deviceState); + qWarning("QAudioOutput: open error"); + return false; + } + + totalTimeValue = 0; + timeStampOpened.restart(); + + errorState = QAudio::NoError; + if(pullMode) { + deviceState = QAudio::ActiveState; + QTimer::singleShot(10, this, SLOT(feedback())); + } else + deviceState = QAudio::IdleState; + + return true; +} + +void QAudioOutputPrivate::close() +{ + if(deviceState == QAudio::StopState) + return; + + deviceState = QAudio::StopState; + int delay = (buffer_size-bytesFree())*1000/(settings.frequency() + *settings.channels()*(settings.sampleSize()/8)); + waveOutReset(hWaveOut); + Sleep(delay+10); + + freeBlocks(waveBlocks); + waveOutClose(hWaveOut); + delete [] audioBuffer; + audioBuffer = 0; + buffer_size = 0; +} + +int QAudioOutputPrivate::bytesFree() const +{ + int buf; + buf = waveFreeBlockCount*period_size; + return buf; +} + +int QAudioOutputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioOutputPrivate::setBufferSize(int value) +{ + if(deviceState == QAudio::StopState) + buffer_size = value; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return buffer_size; +} + +void QAudioOutputPrivate::setNotifyInterval(int ms) +{ + intervalTime = ms; +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioOutputPrivate::totalTime() const +{ + return totalTimeValue; +} + +qint64 QAudioOutputPrivate::write( const char *data, qint64 len ) +{ + // Write out some audio data + + char* p = (char*)data; + int l = (int)len; + + WAVEHDR* current; + int remain; + current = &waveBlocks[waveCurrentBlock]; + while(l > 0) { + if(waveFreeBlockCount==0) + break; + + if(current->dwFlags & WHDR_PREPARED) + waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR)); + + if(l < period_size) + remain = l; + else + remain = period_size; + memcpy(current->lpData, p, remain); + + l -= remain; + p += remain; + current->dwBufferLength = remain; + waveOutPrepareHeader(hWaveOut, current, sizeof(WAVEHDR)); + waveOutWrite(hWaveOut, current, sizeof(WAVEHDR)); + + EnterCriticalSection(&waveOutCriticalSection); + waveFreeBlockCount--; + LeaveCriticalSection(&waveOutCriticalSection); +#ifdef DEBUG_AUDIO + qDebug("write out l=%d, waveFreeBlockCount=%d", + current->dwBufferLength,waveFreeBlockCount); +#endif + totalTimeValue += current->dwBufferLength + /(settings.channels()*(settings.sampleSize()/8)) + *1000000/settings.frequency();; + waveCurrentBlock++; + waveCurrentBlock %= buffer_size/period_size; + current = &waveBlocks[waveCurrentBlock]; + current->dwUser = 0; + } + return (len-l); +} + +void QAudioOutputPrivate::resume() +{ + if(deviceState == QAudio::SuspendState) { + deviceState = QAudio::ActiveState; + errorState = QAudio::NoError; + waveOutRestart(hWaveOut); + QTimer::singleShot(10, this, SLOT(feedback())); + emit stateChanged(deviceState); + } +} + +void QAudioOutputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState) { + waveOutPause(hWaveOut); + deviceState = QAudio::SuspendState; + errorState = QAudio::NoError; + emit stateChanged(deviceState); + } +} + +void QAudioOutputPrivate::feedback() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback()"; +#endif + bytesAvailable = bytesFree(); + + if(!(deviceState==QAudio::StopState||deviceState==QAudio::SuspendState)) { + if(bytesAvailable >= period_size) + QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection); + } +} + +bool QAudioOutputPrivate::deviceReady() +{ + if(pullMode) { + int i = 0; + int chunks = bytesAvailable/period_size; +#ifdef DEBUG_AUDIO + qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes"; + qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<chunks*period_size; +#endif + bool startup = false; + if(totalTimeValue == 0) + startup = true; + + if(startup) + waveOutPause(hWaveOut); + int input = period_size*chunks; + int l = audioSource->read(audioBuffer,input); + if(l > 0) { + int out= write(audioBuffer,l); + if(out > 0) + deviceState = QAudio::ActiveState; + if(startup) + waveOutRestart(hWaveOut); + } else if(l == 0) { + bytesAvailable = bytesFree(); + if(waveFreeBlockCount == buffer_size/period_size) { + errorState = QAudio::UnderrunError; + deviceState = QAudio::IdleState; + emit stateChanged(deviceState); + } + + } else if(i < 0) { + bytesAvailable = bytesFree(); + errorState = QAudio::IOError; + } + } + if(deviceState != QAudio::ActiveState) + return true; + + if(timeStamp.elapsed() > intervalTime && intervalTime > 50) { + emit notify(); + timeStamp.restart(); + } + + return true; +} + +qint64 QAudioOutputPrivate::clock() const +{ + if(deviceState != QAudio::ActiveState) + return 0; + + return timeStampOpened.elapsed(); +} + +QAudio::Error QAudioOutputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioOutputPrivate::state() const +{ + return deviceState; +} + +void QAudioOutputPrivate::reset() +{ + close(); +} + +OutputPrivate::OutputPrivate(QAudioOutputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioOutputPrivate*>(audio); +} + +OutputPrivate::~OutputPrivate() {} + +qint64 OutputPrivate::readData( char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + + return 0; +} + +qint64 OutputPrivate::writeData(const char* data, qint64 len) +{ + int retry = 0; + qint64 written = 0; + + if((audioDevice->deviceState == QAudio::ActiveState) + ||(audioDevice->deviceState == QAudio::IdleState)) { + qint64 l = len; + while(written < l) { + int chunk = audioDevice->write(data+written,(l-written)); + if(chunk <= 0) + retry++; + else + written+=chunk; + + if(retry > 10) + return written; + } + audioDevice->deviceState = QAudio::ActiveState; + } + return written; +} diff --git a/src/multimedia/audio/qaudiooutput_win32_p.h b/src/multimedia/audio/qaudiooutput_win32_p.h new file mode 100644 index 0000000..91f14f5 --- /dev/null +++ b/src/multimedia/audio/qaudiooutput_win32_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtMultimedia 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $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 other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOOUTPUTWIN_H +#define QAUDIOOUTPUTWIN_H + +#include <windows.h> +#include <mmsystem.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +static CRITICAL_SECTION waveOutCriticalSection; + +class QAudioOutputPrivate : public QAbstractAudioOutput +{ + Q_OBJECT +public: + QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioOutputPrivate(); + + qint64 write( const char *data, qint64 len ); + + QAudioFormat format() const; + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesFree() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 totalTime() const; + qint64 clock() const; + QAudio::Error error() const; + QAudio::State state() const; + + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private slots: + void feedback(); + bool deviceReady(); + +signals: + void stateChanged(QAudio::State); + void notify(); + +private: + QByteArray m_device; + bool resuming; + int bytesAvailable; + QTime timeStamp; + QTime timeStampOpened; + qint32 buffer_size; + qint32 period_size; + qint64 totalTimeValue; + bool pullMode; + int intervalTime; + static void CALLBACK waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ); + + WAVEHDR* allocateBlocks(int size, int count); + void freeBlocks(WAVEHDR* blockArray); + bool open(); + void close(); + + WAVEFORMATEX wfx; + HWAVEOUT hWaveOut; + MMRESULT result; + WAVEHDR header; + WAVEHDR* waveBlocks; + volatile int waveFreeBlockCount; + int waveCurrentBlock; + char* audioBuffer; +}; + +class OutputPrivate : public QIODevice +{ + Q_OBJECT +public: + OutputPrivate(QAudioOutputPrivate* audio); + ~OutputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + +private: + QAudioOutputPrivate *audioDevice; +}; + +#endif diff --git a/src/multimedia/multimedia.pro b/src/multimedia/multimedia.pro new file mode 100644 index 0000000..7d38afa --- /dev/null +++ b/src/multimedia/multimedia.pro @@ -0,0 +1,11 @@ +TARGET = QtMultimedia +QPRO_PWD = $$PWD +QT = core +DEFINES += QT_BUILD_MULTIMEDIA_LIB + +win32-msvc*:QMAKE_LIBS += $$QMAKE_LIBS_CORE + +unix:QMAKE_PKGCONFIG_REQUIRES = QtCore + +include(../qbase.pri) +include(audio/audio.pri) diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 75ab837..4c52545 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -87,58 +87,11 @@ QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate() delete []channels; } -void QHttpNetworkConnectionPrivate::connectSignals(QAbstractSocket *socket) -{ - Q_Q(QHttpNetworkConnection); - - QObject::connect(socket, SIGNAL(bytesWritten(qint64)), - q, SLOT(_q_bytesWritten(qint64)), - Qt::DirectConnection); - QObject::connect(socket, SIGNAL(connected()), - q, SLOT(_q_connected()), - Qt::DirectConnection); - QObject::connect(socket, SIGNAL(readyRead()), - q, SLOT(_q_readyRead()), - Qt::DirectConnection); - QObject::connect(socket, SIGNAL(disconnected()), - q, SLOT(_q_disconnected()), - Qt::DirectConnection); - QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), - q, SLOT(_q_error(QAbstractSocket::SocketError)), - Qt::DirectConnection); -#ifndef QT_NO_NETWORKPROXY - QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), - q, SLOT(_q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), - Qt::DirectConnection); -#endif - -#ifndef QT_NO_OPENSSL - QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); - if (sslSocket) { - // won't be a sslSocket if encrypt is false - QObject::connect(sslSocket, SIGNAL(encrypted()), - q, SLOT(_q_encrypted()), - Qt::DirectConnection); - QObject::connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)), - q, SLOT(_q_sslErrors(const QList<QSslError>&)), - Qt::DirectConnection); - } -#endif -} - void QHttpNetworkConnectionPrivate::init() { - for (int i = 0; i < channelCount; ++i) { -#ifndef QT_NO_OPENSSL - if (encrypt) - channels[i].socket = new QSslSocket; - else - channels[i].socket = new QTcpSocket; -#else - channels[i].socket = new QTcpSocket; -#endif - - connectSignals(channels[i].socket); + for (int i = 0; i < channelCount; i++) { + channels[i].setConnection(this->q_func()); + channels[i].init(); } } @@ -176,35 +129,6 @@ bool QHttpNetworkConnectionPrivate::isSocketReading(QAbstractSocket *socket) con return (i != -1 && (channels[i].state & QHttpNetworkConnectionChannel::ReadingState)); } -void QHttpNetworkConnectionPrivate::appendUncompressedData(QHttpNetworkReply &reply, QByteArray &qba) -{ - reply.d_func()->responseData.append(qba); - - // clear the original! helps with implicit sharing and - // avoiding memcpy when the user is reading the data - qba.clear(); -} - -void QHttpNetworkConnectionPrivate::appendUncompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data) -{ - reply.d_func()->responseData.append(data); - - // clear the original! helps with implicit sharing and - // avoiding memcpy when the user is reading the data - data.clear(); -} - -void QHttpNetworkConnectionPrivate::appendCompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data) -{ - // Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer - // instead of one QByteArray. - for(int i = 0; i < data.bufferCount(); i++) { - QByteArray &byteData = data[i]; - reply.d_func()->compressedData.append(byteData.constData(), byteData.size()); - } - data.clear(); -} - qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const { return reply.d_func()->responseData.byteAmount(); @@ -406,7 +330,7 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket) QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); if (uploadByteDevice) { // connect the signals so this function gets called again - QObject::connect(uploadByteDevice, SIGNAL(readyRead()), q, SLOT(_q_uploadDataReadyRead())); + QObject::connect(uploadByteDevice, SIGNAL(readyRead()), &channels[i], SLOT(_q_uploadDataReadyRead())); channels[i].bytesTotal = channels[i].request.contentLength(); } else { @@ -485,7 +409,7 @@ bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket) { QNonContiguousByteDevice* uploadByteDevice = channels[i].request.uploadByteDevice(); if (uploadByteDevice) { - QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), q, SLOT(_q_uploadDataReadyRead())); + QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), &channels[i], SLOT(_q_uploadDataReadyRead())); } // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called // this is needed if the sends an reply before we have finished sending the request. In that @@ -564,7 +488,7 @@ bool QHttpNetworkConnectionPrivate::expand(QAbstractSocket *socket, QHttpNetwork if (ret >= retCheck) { if (inflated.size()) { reply->d_func()->totalProgress += inflated.size(); - appendUncompressedData(*reply, inflated); + reply->d_func()->appendUncompressedReplyData(inflated); if (shouldEmitSignals(reply)) { // important: At the point of this readyRead(), inflated must be cleared, // else implicit sharing will trigger memcpy when the user is reading data! @@ -685,9 +609,9 @@ void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpN bytes = reply->d_func()->readBody(socket, &byteDatas); if (bytes) { if (reply->d_func()->autoDecompress) - appendCompressedData(*reply, byteDatas); + reply->d_func()->appendCompressedReplyData(byteDatas); else - appendUncompressedData(*reply, byteDatas); + reply->d_func()->appendUncompressedReplyData(byteDatas); if (!reply->d_func()->autoDecompress) { reply->d_func()->totalProgress += bytes; @@ -1004,11 +928,7 @@ void QHttpNetworkConnectionPrivate::unqueueAndSendRequest(QAbstractSocket *socke void QHttpNetworkConnectionPrivate::closeChannel(int channel) { - QAbstractSocket *socket = channels[channel].socket; - socket->blockSignals(true); - socket->close(); - socket->blockSignals(false); - channels[channel].state = QHttpNetworkConnectionChannel::IdleState; + channels[channel].close(); } void QHttpNetworkConnectionPrivate::resendCurrentRequest(QAbstractSocket *socket) @@ -1102,53 +1022,6 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply) } -//private slots -void QHttpNetworkConnectionPrivate::_q_readyRead() -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; // ### error - if (isSocketWaiting(socket) || isSocketReading(socket)) { - int i = indexOf(socket); - channels[i].state = QHttpNetworkConnectionChannel::ReadingState; - if (channels[i].reply) - receiveReply(socket, channels[i].reply); - } - // ### error -} - -void QHttpNetworkConnectionPrivate::_q_bytesWritten(qint64 bytes) -{ - Q_UNUSED(bytes); - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; // ### error - // bytes have been written to the socket. write even more of them :) - if (isSocketWriting(socket)) - sendRequest(socket); - // otherwise we do nothing -} - -void QHttpNetworkConnectionPrivate::_q_disconnected() -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; // ### error - // read the available data before closing - int i = indexOf(socket); - if (isSocketWaiting(socket) || isSocketReading(socket)) { - channels[i].state = QHttpNetworkConnectionChannel::ReadingState; - if (channels[i].reply) - receiveReply(socket, channels[i].reply); - } else if (channels[i].state == QHttpNetworkConnectionChannel::IdleState && channels[i].resendCurrent) { - // re-sending request because the socket was in ClosingState - QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection); - } - channels[i].state = QHttpNetworkConnectionChannel::IdleState; -} void QHttpNetworkConnectionPrivate::_q_startNextRequest() { @@ -1188,121 +1061,6 @@ void QHttpNetworkConnectionPrivate::_q_restartAuthPendingRequests() } } -void QHttpNetworkConnectionPrivate::_q_connected() -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; // ### error - - // improve performance since we get the request sent by the kernel ASAP - socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); - - int i = indexOf(socket); - // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! - //channels[i].reconnectAttempts = 2; - if (!channels[i].pendingEncrypt) { - channels[i].state = QHttpNetworkConnectionChannel::IdleState; - if (channels[i].reply) - sendRequest(socket); - else - closeChannel(i); - } -} - - -void QHttpNetworkConnectionPrivate::_q_error(QAbstractSocket::SocketError socketError) -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; - bool send2Reply = false; - int i = indexOf(socket); - QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError; - - switch (socketError) { - case QAbstractSocket::HostNotFoundError: - errorCode = QNetworkReply::HostNotFoundError; - break; - case QAbstractSocket::ConnectionRefusedError: - errorCode = QNetworkReply::ConnectionRefusedError; - break; - case QAbstractSocket::RemoteHostClosedError: - // try to reconnect/resend before sending an error. - // while "Reading" the _q_disconnected() will handle this. - if (channels[i].state != QHttpNetworkConnectionChannel::IdleState && channels[i].state != QHttpNetworkConnectionChannel::ReadingState) { - if (channels[i].reconnectAttempts-- > 0) { - resendCurrentRequest(socket); - return; - } else { - send2Reply = true; - errorCode = QNetworkReply::RemoteHostClosedError; - } - } else { - return; - } - break; - case QAbstractSocket::SocketTimeoutError: - // try to reconnect/resend before sending an error. - if (channels[i].state == QHttpNetworkConnectionChannel::WritingState && (channels[i].reconnectAttempts-- > 0)) { - resendCurrentRequest(socket); - return; - } - send2Reply = true; - errorCode = QNetworkReply::TimeoutError; - break; - case QAbstractSocket::ProxyAuthenticationRequiredError: - errorCode = QNetworkReply::ProxyAuthenticationRequiredError; - break; - case QAbstractSocket::SslHandshakeFailedError: - errorCode = QNetworkReply::SslHandshakeFailedError; - break; - default: - // all other errors are treated as NetworkError - errorCode = QNetworkReply::UnknownNetworkError; - break; - } - QPointer<QObject> that = q; - QString errorString = errorDetail(errorCode, socket); - if (send2Reply) { - if (channels[i].reply) { - channels[i].reply->d_func()->errorString = errorString; - // this error matters only to this reply - emit channels[i].reply->finishedWithError(errorCode, errorString); - } - // send the next request - QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection); - } else { - // the failure affects all requests. - emit q->error(errorCode, errorString); - } - if (that) //signals make enter the event loop - closeChannel(i); -} - -#ifndef QT_NO_NETWORKPROXY -void QHttpNetworkConnectionPrivate::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth) -{ - Q_Q(QHttpNetworkConnection); - emit q->proxyAuthenticationRequired(proxy, auth, q); -} -#endif - -void QHttpNetworkConnectionPrivate::_q_uploadDataReadyRead() -{ - Q_Q(QHttpNetworkConnection); - // upload data emitted readyRead() - // find out which channel it is for - QObject *sender = q->sender(); - - for (int i = 0; i < channelCount; ++i) { - if (sender == channels[i].request.uploadByteDevice()) { - sendRequest(channels[i].socket); - break; - } - } -} QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent) : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent) @@ -1397,27 +1155,6 @@ QNetworkProxy QHttpNetworkConnection::transparentProxy() const // SSL support below #ifndef QT_NO_OPENSSL -void QHttpNetworkConnectionPrivate::_q_encrypted() -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; // ### error - int i = indexOf(socket); - channels[i].state = QHttpNetworkConnectionChannel::IdleState; - sendRequest(socket); -} - -void QHttpNetworkConnectionPrivate::_q_sslErrors(const QList<QSslError> &errors) -{ - Q_Q(QHttpNetworkConnection); - QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender()); - if (!socket) - return; - //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure; - emit q->sslErrors(errors); -} - QSslConfiguration QHttpNetworkConnectionPrivate::sslConfiguration(const QHttpNetworkReply &reply) const { if (!encrypt) diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index db6a140..d6b0325 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -138,23 +138,10 @@ private: Q_DECLARE_PRIVATE(QHttpNetworkConnection) Q_DISABLE_COPY(QHttpNetworkConnection) friend class QHttpNetworkReply; + friend class QHttpNetworkConnectionChannel; - Q_PRIVATE_SLOT(d_func(), void _q_bytesWritten(qint64)) - Q_PRIVATE_SLOT(d_func(), void _q_readyRead()) - Q_PRIVATE_SLOT(d_func(), void _q_disconnected()) Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest()) Q_PRIVATE_SLOT(d_func(), void _q_restartAuthPendingRequests()) - Q_PRIVATE_SLOT(d_func(), void _q_connected()) - Q_PRIVATE_SLOT(d_func(), void _q_error(QAbstractSocket::SocketError)) -#ifndef QT_NO_NETWORKPROXY - Q_PRIVATE_SLOT(d_func(), void _q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)) -#endif - Q_PRIVATE_SLOT(d_func(), void _q_uploadDataReadyRead()) - -#ifndef QT_NO_OPENSSL - Q_PRIVATE_SLOT(d_func(), void _q_encrypted()) - Q_PRIVATE_SLOT(d_func(), void _q_sslErrors(const QList<QSslError>&)) -#endif }; @@ -169,7 +156,6 @@ public: QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt); ~QHttpNetworkConnectionPrivate(); void init(); - void connectSignals(QAbstractSocket *socket); enum { ChunkSize = 4096 }; @@ -189,18 +175,8 @@ public: void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy); // private slots - void _q_bytesWritten(qint64 bytes); // proceed sending - void _q_readyRead(); // pending data to read - void _q_disconnected(); // disconnected from host void _q_startNextRequest(); // send the next request from the queue void _q_restartAuthPendingRequests(); // send the currently blocked request - void _q_connected(); // start sending request - void _q_error(QAbstractSocket::SocketError); // error from socket -#ifndef QT_NO_NETWORKPROXY - void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy -#endif - - void _q_uploadDataReadyRead(); void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request); bool ensureConnection(QAbstractSocket *socket); @@ -221,10 +197,6 @@ public: bool pendingAuthSignal; // there is an incomplete authentication signal bool pendingProxyAuthSignal; // there is an incomplete proxy authentication signal - void appendUncompressedData(QHttpNetworkReply &reply, QByteArray &qba); - void appendUncompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data); - void appendCompressedData(QHttpNetworkReply &reply, QByteDataBuffer &data); - qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const; qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const; qint64 compressedBytesAvailable(const QHttpNetworkReply &reply) const; @@ -237,8 +209,6 @@ public: inline bool expectContent(QHttpNetworkReply *reply); #ifndef QT_NO_OPENSSL - void _q_encrypted(); // start sending request (https) - void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket QSslConfiguration sslConfiguration(const QHttpNetworkReply &reply) const; #endif @@ -249,6 +219,8 @@ public: //The request queues QList<HttpMessagePair> highPriorityQueue; QList<HttpMessagePair> lowPriorityQueue; + + friend class QHttpNetworkConnectionChannel; }; diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index a04b530..6cd46fd 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -57,6 +57,220 @@ QT_BEGIN_NAMESPACE // TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp +void QHttpNetworkConnectionChannel::init() +{ +#ifndef QT_NO_OPENSSL + if (connection->d_func()->encrypt) + socket = new QSslSocket; + else + socket = new QTcpSocket; +#else + socket = new QTcpSocket; +#endif + + QObject::connect(socket, SIGNAL(bytesWritten(qint64)), + this, SLOT(_q_bytesWritten(qint64)), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(connected()), + this, SLOT(_q_connected()), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(readyRead()), + this, SLOT(_q_readyRead()), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(disconnected()), + this, SLOT(_q_disconnected()), + Qt::DirectConnection); + QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(_q_error(QAbstractSocket::SocketError)), + Qt::DirectConnection); +#ifndef QT_NO_NETWORKPROXY + QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), + this, SLOT(_q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)), + Qt::DirectConnection); +#endif + +#ifndef QT_NO_OPENSSL + QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket); + if (sslSocket) { + // won't be a sslSocket if encrypt is false + QObject::connect(sslSocket, SIGNAL(encrypted()), + this, SLOT(_q_encrypted()), + Qt::DirectConnection); + QObject::connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)), + this, SLOT(_q_sslErrors(const QList<QSslError>&)), + Qt::DirectConnection); + } +#endif +} + + +void QHttpNetworkConnectionChannel::close() +{ + socket->blockSignals(true); + socket->close(); + socket->blockSignals(false); + state = QHttpNetworkConnectionChannel::IdleState; +} + + +//private slots +void QHttpNetworkConnectionChannel::_q_readyRead() +{ + if (!socket) + return; // ### error + if (connection->d_func()->isSocketWaiting(socket) || connection->d_func()->isSocketReading(socket)) { + state = QHttpNetworkConnectionChannel::ReadingState; + if (reply) + connection->d_func()->receiveReply(socket, reply); + } + // ### error +} + +void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes) +{ + Q_UNUSED(bytes); + if (!socket) + return; // ### error + // bytes have been written to the socket. write even more of them :) + if (connection->d_func()->isSocketWriting(socket)) + connection->d_func()->sendRequest(socket); + // otherwise we do nothing +} + +void QHttpNetworkConnectionChannel::_q_disconnected() +{ + if (!socket) + return; // ### error + // read the available data before closing + if (connection->d_func()->isSocketWaiting(socket) || connection->d_func()->isSocketReading(socket)) { + state = QHttpNetworkConnectionChannel::ReadingState; + if (reply) + connection->d_func()->receiveReply(socket, reply); + } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) { + // re-sending request because the socket was in ClosingState + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); + } + state = QHttpNetworkConnectionChannel::IdleState; +} + + +void QHttpNetworkConnectionChannel::_q_connected() +{ + if (!socket) + return; // ### error + + // improve performance since we get the request sent by the kernel ASAP + socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); + + // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again! + //channels[i].reconnectAttempts = 2; + if (!pendingEncrypt) { + state = QHttpNetworkConnectionChannel::IdleState; + if (reply) + connection->d_func()->sendRequest(socket); + else + close(); + } +} + + +void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError) +{ + if (!socket) + return; + bool send2Reply = false; + QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError; + + switch (socketError) { + case QAbstractSocket::HostNotFoundError: + errorCode = QNetworkReply::HostNotFoundError; + break; + case QAbstractSocket::ConnectionRefusedError: + errorCode = QNetworkReply::ConnectionRefusedError; + break; + case QAbstractSocket::RemoteHostClosedError: + // try to reconnect/resend before sending an error. + // while "Reading" the _q_disconnected() will handle this. + if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) { + if (reconnectAttempts-- > 0) { + connection->d_func()->resendCurrentRequest(socket); + return; + } else { + send2Reply = true; + errorCode = QNetworkReply::RemoteHostClosedError; + } + } else { + return; + } + break; + case QAbstractSocket::SocketTimeoutError: + // try to reconnect/resend before sending an error. + if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) { + connection->d_func()->resendCurrentRequest(socket); + return; + } + send2Reply = true; + errorCode = QNetworkReply::TimeoutError; + break; + case QAbstractSocket::ProxyAuthenticationRequiredError: + errorCode = QNetworkReply::ProxyAuthenticationRequiredError; + break; + case QAbstractSocket::SslHandshakeFailedError: + errorCode = QNetworkReply::SslHandshakeFailedError; + break; + default: + // all other errors are treated as NetworkError + errorCode = QNetworkReply::UnknownNetworkError; + break; + } + QPointer<QObject> that = connection; + QString errorString = connection->d_func()->errorDetail(errorCode, socket); + if (send2Reply) { + if (reply) { + reply->d_func()->errorString = errorString; + // this error matters only to this reply + emit reply->finishedWithError(errorCode, errorString); + } + // send the next request + QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection); + } else { + // the failure affects all requests. + emit connection->error(errorCode, errorString); + } + if (that) //signal emission triggered event loop + close(); +} + +#ifndef QT_NO_NETWORKPROXY +void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth) +{ + emit connection->proxyAuthenticationRequired(proxy, auth, connection); +} +#endif + +void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead() +{ + connection->d_func()->sendRequest(socket); +} + +#ifndef QT_NO_OPENSSL +void QHttpNetworkConnectionChannel::_q_encrypted() +{ + if (!socket) + return; // ### error + state = QHttpNetworkConnectionChannel::IdleState; + connection->d_func()->sendRequest(socket); +} + +void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors) +{ + if (!socket) + return; + //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure; + emit connection->sslErrors(errors); +} +#endif + QT_END_NAMESPACE #include "moc_qhttpnetworkconnectionchannel_p.cpp" diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index cbabc67..013e7a5 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -80,6 +80,7 @@ QT_BEGIN_NAMESPACE class QHttpNetworkRequest; class QHttpNetworkReply; class QByteArray; +class QHttpNetworkConnection; class QHttpNetworkConnectionChannel : public QObject { Q_OBJECT @@ -117,7 +118,31 @@ public: #ifndef QT_NO_OPENSSL , ignoreAllSslErrors(false) #endif + , connection(0) {} + + void setConnection(QHttpNetworkConnection *c) {connection = c;} + QHttpNetworkConnection *connection; + + void init(); + void close(); + + protected slots: + void _q_bytesWritten(qint64 bytes); // proceed sending + void _q_readyRead(); // pending data to read + void _q_disconnected(); // disconnected from host + void _q_connected(); // start sending request + void _q_error(QAbstractSocket::SocketError); // error from socket +#ifndef QT_NO_NETWORKPROXY + void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy +#endif + + void _q_uploadDataReadyRead(); + +#ifndef QT_NO_OPENSSL + void _q_encrypted(); // start sending request (https) + void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket +#endif }; diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index a623999..f40f7bf 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -687,6 +687,36 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize) return bytes; } +void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteArray &qba) +{ + responseData.append(qba); + + // clear the original! helps with implicit sharing and + // avoiding memcpy when the user is reading the data + qba.clear(); +} + +void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteDataBuffer &data) +{ + responseData.append(data); + + // clear the original! helps with implicit sharing and + // avoiding memcpy when the user is reading the data + data.clear(); +} + +void QHttpNetworkReplyPrivate::appendCompressedReplyData(QByteDataBuffer &data) +{ + // Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer + // instead of one QByteArray. + for(int i = 0; i < data.bufferCount(); i++) { + QByteArray &byteData = data[i]; + compressedData.append(byteData.constData(), byteData.size()); + } + data.clear(); +} + + // SSL support below #ifndef QT_NO_OPENSSL diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 575e824..fe49799 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -149,6 +149,7 @@ private: Q_DECLARE_PRIVATE(QHttpNetworkReply) friend class QHttpNetworkConnection; friend class QHttpNetworkConnectionPrivate; + friend class QHttpNetworkConnectionChannel; }; @@ -171,6 +172,10 @@ public: qint64 readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out); qint64 getChunkSize(QIODevice *in, qint64 *chunkSize); + void appendUncompressedReplyData(QByteArray &qba); + void appendUncompressedReplyData(QByteDataBuffer &data); + void appendCompressedReplyData(QByteDataBuffer &data); + qint64 bytesAvailable() const; bool isChunked(); bool connectionCloseEnabled(); diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 61e5601..e1d6634 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -121,7 +121,9 @@ static void ensureInitialized() as well as meta-data (headers, etc.). \note After the request has finished, it is the responsibility of the user - to delete the QNetworkReply object at an appropriate time. + to delete the QNetworkReply object at an appropriate time. Do not directly + delete it inside the slot connected to finished(). You can use the + deleteLater() function. A more involved example, assuming the manager is already existent, can be: @@ -202,6 +204,9 @@ static void ensureInitialized() See QNetworkReply::finished() for information on the status that the object will be in. + \note Do not delete the \a reply object in the slot connected to this + signal. Use deleteLater(). + \sa QNetworkReply::finished(), QNetworkReply::error() */ diff --git a/src/network/access/qnetworkcookiejar.h b/src/network/access/qnetworkcookiejar.h index b2cdf12..fae0857 100644 --- a/src/network/access/qnetworkcookiejar.h +++ b/src/network/access/qnetworkcookiejar.h @@ -43,6 +43,10 @@ #define QNETWORKCOOKIEJAR_H #include <QtCore/QObject> +#include <QtCore/QUrl> + +// ### Qt5 remove this include +#include "qnetworkcookie.h" QT_BEGIN_HEADER @@ -50,8 +54,6 @@ QT_BEGIN_NAMESPACE QT_MODULE(Network) -class QNetworkCookie; - class QNetworkCookieJarPrivate; class Q_NETWORK_EXPORT QNetworkCookieJar: public QObject { diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp index 3abf927..f6649b6 100644 --- a/src/network/access/qnetworkreply.cpp +++ b/src/network/access/qnetworkreply.cpp @@ -87,6 +87,9 @@ QNetworkReplyPrivate::QNetworkReplyPrivate() indicates the progress of the upload for operations that have such content. + \note Do not delete the object in the slot connected to the + error() or finished() signal. Use deleteLater(). + \sa QNetworkRequest, QNetworkAccessManager */ @@ -232,6 +235,9 @@ QNetworkReplyPrivate::QNetworkReplyPrivate() QNetworkAccessManager::finished() where that signal's reply parameter is this object. + \note Do not delete the object in the slot connected to this + signal. Use deleteLater(). + \sa QNetworkAccessManager::finished() */ @@ -246,6 +252,9 @@ QNetworkReplyPrivate::QNetworkReplyPrivate() detected. Call errorString() to obtain a textual representation of the error condition. + \note Do not delete the object in the slot connected to this + signal. Use deleteLater(). + \sa error(), errorString() */ diff --git a/src/network/socket/qnet_unix_p.h b/src/network/socket/qnet_unix_p.h index f38c2fc..040b3ec 100644 --- a/src/network/socket/qnet_unix_p.h +++ b/src/network/socket/qnet_unix_p.h @@ -85,7 +85,7 @@ static inline int qt_safe_socket(int domain, int type, int protocol, int flags = Q_ASSERT((flags & ~O_NONBLOCK) == 0); register int fd; -#ifdef SOCK_CLOEXEC +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) int newtype = type | SOCK_CLOEXEC; if (flags & O_NONBLOCK) newtype |= SOCK_NONBLOCK; diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro index c92b8cf..868484e 100644 --- a/src/opengl/opengl.pro +++ b/src/opengl/opengl.pro @@ -20,6 +20,7 @@ HEADERS += qgl.h \ qgl_p.h \ qglcolormap.h \ qglpixelbuffer.h \ + qglpixelbuffer_p.h \ qglframebufferobject.h \ qglextensions_p.h diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index edda6b6..0631df5 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -86,6 +86,7 @@ #include <private/qpixmapdata_gl_p.h> #include <private/qglpixelbuffer_p.h> #include <private/qwindowsurface_gl_p.h> +#include <private/qimagepixmapcleanuphooks_p.h> #include "qcolormap.h" #include "qfile.h" #include "qlibrary.h" @@ -526,7 +527,7 @@ void QGLFormat::setAccum(bool enable) \fn bool QGLFormat::stencil() const Returns true if the stencil buffer is enabled; otherwise returns - false. The stencil buffer is disabled by default. + false. The stencil buffer is enabled by default. \sa setStencil(), setStencilBufferSize() */ @@ -535,7 +536,7 @@ void QGLFormat::setAccum(bool enable) If \a enable is true enables the stencil buffer; otherwise disables the stencil buffer. - The stencil buffer is disabled by default. + The stencil buffer is enabled by default. The stencil buffer masks certain parts of the drawing area so that masked parts are not drawn on. @@ -1407,15 +1408,17 @@ QGLTextureCache::QGLTextureCache() { Q_ASSERT(qt_gl_texture_cache == 0); qt_gl_texture_cache = this; - qt_pixmap_cleanup_hook_64 = cleanupHook; - qt_image_cleanup_hook_64 = cleanupHook; + + QImagePixmapCleanupHooks::instance()->addPixmapHook(pixmapCleanupHook); + QImagePixmapCleanupHooks::instance()->addImageHook(imageCleanupHook); } QGLTextureCache::~QGLTextureCache() { qt_gl_texture_cache = 0; - qt_pixmap_cleanup_hook_64 = 0; - qt_image_cleanup_hook_64 = 0; + + QImagePixmapCleanupHooks::instance()->removePixmapHook(pixmapCleanupHook); + QImagePixmapCleanupHooks::instance()->removeImageHook(imageCleanupHook); } void QGLTextureCache::insert(QGLContext* ctx, qint64 key, QGLTexture* texture, int cost) @@ -1471,7 +1474,7 @@ QGLTextureCache* QGLTextureCache::instance() a hook that removes textures from the cache when a pixmap/image is deref'ed */ -void QGLTextureCache::cleanupHook(qint64 cacheKey) +void QGLTextureCache::imageCleanupHook(qint64 cacheKey) { // ### remove when the GL texture cache becomes thread-safe if (qApp->thread() != QThread::currentThread()) @@ -1481,6 +1484,24 @@ void QGLTextureCache::cleanupHook(qint64 cacheKey) instance()->remove(cacheKey); } + +void QGLTextureCache::pixmapCleanupHook(QPixmap* pixmap) +{ + // ### remove when the GL texture cache becomes thread-safe + if (qApp->thread() == QThread::currentThread()) { + const qint64 cacheKey = pixmap->cacheKey(); + QGLTexture *texture = instance()->getTexture(cacheKey); + if (texture && texture->clean) + instance()->remove(cacheKey); + } +#if defined(Q_WS_X11) + QPixmapData *pd = pixmap->data_ptr(); + // Only need to delete the gl surface if the pixmap is about to be deleted + if (pd->ref == 0) + QGLContextPrivate::destroyGlSurfaceForPixmap(pd); +#endif +} + void QGLTextureCache::deleteIfEmpty() { if (instance()->size() == 0) @@ -2021,11 +2042,11 @@ QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, #if defined(Q_WS_X11) // Try to use texture_from_pixmap if (pd->classId() == QPixmapData::X11Class) { - QPixmap *thatPixmap = const_cast<QPixmap*>(&pixmap); - texture = bindTextureFromNativePixmap(thatPixmap, key, canInvert); + texture = bindTextureFromNativePixmap(pd, key, canInvert); if (texture) { texture->clean = clean; - boundPixmaps.insert(thatPixmap->data_ptr(), QPixmap(pixmap)); + texture->boundPixmap = pd; + boundPixmaps.insert(pd, QPixmap(pixmap)); } } #endif @@ -4583,7 +4604,7 @@ QGLFormat QGLDrawable::format() const GLuint QGLDrawable::bindTexture(const QImage &image, GLenum target, GLint format) { - QGLTexture *texture; + QGLTexture *texture = 0; if (widget) texture = widget->d_func()->glcx->d_func()->bindTexture(image, target, format, true); else if (buffer) @@ -4599,7 +4620,7 @@ GLuint QGLDrawable::bindTexture(const QImage &image, GLenum target, GLint format GLuint QGLDrawable::bindTexture(const QPixmap &pixmap, GLenum target, GLint format) { - QGLTexture *texture; + QGLTexture *texture = 0; if (widget) texture = widget->d_func()->glcx->d_func()->bindTexture(pixmap, target, format, true, true); else if (buffer) @@ -4697,6 +4718,7 @@ void QGLShareRegister::removeShare(const QGLContext *context) { int count = it.value().removeAll(context); Q_ASSERT(count == 1); + Q_UNUSED(count); Q_ASSERT(it.value().size() != 0); if (it.value().size() == 1) diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 85dae0d..f21ab93 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -241,7 +241,9 @@ public: quint32 gpm; int screen; QHash<QPixmapData*, QPixmap> boundPixmaps; - QGLTexture *bindTextureFromNativePixmap(QPixmap *pm, const qint64 key, bool internal); + QGLTexture *bindTextureFromNativePixmap(QPixmapData*, const qint64 key, bool canInvert); + static void destroyGlSurfaceForPixmap(QPixmapData*); + static void unbindPixmapFromTexture(QPixmapData*); #endif #if defined(Q_WS_MAC) bool update; @@ -423,7 +425,8 @@ public: // is a current context - the context the pixmap was bound to a texture in. // Otherwise the release doesn't do anything and you get BadDrawable errors // when you come to delete the context. - deleteBoundPixmap(); + if (boundPixmap) + QGLContextPrivate::unbindPixmapFromTexture(boundPixmap); #endif glDeleteTextures(1, &id); if (switch_context && current) @@ -437,9 +440,9 @@ public: bool clean; bool yInverted; // NOTE: Y-Inverted textures are for internal use only! #if defined(Q_WS_X11) - Qt::HANDLE boundPixmap; - void deleteBoundPixmap(); // in qgl_x11.cpp/qgl_x11egl.cpp + QPixmapData* boundPixmap; #endif + }; class QGLTextureCache { @@ -458,7 +461,8 @@ public: static QGLTextureCache *instance(); static void deleteIfEmpty(); - static void cleanupHook(qint64 cacheKey); + static void imageCleanupHook(qint64 cacheKey); + static void pixmapCleanupHook(QPixmap* pixmap); private: QCache<qint64, QGLTexture> m_cache; diff --git a/src/opengl/qgl_x11.cpp b/src/opengl/qgl_x11.cpp index cf70991..4509a64 100644 --- a/src/opengl/qgl_x11.cpp +++ b/src/opengl/qgl_x11.cpp @@ -58,11 +58,16 @@ #include <private/qglpixelbuffer_p.h> #endif +// We always define GLX_EXT_texture_from_pixmap ourselves because +// we can't trust system headers to do it properly +#define GLX_EXT_texture_from_pixmap 1 + #define INT8 dummy_INT8 #define INT32 dummy_INT32 #include <GL/glx.h> #undef INT8 #undef INT32 + #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/Xos.h> @@ -90,7 +95,7 @@ extern const QX11Info *qt_x11Info(const QPaintDevice *pd); #define GLX_SAMPLES_ARB 100001 #endif -#ifndef GLX_EXT_texture_from_pixmap +#ifndef GLX_TEXTURE_2D_BIT_EXT #define GLX_TEXTURE_2D_BIT_EXT 0x00000002 #define GLX_TEXTURE_RECTANGLE_BIT_EXT 0x00000004 #define GLX_BIND_TO_TEXTURE_RGB_EXT 0x20D0 @@ -1548,38 +1553,26 @@ void QGLExtensions::init() } } -#if !defined(glXBindTexImageEXT) +// Solaris defines glXBindTexImageEXT as part of the GL library +#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) typedef void (*qt_glXBindTexImageEXT)(Display*, GLXDrawable, int, const int*); -static qt_glXBindTexImageEXT glXBindTexImageEXT = 0; -#endif -#if !defined(glXReleaseTexImageEXT) typedef void (*qt_glXReleaseTexImageEXT)(Display*, GLXDrawable, int); +static qt_glXBindTexImageEXT glXBindTexImageEXT = 0; static qt_glXReleaseTexImageEXT glXReleaseTexImageEXT = 0; -#endif -static bool qt_resolved_texture_from_pixmap = false; -QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pm, const qint64 key, bool canInvert) +bool qt_resolveTextureFromPixmap() { -#if !defined(Q_OS_LINUX) - return 0; -#else - Q_Q(QGLContext); - - if (pm->data_ptr()->classId() != QPixmapData::X11Class) - return 0; - QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pm->data_ptr()); - const QX11Info *x11Info = qt_x11Info(pm); + static bool resolvedTextureFromPixmap = false; + if (!resolvedTextureFromPixmap) { + resolvedTextureFromPixmap = true; - // Check to see if we have NPOT texture support - // TODO: Use GLX_TEXTURE_RECTANGLE_EXT texture target on systems without npot textures - if ( !(QGLExtensions::glExtensions & QGLExtensions::NPOTTextures) && - !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0)) - return 0; - - - if (!qt_resolved_texture_from_pixmap) { - qt_resolved_texture_from_pixmap = true; + // Check to see if we have NPOT texture support + if ( !(QGLExtensions::glExtensions & QGLExtensions::NPOTTextures) && + !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0)) + { + return false; // Can't use TFP without NPOT + } QString glxExt = QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS)); if (glxExt.contains(QLatin1String("GLX_EXT_texture_from_pixmap"))) { @@ -1601,81 +1594,139 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pm, const qi } } - if (!glXBindTexImageEXT) - return 0; + return glXBindTexImageEXT && glXReleaseTexImageEXT; +} +#endif //defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) + +QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData *pmd, const qint64 key, bool canInvert) +{ #if !defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX) return 0; #else - GLXFBConfig *configList = 0; - GLXFBConfig glxPixmapConfig; - int configCount = 0; - bool hasAlpha = pixmapData->hasAlphaChannel(); - - int configAttribs[] = { - hasAlpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT : GLX_BIND_TO_TEXTURE_RGB_EXT, True, - GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, - GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, - // QGLContext::bindTexture() can't return an inverted texture, but QPainter::drawPixmap() can: - GLX_Y_INVERTED_EXT, canInvert ? GLX_DONT_CARE : False, - XNone -// GLX_BIND_TO_MIPMAP_TEXTURE_EXT, False, -// GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_1D_BIT_EXT or GLX_TEXTURE_2D_BIT_EXT or GLX_TEXTURE_RECTANGLE_BIT_EXT - }; - configList = glXChooseFBConfig(x11Info->display(), x11Info->screen(), configAttribs, &configCount); - if (!configList) - return 0; - glxPixmapConfig = configList[0]; - XFree(configList); - - GLXPixmap glxPixmap; - int pixmapAttribs[] = { - GLX_TEXTURE_FORMAT_EXT, hasAlpha ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT, - GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, - GLX_MIPMAP_TEXTURE_EXT, False, - XNone -// GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT or GLX_TEXTURE_FORMAT_RGB_EXT or GLX_TEXTURE_FORMAT_NONE_EXT, -// GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT or GLX_TEXTURE_RECTANGLE_EXT, -// GLX_MIPMAP_TEXTURE_EXT, True or False, - }; + Q_Q(QGLContext); - // Wrap the X Pixmap into a GLXPixmap: - glxPixmap = glXCreatePixmap(x11Info->display(), glxPixmapConfig, pixmapData->handle(), pixmapAttribs); + Q_ASSERT(pmd->classId() == QPixmapData::X11Class); - if (!glxPixmap) + if (!qt_resolveTextureFromPixmap()) return 0; - int yInverted; - glXGetFBConfigAttrib(x11Info->display(), glxPixmapConfig, GLX_Y_INVERTED_EXT, &yInverted); + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd); + const QX11Info &x11Info = pixmapData->xinfo; + + // Store the configs (Can be static because configs aren't dependent on current context) + static GLXFBConfig glxRGBPixmapConfig = 0; + static bool RGBConfigInverted = false; + static GLXFBConfig glxRGBAPixmapConfig = 0; + static bool RGBAConfigInverted = false; + + bool hasAlpha = pixmapData->hasAlphaChannel(); + + // Check to see if we need a config + if ( (hasAlpha && !glxRGBAPixmapConfig) || (!hasAlpha && !glxRGBPixmapConfig) ) { + GLXFBConfig *configList = 0; + int configCount = 0; + + int configAttribs[] = { + hasAlpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT : GLX_BIND_TO_TEXTURE_RGB_EXT, True, + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, + GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, + // QGLContext::bindTexture() can't return an inverted texture, but QPainter::drawPixmap() can: + GLX_Y_INVERTED_EXT, canInvert ? GLX_DONT_CARE : False, + XNone + }; + configList = glXChooseFBConfig(x11Info.display(), x11Info.screen(), configAttribs, &configCount); + if (!configList) + return 0; + + int yInv; + glXGetFBConfigAttrib(x11Info.display(), configList[0], GLX_Y_INVERTED_EXT, &yInv); + + if (hasAlpha) { + glxRGBAPixmapConfig = configList[0]; + RGBAConfigInverted = yInv; + } + else { + glxRGBPixmapConfig = configList[0]; + RGBConfigInverted = yInv; + } + + XFree(configList); + } + + // Check to see if the surface is still valid + if (pixmapData->gl_surface && + hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) + { + // Surface is invalid! + destroyGlSurfaceForPixmap(pixmapData); + } + + // Check to see if we need a surface + if (!pixmapData->gl_surface) { + GLXPixmap glxPixmap; + int pixmapAttribs[] = { + GLX_TEXTURE_FORMAT_EXT, hasAlpha ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT, + GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, + GLX_MIPMAP_TEXTURE_EXT, False, // Maybe needs to be don't care + XNone + }; + + // Wrap the X Pixmap into a GLXPixmap: + glxPixmap = glXCreatePixmap(x11Info.display(), + hasAlpha ? glxRGBAPixmapConfig : glxRGBPixmapConfig, + pixmapData->handle(), pixmapAttribs); + + if (!glxPixmap) + return 0; + + pixmapData->gl_surface = (Qt::HANDLE)glxPixmap; + + // Make sure the cleanup hook gets called so we can delete the glx pixmap + pixmapData->is_cached = true; + } GLuint textureId; glGenTextures(1, &textureId); glBindTexture(GL_TEXTURE_2D, textureId); - glXBindTexImageEXT(x11Info->display(), glxPixmap, GLX_FRONT_LEFT_EXT, 0); + glXBindTexImageEXT(x11Info.display(), (GLXPixmap)pixmapData->gl_surface, GLX_FRONT_LEFT_EXT, 0); glBindTexture(GL_TEXTURE_2D, textureId); - QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, canInvert, yInverted); - texture->boundPixmap = glxPixmap; + QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, canInvert, false); + texture->yInverted = (hasAlpha && RGBAConfigInverted) || (!hasAlpha && RGBConfigInverted); + if (texture->yInverted) + pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture; // We assume the cost of bound pixmaps is zero QGLTextureCache::instance()->insert(q, key, texture, 0); return texture; #endif //!defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX) -#endif //!defined(Q_OS_LINUX } -void QGLTexture::deleteBoundPixmap() + +void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd) { -#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) && defined(Q_OS_LINUX) - if (boundPixmap) { - glXReleaseTexImageEXT(QX11Info::display(), boundPixmap, GLX_FRONT_LEFT_EXT); - glXDestroyPixmap(QX11Info::display(), boundPixmap); - boundPixmap = 0; +#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) + Q_ASSERT(pmd->classId() == QPixmapData::X11Class); + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd); + if (pixmapData->gl_surface) { + glXDestroyPixmap(QX11Info::display(), (GLXPixmap)pixmapData->gl_surface); + pixmapData->gl_surface = 0; } #endif } +void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd) +{ +#if defined(GLX_VERSION_1_3) && !defined(Q_OS_HPUX) + Q_ASSERT(pmd->classId() == QPixmapData::X11Class); + Q_ASSERT(QGLContext::currentContext()); + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd); + if (pixmapData->gl_surface) + glXReleaseTexImageEXT(QX11Info::display(), (GLXPixmap)pixmapData->gl_surface, GLX_FRONT_LEFT_EXT); +#endif +} QT_END_NAMESPACE diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index c6904fe..ed9930f 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -41,15 +41,16 @@ #include "qgl.h" #include <private/qt_x11_p.h> +#include <private/qpixmap_x11_p.h> #include <private/qgl_p.h> #include <private/qpaintengine_opengl_p.h> #include "qgl_egl_p.h" #include "qcolormap.h" +#include <QDebug> QT_BEGIN_NAMESPACE - bool QGLFormat::hasOpenGL() { return true; @@ -298,6 +299,7 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, int matchingCount = 0; chosenVisualInfo = XGetVisualInfo(x11Info().display(), VisualIDMask, &vi, &matchingCount); if (chosenVisualInfo) { +#if !defined(QT_NO_XRENDER) if (useArgbVisual) { // Check to make sure the visual provided by EGL is ARGB XRenderPictFormat *format; @@ -311,8 +313,9 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, nativeVisualId, (int)qeglCtx->config()); vi.visualid = 0; } - } - else { + } else +#endif + { qDebug("Using opaque X Visual ID (%d) provided by EGL", (int)vi.visualid); vi = *chosenVisualInfo; } @@ -486,15 +489,175 @@ void QGLWidgetPrivate::recreateEglSurface(bool force) } } -QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pm, const qint64 key, bool canInvert) + +QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, const qint64 key, bool canInvert) { - // TODO - return 0; + Q_Q(QGLContext); + + Q_ASSERT(pd->classId() == QPixmapData::X11Class); + + static bool checkedForTFP = false; + static bool haveTFP = false; + + if (!checkedForTFP) { + // Check for texture_from_pixmap egl extension + checkedForTFP = true; + if (eglContext->hasExtension("EGL_NOKIA_texture_from_pixmap") || + eglContext->hasExtension("EGL_EXT_texture_from_pixmap")) + { + qDebug("Found texture_from_pixmap EGL extension!"); + haveTFP = true; + } + } + + if (!haveTFP) + return 0; + + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pd); + + bool hasAlpha = pixmapData->hasAlphaChannel(); + + // Check to see if the surface is still valid + if (pixmapData->gl_surface && + hasAlpha != (pixmapData->flags & QX11PixmapData::GlSurfaceCreatedWithAlpha)) + { + // Surface is invalid! + destroyGlSurfaceForPixmap(pixmapData); + } + + EGLint pixmapAttribs[] = { + EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, + EGL_TEXTURE_FORMAT, hasAlpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB, + EGL_NONE + }; + Q_ASSERT(sizeof(Qt::HANDLE) >= sizeof(EGLSurface)); // Just to make totally sure! + if (pixmapData->gl_surface == 0) + pixmapData->gl_surface = (Qt::HANDLE)EGL_NO_SURFACE; + EGLSurface pixmapSurface = (EGLSurface)pixmapData->gl_surface; + static EGLConfig pixmapRGBConfig = 0; + static EGLConfig pixmapRGBAConfig = 0; + + // Check to see if we need to find a config + if ((hasAlpha && !pixmapRGBAConfig) || (!hasAlpha && !pixmapRGBConfig) ) { + const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PIXMAP_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_DEPTH_SIZE, 0, + hasAlpha ? EGL_BIND_TO_TEXTURE_RGBA : EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE, + EGL_NONE + }; + + EGLint configCount = 0; + eglChooseConfig(eglContext->display(), configAttribs, 0, 256, &configCount); + if (configCount == 0) { + haveTFP = false; + qWarning("bindTextureFromNativePixmap() - Couldn't find a suitable config"); + return 0; + } + + EGLConfig *configList = new EGLConfig[configCount]; + eglChooseConfig(eglContext->display(), configAttribs, configList, configCount, &configCount); + Q_ASSERT(configCount); + + // Try to create a pixmap surface for each config until one works + for (int i = 0; i < configCount; ++i) { + pixmapSurface = eglCreatePixmapSurface(eglContext->display(), configList[i], + (EGLNativePixmapType) pixmapData->handle(), + pixmapAttribs); + if (pixmapSurface != EGL_NO_SURFACE) { + // Got one! + qDebug() << "Found an" << (hasAlpha ? "ARGB" : "RGB") + << "config (" << int(configList[i]) << ") to create a pixmap surface"; + if (hasAlpha) + pixmapRGBAConfig = configList[i]; + else + pixmapRGBConfig = configList[i]; + pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface; + break; + } + } + delete configList; + + if ((hasAlpha && !pixmapRGBAConfig) || (!hasAlpha && !pixmapRGBConfig) ) { + qDebug("Couldn't create a pixmap surface with any of the provided configs"); + haveTFP = false; + return 0; + } + } + + if (pixmapSurface == EGL_NO_SURFACE) { + pixmapSurface = eglCreatePixmapSurface(eglContext->display(), + hasAlpha? pixmapRGBAConfig : pixmapRGBConfig, + (EGLNativePixmapType) pixmapData->handle(), + pixmapAttribs); + if (pixmapSurface == EGL_NO_SURFACE) { + qWarning("Failed to create a pixmap surface using config %d", + (int)(hasAlpha? pixmapRGBAConfig : pixmapRGBConfig)); + haveTFP = false; + return 0; + } + pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface; + } + + // Make sure the cleanup hook gets called so we can delete the glx pixmap + pixmapData->is_cached = true; + Q_ASSERT(pixmapData->gl_surface); + + GLuint textureId; + glGenTextures(1, &textureId); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textureId); + + // bind the egl pixmap surface to a texture + EGLBoolean success; + success = eglBindTexImage(eglContext->display(), pixmapSurface, EGL_BACK_BUFFER); + if (success == EGL_FALSE) { + qWarning() << "eglBindTexImage() failed:" << eglContext->errorString(eglGetError()); + eglDestroySurface(eglContext->display(), pixmapSurface); + pixmapData->gl_surface = (Qt::HANDLE)EGL_NO_SURFACE; + haveTFP = false; + return 0; + } + + QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, canInvert, true); + pixmapData->flags |= QX11PixmapData::InvertedWhenBoundToTexture; + + // We assume the cost of bound pixmaps is zero + QGLTextureCache::instance()->insert(q, key, texture, 0); + + glBindTexture(GL_TEXTURE_2D, textureId); + return texture; +} + +void QGLContextPrivate::destroyGlSurfaceForPixmap(QPixmapData* pmd) +{ + Q_ASSERT(pmd->classId() == QPixmapData::X11Class); + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd); + if (pixmapData->gl_surface) { + EGLBoolean success; + success = eglDestroySurface(QEglContext::defaultDisplay(0), (EGLSurface)pixmapData->gl_surface); + if (success == EGL_FALSE) { + qWarning() << "destroyGlSurfaceForPixmap() - Error deleting surface: " + << QEglContext::errorString(eglGetError()); + } + pixmapData->gl_surface = 0; + } } -void QGLTexture::deleteBoundPixmap() +void QGLContextPrivate::unbindPixmapFromTexture(QPixmapData* pmd) { - //TODO + Q_ASSERT(pmd->classId() == QPixmapData::X11Class); + QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pmd); + if (pixmapData->gl_surface) { + EGLBoolean success; + success = eglReleaseTexImage(QEglContext::defaultDisplay(0), + (EGLSurface)pixmapData->gl_surface, + EGL_BACK_BUFFER); + if (success == EGL_FALSE) { + qWarning() << "unbindPixmapFromTexture() - Unable to release bound texture: " + << QEglContext::errorString(eglGetError()); + } + } } QT_END_NAMESPACE diff --git a/src/opengl/qglcolormap.cpp b/src/opengl/qglcolormap.cpp index 426e090..481089a 100644 --- a/src/opengl/qglcolormap.cpp +++ b/src/opengl/qglcolormap.cpp @@ -174,13 +174,14 @@ void QGLColormap::setEntry(int idx, QRgb color) detach(); if (!d->cells) d->cells = new QVector<QRgb>(256); - d->cells->insert(idx, color); + d->cells->replace(idx, color); } /*! Set an array of cells in this colormap. \a count is the number of colors that should be set, \a colors is the array of colors, and - \a base is the starting index. + \a base is the starting index. The first element in \a colors + is set at \a base in the colormap. */ void QGLColormap::setEntries(int count, const QRgb *colors, int base) { @@ -188,10 +189,10 @@ void QGLColormap::setEntries(int count, const QRgb *colors, int base) if (!d->cells) d->cells = new QVector<QRgb>(256); - Q_ASSERT_X(!colors || base >= 0 || base + count < d->cells->size(), "QGLColormap::setEntries", + Q_ASSERT_X(colors && base >= 0 && (base + count) <= d->cells->size(), "QGLColormap::setEntries", "preconditions not met"); - for (int i = base; i < base + count; ++i) - setEntry(i, colors[i]); + for (int i = 0; i < count; ++i) + setEntry(base + i, colors[i]); } /*! @@ -227,8 +228,17 @@ QColor QGLColormap::entryColor(int idx) const } /*! - Returns true if the colormap is empty; otherwise returns false. A - colormap with no color values set is considered to be empty. + Returns true if the colormap is empty or it is not in use + by a QGLWidget; otherwise returns false. + + A colormap with no color values set is considered to be empty. + For historical reasons, a colormap that has color values set + but which is not in use by a QGLWidget is also considered empty. + + Compare size() with zero to determine if the colormap is empty + regardless of whether it is in use by a QGLWidget or not. + + \sa size() */ bool QGLColormap::isEmpty() const { diff --git a/src/plugins/audio/audio.pro b/src/plugins/audio/audio.pro new file mode 100644 index 0000000..e93b369 --- /dev/null +++ b/src/plugins/audio/audio.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +#SUBDIRS += ossaudio diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.cpp index b5376b1..13c4053 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.cpp +++ b/src/plugins/gfxdrivers/directfb/qdirectfbkeyboard.cpp @@ -77,13 +77,15 @@ private: QSocketNotifier *keyboardNotifier; DFBEvent event; int bytesRead; - + int lastUnicode, lastKeycode; + Qt::KeyboardModifiers lastModifiers; private Q_SLOTS: void readKeyboardData(); }; QDirectFBKeyboardHandlerPrivate::QDirectFBKeyboardHandlerPrivate(QDirectFBKeyboardHandler *h) - : handler(h), eventBuffer(0) + : handler(h), eventBuffer(0), keyboardNotifier(0), bytesRead(0), + lastUnicode(0), lastKeycode(0), lastModifiers(0) { Q_ASSERT(qt_screen); @@ -114,8 +116,6 @@ QDirectFBKeyboardHandlerPrivate::QDirectFBKeyboardHandlerPrivate(QDirectFBKeyboa ::fcntl(fd, F_SETFL, flags | O_NONBLOCK); memset(&event, 0, sizeof(event)); - bytesRead = 0; - keyboardNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); connect(keyboardNotifier, SIGNAL(activated(int)), @@ -213,8 +213,27 @@ void QDirectFBKeyboardHandlerPrivate::readKeyboardData() unicode = symbol; if (unicode != -1 || keycode != 0) { + bool autoRepeat = false; + if (press) { + if (unicode == lastUnicode && keycode == lastKeycode && modifiers == lastModifiers) { + autoRepeat = true; + } else { + lastUnicode = unicode; + lastKeycode = keycode; + lastModifiers = modifiers; + } + } else { + lastUnicode = lastKeycode = -1; + lastModifiers = 0; + } + if (autoRepeat) { + handler->processKeyEvent(unicode, keycode, + modifiers, false, autoRepeat); + + } + handler->processKeyEvent(unicode, keycode, - modifiers, press, false); + modifiers, press, autoRepeat); } } } diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.cpp index 86e92f7..2075799 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.cpp +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.cpp @@ -43,10 +43,17 @@ #include "qdirectfbscreen.h" #include "qdirectfbpaintdevice.h" +#include "qdirectfbpaintengine.h" + +QDirectFBPaintDevice::QDirectFBPaintDevice(QDirectFBScreen *scr) + : QCustomRasterPaintDevice(0), dfbSurface(0), lockedImage(0), screen(scr), + lock(DFBSurfaceLockFlags(0)), mem(0), engine(0) +{} QDirectFBPaintDevice::~QDirectFBPaintDevice() { delete lockedImage; + delete engine; } @@ -164,4 +171,10 @@ int QDirectFBPaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) const } } +QPaintEngine *QDirectFBPaintDevice::paintEngine() const +{ + return engine; +} + #endif + diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.h b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.h index 248a15b..c4aeb70 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.h +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintdevice.h @@ -51,7 +51,8 @@ QT_BEGIN_HEADER QT_MODULE(Gui) // Inherited by both window surface and pixmap - class QDirectFBPaintDevice : public QCustomRasterPaintDevice +class QDirectFBPaintEngine; +class QDirectFBPaintDevice : public QCustomRasterPaintDevice { public: ~QDirectFBPaintDevice(); @@ -62,19 +63,16 @@ public: void unlockDirectFB(); // Reimplemented from QCustomRasterPaintDevice: - void* memory() const; + void *memory() const; QImage::Format format() const; int bytesPerLine() const; QSize size() const; int metric(QPaintDevice::PaintDeviceMetric metric) const; DFBSurfaceLockFlags lockFlags() const { return lock; } -protected: - // Shouldn't create QDirectFBPaintDevice by itself but only sub-class it: - QDirectFBPaintDevice(QDirectFBScreen *scr = QDirectFBScreen::instance()) - : QCustomRasterPaintDevice(0), dfbSurface(0), lockedImage(0), screen(scr), - lock(DFBSurfaceLockFlags(0)), mem(0) - {} + QPaintEngine *paintEngine() const; +protected: + QDirectFBPaintDevice(QDirectFBScreen *scr); inline int dotsPerMeterX() const { return (screen->deviceWidth() * 1000) / screen->physicalWidth(); @@ -83,16 +81,17 @@ protected: { return (screen->deviceHeight() * 1000) / screen->physicalHeight(); } - +protected: IDirectFBSurface *dfbSurface; QImage *lockedImage; QDirectFBScreen *screen; int bpl; DFBSurfaceLockFlags lock; uchar *mem; + QDirectFBPaintEngine *engine; private: - Q_DISABLE_COPY(QDirectFBPaintDevice) - }; + Q_DISABLE_COPY(QDirectFBPaintDevice); +}; QT_END_HEADER diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp index b264ac0..58c8a58 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpaintengine.cpp @@ -53,16 +53,32 @@ #include <qmath.h> #include <private/qpixmapdata_p.h> #include <private/qpixmap_raster_p.h> +#include <private/qimagepixmapcleanuphooks_p.h> class SurfaceCache; class QDirectFBPaintEnginePrivate : public QRasterPaintEnginePrivate { public: enum TransformationTypeFlags { - NegativeScale = 0x100, - RectsUnsupported = (QTransform::TxRotate|QTransform::TxShear|QTransform::TxProject), - BlitUnsupported = (NegativeScale|RectsUnsupported) + Matrix_NegativeScale = 0x100, + Matrix_RectsUnsupported = (QTransform::TxRotate|QTransform::TxShear|QTransform::TxProject), + Matrix_BlitsUnsupported = (Matrix_NegativeScale|Matrix_RectsUnsupported) }; + + enum CompositionModeStatus { + PorterDuff_None = 0x0, + PorterDuff_SupportedBlits = 0x1, + PorterDuff_SupportedPrimitives = 0x2 + }; + + enum ClipType { + ClipUnset, + NoClip, + RectClip, + RegionClip, + ComplexClip + }; + QDirectFBPaintEnginePrivate(QDirectFBPaintEngine *p); ~QDirectFBPaintEnginePrivate(); @@ -76,9 +92,6 @@ public: inline void lock(); inline void unlock(); - inline bool dfbCanHandleClip(const QRect &rect) const; - inline bool dfbCanHandleClip(const QRectF &rect) const; - inline bool dfbCanHandleClip() const; inline bool isSimpleBrush(const QBrush &brush) const; void drawTiledPixmap(const QRectF &dest, const QPixmap &pixmap, const QPointF &pos); @@ -100,19 +113,16 @@ private: bool antialiased; bool simplePen; - uint transformationType; // this is QTransform::type() + NegativeScale if qMin(transform.m11(), transform.m22()) < 0 + uint transformationType; // this is QTransform::type() + Matrix_NegativeScale if qMin(transform.m11(), transform.m22()) < 0 SurfaceCache *surfaceCache; - IDirectFB *fb; - quint8 opacity; bool dirtyClip; - bool dfbHandledClip; - bool ignoreSystemClip; + ClipType clipType; QDirectFBPaintDevice *dfbDevice; - bool unsupportedCompositionMode; + uint compositionModeStatus; QDirectFBPaintEngine *q; QRect currentClip; @@ -161,8 +171,12 @@ enum PaintOperation { #ifdef QT_DIRECTFB_WARN_ON_RASTERFALLBACKS template <typename device, typename T1, typename T2, typename T3> -static void rasterFallbackWarn(const char *msg, const char *, const device *, uint, bool, bool, bool, - const char *, const T1 &, const char *, const T2 &, const char *, const T3 &); +static void rasterFallbackWarn(const char *msg, const char *func, const device *dev, + uint transformationType, bool simplePen, + uint clipType, uint compositionModeStatus, + const char *nameOne, const T1 &one, + const char *nameTwo, const T2 &two, + const char *nameThree, const T3 &three); #endif #if defined QT_DIRECTFB_WARN_ON_RASTERFALLBACKS && defined QT_DIRECTFB_DISABLE_RASTERFALLBACKS @@ -172,8 +186,8 @@ static void rasterFallbackWarn(const char *msg, const char *, const device *, ui __FUNCTION__, state()->painter->device(), \ d_func()->transformationType, \ d_func()->simplePen, \ - d_func()->dfbCanHandleClip(), \ - d_func()->unsupportedCompositionMode, \ + d_func()->clipType, \ + d_func()->compositionModeStatus, \ #one, one, #two, two, #three, three); \ if (op & (QT_DIRECTFB_DISABLE_RASTERFALLBACKS)) \ return; @@ -188,14 +202,13 @@ static void rasterFallbackWarn(const char *msg, const char *, const device *, ui __FUNCTION__, state()->painter->device(), \ d_func()->transformationType, \ d_func()->simplePen, \ - d_func()->dfbCanHandleClip(), \ - d_func()->unsupportedCompositionMode, \ + d_func()->clipType, \ + d_func()->compositionModeStatus, \ #one, one, #two, two, #three, three); #else #define RASTERFALLBACK(op, one, two, three) #endif - template <class T> static inline void drawLines(const T *lines, int n, const QTransform &transform, IDirectFBSurface *surface); template <class T> @@ -203,6 +216,36 @@ static inline void fillRects(const T *rects, int n, const QTransform &transform, template <class T> static inline void drawRects(const T *rects, int n, const QTransform &transform, IDirectFBSurface *surface); +#define CLIPPED_PAINT(operation) { \ + DFBRegion clipRegion; \ + switch (d->clipType) { \ + case QDirectFBPaintEnginePrivate::NoClip: \ + case QDirectFBPaintEnginePrivate::RectClip: \ + operation; \ + break; \ + case QDirectFBPaintEnginePrivate::RegionClip: { \ + Q_ASSERT(d->clip()); \ + const QVector<QRect> cr = d->clip()->clipRegion.rects(); \ + const int size = cr.size(); \ + for (int i=0; i<size; ++i) { \ + d->currentClip = cr.at(i); \ + clipRegion.x1 = d->currentClip.x(); \ + clipRegion.y1 = d->currentClip.y(); \ + clipRegion.x2 = d->currentClip.right(); \ + clipRegion.y2 = d->currentClip.bottom(); \ + d->surface->SetClip(d->surface, &clipRegion); \ + operation; \ + } \ + d->dirtyClip = true; \ + break; } \ + case QDirectFBPaintEnginePrivate::ComplexClip: \ + case QDirectFBPaintEnginePrivate::ClipUnset: \ + qFatal("CLIPPED_PAINT internal error %d", d->clipType); \ + break; \ + } \ + } + + QDirectFBPaintEngine::QDirectFBPaintEngine(QPaintDevice *device) : QRasterPaintEngine(*(new QDirectFBPaintEnginePrivate(this)), device) { @@ -233,7 +276,7 @@ bool QDirectFBPaintEngine::begin(QPaintDevice *device) } d->prepare(d->dfbDevice); - + d->setCompositionMode(state()->composition_mode); return QRasterPaintEngine::begin(device); } @@ -323,58 +366,60 @@ void QDirectFBPaintEngine::clip(const QRect &rect, Qt::ClipOperation op) void QDirectFBPaintEngine::drawRects(const QRect *rects, int rectCount) { Q_D(QDirectFBPaintEngine); - d->updateClip(); + const QPen &pen = state()->pen; const QBrush &brush = state()->brush; - if (d->unsupportedCompositionMode - || (d->transformationType & QDirectFBPaintEnginePrivate::RectsUnsupported) + if (brush == Qt::NoBrush && pen == Qt::NoPen) + return; + + d->updateClip(); + if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedPrimitives) + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_RectsUnsupported) || !d->simplePen - || !d->dfbCanHandleClip() + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip || !d->isSimpleBrush(brush)) { RASTERFALLBACK(DRAW_RECTS, rectCount, VOID_ARG(), VOID_ARG()); d->lock(); QRasterPaintEngine::drawRects(rects, rectCount); return; } - d->unlock(); - if (brush != Qt::NoBrush) { d->setDFBColor(brush.color()); - ::fillRects<QRect>(rects, rectCount, state()->matrix, d->surface); + CLIPPED_PAINT(::fillRects<QRect>(rects, rectCount, state()->matrix, d->surface)); } - const QPen &pen = state()->pen; if (pen != Qt::NoPen) { d->setDFBColor(pen.color()); - ::drawRects<QRect>(rects, rectCount, state()->matrix, d->surface); + CLIPPED_PAINT(::drawRects<QRect>(rects, rectCount, state()->matrix, d->surface)); } } void QDirectFBPaintEngine::drawRects(const QRectF *rects, int rectCount) { Q_D(QDirectFBPaintEngine); - d->updateClip(); + const QPen &pen = state()->pen; const QBrush &brush = state()->brush; - if (d->unsupportedCompositionMode - || (d->transformationType & QDirectFBPaintEnginePrivate::RectsUnsupported) + if (brush == Qt::NoBrush && pen == Qt::NoPen) + return; + + d->updateClip(); + if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedPrimitives) + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_RectsUnsupported) || !d->simplePen - || !d->dfbCanHandleClip() + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip || !d->isSimpleBrush(brush)) { RASTERFALLBACK(DRAW_RECTS, rectCount, VOID_ARG(), VOID_ARG()); d->lock(); QRasterPaintEngine::drawRects(rects, rectCount); return; } - d->unlock(); - if (brush != Qt::NoBrush) { d->setDFBColor(brush.color()); - ::fillRects<QRectF>(rects, rectCount, state()->matrix, d->surface); + CLIPPED_PAINT(::fillRects<QRectF>(rects, rectCount, state()->matrix, d->surface)); } - const QPen &pen = state()->pen; if (pen != Qt::NoPen) { d->setDFBColor(pen.color()); - ::drawRects<QRectF>(rects, rectCount, state()->matrix, d->surface); + CLIPPED_PAINT(::drawRects<QRectF>(rects, rectCount, state()->matrix, d->surface)); } } @@ -382,7 +427,10 @@ void QDirectFBPaintEngine::drawLines(const QLine *lines, int lineCount) { Q_D(QDirectFBPaintEngine); d->updateClip(); - if (d->unsupportedCompositionMode || !d->simplePen || !d->dfbCanHandleClip()) { + + if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedPrimitives) + || !d->simplePen + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip) { RASTERFALLBACK(DRAW_LINES, lineCount, VOID_ARG(), VOID_ARG()); d->lock(); QRasterPaintEngine::drawLines(lines, lineCount); @@ -393,7 +441,7 @@ void QDirectFBPaintEngine::drawLines(const QLine *lines, int lineCount) if (pen != Qt::NoPen) { d->unlock(); d->setDFBColor(pen.color()); - ::drawLines<QLine>(lines, lineCount, state()->matrix, d->surface); + CLIPPED_PAINT(::drawLines<QLine>(lines, lineCount, state()->matrix, d->surface)); } } @@ -401,7 +449,10 @@ void QDirectFBPaintEngine::drawLines(const QLineF *lines, int lineCount) { Q_D(QDirectFBPaintEngine); d->updateClip(); - if (d->unsupportedCompositionMode || !d->simplePen || !d->dfbCanHandleClip()) { + + if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedPrimitives) + || !d->simplePen + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip) { RASTERFALLBACK(DRAW_LINES, lineCount, VOID_ARG(), VOID_ARG()); d->lock(); QRasterPaintEngine::drawLines(lines, lineCount); @@ -412,7 +463,7 @@ void QDirectFBPaintEngine::drawLines(const QLineF *lines, int lineCount) if (pen != Qt::NoPen) { d->unlock(); d->setDFBColor(pen.color()); - ::drawLines<QLineF>(lines, lineCount, state()->matrix, d->surface); + CLIPPED_PAINT(::drawLines<QLineF>(lines, lineCount, state()->matrix, d->surface)); } } @@ -442,9 +493,9 @@ void QDirectFBPaintEngine::drawImage(const QRectF &r, const QImage &image, d->updateClip(); #if !defined QT_NO_DIRECTFB_PREALLOCATED || defined QT_DIRECTFB_IMAGECACHE - if (d->unsupportedCompositionMode - || (d->transformationType & QDirectFBPaintEnginePrivate::BlitUnsupported) - || !d->dfbCanHandleClip(r) + if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedBlits) + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported) + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip #ifndef QT_DIRECTFB_IMAGECACHE || QDirectFBScreen::getSurfacePixelFormat(image.format()) == DSPF_UNKNOWN #elif defined QT_NO_DIRECTFB_PREALLOCATED @@ -463,7 +514,7 @@ void QDirectFBPaintEngine::drawImage(const QRectF &r, const QImage &image, bool release; IDirectFBSurface *imgSurface = d->getSurface(image, &release); d->prepareForBlit(QDirectFBScreen::hasAlpha(imgSurface)); - d->blit(r, imgSurface, sr); + CLIPPED_PAINT(d->blit(r, imgSurface, sr)); if (release) { #if (Q_DIRECTFB_VERSION >= 0x010000) d->surface->ReleaseSource(d->surface); @@ -482,15 +533,15 @@ void QDirectFBPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, const QRectF &sr) { Q_D(QDirectFBPaintEngine); - d->updateClip(); + d->updateClip(); if (pixmap.pixmapData()->classId() != QPixmapData::DirectFBClass) { RASTERFALLBACK(DRAW_PIXMAP, r, pixmap.size(), sr); d->lock(); QRasterPaintEngine::drawPixmap(r, pixmap, sr); - } else if (d->unsupportedCompositionMode - || (d->transformationType & QDirectFBPaintEnginePrivate::BlitUnsupported) - || !d->dfbCanHandleClip(r)) { + } else if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedBlits) + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported) + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip) { RASTERFALLBACK(DRAW_PIXMAP, r, pixmap.size(), sr); const QImage *img = static_cast<QDirectFBPixmapData*>(pixmap.pixmapData())->buffer(DSLF_READ); d->lock(); @@ -503,7 +554,8 @@ void QDirectFBPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pixmap, QDirectFBPixmapData *dfbData = static_cast<QDirectFBPixmapData*>(data); dfbData->unlockDirectFB(); IDirectFBSurface *s = dfbData->directFBSurface(); - d->blit(r, s, sr); + + CLIPPED_PAINT(d->blit(r, s, sr)); } } @@ -522,9 +574,9 @@ void QDirectFBPaintEngine::drawTiledPixmap(const QRectF &r, RASTERFALLBACK(DRAW_TILED_PIXMAP, r, pixmap.size(), offset); d->lock(); QRasterPaintEngine::drawTiledPixmap(r, pixmap, offset); - } else if (d->unsupportedCompositionMode - || (d->transformationType & QDirectFBPaintEnginePrivate::BlitUnsupported) - || !d->dfbCanHandleClip(r)) { + } else if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedBlits) + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported) + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip) { RASTERFALLBACK(DRAW_TILED_PIXMAP, r, pixmap.size(), offset); const QImage *img = static_cast<QDirectFBPixmapData*>(pixmap.pixmapData())->buffer(DSLF_READ); d->lock(); @@ -623,30 +675,34 @@ void QDirectFBPaintEngine::fillRect(const QRectF &rect, const QBrush &brush) if (brush.style() == Qt::NoBrush) return; d->updateClip(); - if (!d->unsupportedCompositionMode - && !(d->transformationType & (QDirectFBPaintEnginePrivate::RectsUnsupported)) - && d->dfbCanHandleClip(rect)) { + if (d->clipType != QDirectFBPaintEnginePrivate::ComplexClip) { switch (brush.style()) { case Qt::SolidPattern: { + if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedPrimitives) + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_RectsUnsupported)) { + break; + } const QColor color = brush.color(); if (!color.isValid()) return; d->unlock(); d->setDFBColor(color); const QRect r = state()->matrix.mapRect(rect).toRect(); - d->surface->FillRectangle(d->surface, r.x(), r.y(), - r.width(), r.height()); + CLIPPED_PAINT(d->surface->FillRectangle(d->surface, r.x(), r.y(), r.width(), r.height())); return; } + case Qt::TexturePattern: { - if (d->transformationType & QDirectFBPaintEnginePrivate::NegativeScale) + if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedBlits) + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_BlitsUnsupported)) { break; + } const QPixmap texture = brush.texture(); if (texture.pixmapData()->classId() != QPixmapData::DirectFBClass) break; d->unlock(); - d->drawTiledPixmap(rect, texture, rect.topLeft() - state()->brushOrigin); + CLIPPED_PAINT(d->drawTiledPixmap(rect, texture, rect.topLeft() - state()->brushOrigin)); return; } default: break; @@ -663,9 +719,9 @@ void QDirectFBPaintEngine::fillRect(const QRectF &rect, const QColor &color) return; Q_D(QDirectFBPaintEngine); d->updateClip(); - if (d->unsupportedCompositionMode - || (d->transformationType & QDirectFBPaintEnginePrivate::RectsUnsupported) - || !d->dfbCanHandleClip()) { + if (!(d->compositionModeStatus & QDirectFBPaintEnginePrivate::PorterDuff_SupportedPrimitives) + || (d->transformationType & QDirectFBPaintEnginePrivate::Matrix_RectsUnsupported) + || d->clipType == QDirectFBPaintEnginePrivate::ComplexClip) { RASTERFALLBACK(FILL_RECT, rect, color, VOID_ARG()); d->lock(); QRasterPaintEngine::fillRect(rect, color); @@ -699,9 +755,7 @@ void QDirectFBPaintEngine::initImageCache(int size) { Q_ASSERT(size >= 0); imageCache.setMaxCost(size); - typedef void (*_qt_image_cleanup_hook_64)(qint64); - extern Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64; - qt_image_cleanup_hook_64 = ::cachedImageCleanupHook; + QImagePixmapCleanupHooks::instance()->addImageHook(cachedImageCleanupHook); } #endif // QT_DIRECTFB_IMAGECACHE @@ -712,11 +766,10 @@ void QDirectFBPaintEngine::initImageCache(int size) QDirectFBPaintEnginePrivate::QDirectFBPaintEnginePrivate(QDirectFBPaintEngine *p) : surface(0), antialiased(false), simplePen(false), transformationType(0), opacity(255), dirtyClip(true), - dfbHandledClip(false), dfbDevice(0), - unsupportedCompositionMode(false), q(p) + clipType(ClipUnset), dfbDevice(0), + compositionModeStatus(0), q(p) { fb = QDirectFBScreen::instance()->dfb(); - ignoreSystemClip = QDirectFBScreen::instance()->directFBFlags() & QDirectFBScreen::IgnoreSystemClip; surfaceCache = new SurfaceCache; } @@ -725,23 +778,6 @@ QDirectFBPaintEnginePrivate::~QDirectFBPaintEnginePrivate() delete surfaceCache; } -bool QDirectFBPaintEnginePrivate::dfbCanHandleClip(const QRect &) const -{ - // TODO: Check to see if DirectFB can handle the clip for the given rect - return dfbHandledClip; -} - -bool QDirectFBPaintEnginePrivate::dfbCanHandleClip(const QRectF &) const -{ - // TODO: Check to see if DirectFB can handle the clip for the given rect - return dfbHandledClip; -} - -bool QDirectFBPaintEnginePrivate::dfbCanHandleClip() const -{ - return dfbHandledClip; -} - bool QDirectFBPaintEnginePrivate::isSimpleBrush(const QBrush &brush) const { return (brush.style() == Qt::NoBrush) || (brush.style() == Qt::SolidPattern && !antialiased); @@ -769,7 +805,7 @@ void QDirectFBPaintEnginePrivate::setTransform(const QTransform &transform) { transformationType = transform.type(); if (qMin(transform.m11(), transform.m22()) < 0) { - transformationType |= QDirectFBPaintEnginePrivate::NegativeScale; + transformationType |= QDirectFBPaintEnginePrivate::Matrix_NegativeScale; } setPen(q->state()->pen); } @@ -791,7 +827,53 @@ void QDirectFBPaintEnginePrivate::setPen(const QPen &pen) void QDirectFBPaintEnginePrivate::setCompositionMode(QPainter::CompositionMode mode) { - unsupportedCompositionMode = (mode != QPainter::CompositionMode_SourceOver); + if (!surface) + return; + compositionModeStatus = PorterDuff_SupportedBlits; + switch (mode) { + case QPainter::CompositionMode_Clear: + surface->SetPorterDuff(surface, DSPD_CLEAR); + break; + case QPainter::CompositionMode_Source: + surface->SetPorterDuff(surface, DSPD_SRC); + break; + case QPainter::CompositionMode_SourceOver: + compositionModeStatus |= PorterDuff_SupportedPrimitives; + surface->SetPorterDuff(surface, DSPD_SRC_OVER); + break; + case QPainter::CompositionMode_DestinationOver: + surface->SetPorterDuff(surface, DSPD_DST_OVER); + break; + case QPainter::CompositionMode_SourceIn: + surface->SetPorterDuff(surface, DSPD_SRC_IN); + break; + case QPainter::CompositionMode_DestinationIn: + surface->SetPorterDuff(surface, DSPD_DST_IN); + break; + case QPainter::CompositionMode_SourceOut: + surface->SetPorterDuff(surface, DSPD_SRC_OUT); + break; + case QPainter::CompositionMode_DestinationOut: + surface->SetPorterDuff(surface, DSPD_DST_OUT); + break; +#if (Q_DIRECTFB_VERSION >= 0x010000) + case QPainter::CompositionMode_SourceAtop: + surface->SetPorterDuff(surface, DSPD_SRC_ATOP); + break; + case QPainter::CompositionMode_DestinationAtop: + surface->SetPorterDuff(surface, DSPD_DST_ATOP); + break; + case QPainter::CompositionMode_Plus: + surface->SetPorterDuff(surface, DSPD_ADD); + break; +#endif + case QPainter::CompositionMode_Xor: + surface->SetPorterDuff(surface, DSPD_XOR); + break; + default: + compositionModeStatus = 0; + break; + } } void QDirectFBPaintEnginePrivate::setRenderHints(QPainter::RenderHints hints) @@ -897,7 +979,7 @@ static inline qreal fixCoord(qreal rect_pos, qreal pixmapSize, qreal offset) void QDirectFBPaintEnginePrivate::drawTiledPixmap(const QRectF &dest, const QPixmap &pixmap, const QPointF &off) { Q_ASSERT(!dirtyClip); - Q_ASSERT(!(transformationType & BlitUnsupported)); + Q_ASSERT(!(transformationType & Matrix_BlitsUnsupported)); const QTransform &transform = q->state()->matrix; const QRect destinationRect = transform.mapRect(dest).toRect().normalized(); QRect newClip = destinationRect; @@ -910,8 +992,8 @@ void QDirectFBPaintEnginePrivate::drawTiledPixmap(const QRectF &dest, const QPix const DFBRegion clip = { newClip.x(), newClip.y(), - newClip.x() + newClip.width() - 1, - newClip.y() + newClip.height() - 1 + newClip.right(), + newClip.bottom() }; surface->SetClip(surface, &clip); @@ -980,8 +1062,8 @@ void QDirectFBPaintEnginePrivate::drawTiledPixmap(const QRectF &dest, const QPix const DFBRegion clip = { currentClip.x(), currentClip.y(), - currentClip.x() + currentClip.width(), - currentClip.y() + currentClip.height() + currentClip.right(), + currentClip.bottom() }; surface->SetClip(surface, &clip); } @@ -996,22 +1078,22 @@ void QDirectFBPaintEnginePrivate::updateClip() const QClipData *clipData = clip(); if (!clipData || !clipData->enabled) { surface->SetClip(surface, NULL); - dfbHandledClip = true; + clipType = NoClip; } else if (clipData->hasRectClip) { const DFBRegion r = { clipData->clipRect.x(), clipData->clipRect.y(), - clipData->clipRect.x() + clipData->clipRect.width(), - clipData->clipRect.y() + clipData->clipRect.height() + clipData->clipRect.right(), + clipData->clipRect.bottom() }; surface->SetClip(surface, &r); currentClip = clipData->clipRect.normalized(); // ### is this guaranteed to always be normalized? - dfbHandledClip = true; - } else if (clipData->hasRegionClip && ignoreSystemClip && clipData->clipRegion == systemClip) { - dfbHandledClip = true; + clipType = RectClip; + } else if (clipData->hasRegionClip) { + clipType = RegionClip; } else { - dfbHandledClip = false; + clipType = ComplexClip; } dirtyClip = false; @@ -1108,7 +1190,7 @@ template <> inline const bool* ptr<bool>(const bool &) { return 0; } template <typename device, typename T1, typename T2, typename T3> static void rasterFallbackWarn(const char *msg, const char *func, const device *dev, uint transformationType, bool simplePen, - bool dfbHandledClip, bool unsupportedCompositionMode, + uint clipType, uint compositionModeStatus, const char *nameOne, const T1 &one, const char *nameTwo, const T2 &two, const char *nameThree, const T3 &three) @@ -1124,8 +1206,8 @@ static void rasterFallbackWarn(const char *msg, const char *func, const device * dbg << QString("transformationType 0x%1").arg(transformationType, 3, 16, QLatin1Char('0')) << "simplePen" << simplePen - << "dfbHandledClip" << dfbHandledClip - << "unsupportedCompositionMode" << unsupportedCompositionMode; + << "clipType" << clipType + << "compositionModeStatus" << compositionModeStatus; const T1 *t1 = ptr(one); const T2 *t2 = ptr(two); @@ -1142,7 +1224,6 @@ static void rasterFallbackWarn(const char *msg, const char *func, const device * } qWarning("%s", qPrintable(out)); } -#endif - +#endif // QT_DIRECTFB_WARN_ON_RASTERFALLBACKS #endif // QT_NO_DIRECTFB diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.cpp index 52e85ba..bedd9e5 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.cpp +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.cpp @@ -49,9 +49,9 @@ static int global_ser_no = 0; -QDirectFBPixmapData::QDirectFBPixmapData(PixelType pixelType) - : QPixmapData(pixelType, DirectFBClass), - engine(0), format(QImage::Format_Invalid), alpha(false) +QDirectFBPixmapData::QDirectFBPixmapData(QDirectFBScreen *screen, PixelType pixelType) + : QPixmapData(pixelType, DirectFBClass), QDirectFBPaintDevice(screen), + format(QImage::Format_Invalid), alpha(false) { setSerialNumber(0); } @@ -61,7 +61,6 @@ QDirectFBPixmapData::~QDirectFBPixmapData() unlockDirectFB(); if (dfbSurface && QDirectFBScreen::instance()) screen->releaseDFBSurface(dfbSurface); - delete engine; } void QDirectFBPixmapData::resize(int width, int height) @@ -304,7 +303,7 @@ QPixmap QDirectFBPixmapData::transformed(const QTransform &transform, Q_ASSERT(image); const QImage transformed = image->transformed(transform, mode); that->unlockDirectFB(); - QDirectFBPixmapData *data = new QDirectFBPixmapData(QPixmapData::PixmapType); + QDirectFBPixmapData *data = new QDirectFBPixmapData(screen, QPixmapData::PixmapType); data->fromImage(transformed, Qt::AutoColor); return QPixmap(data); } @@ -314,7 +313,7 @@ QPixmap QDirectFBPixmapData::transformed(const QTransform &transform, if (size.isEmpty()) return QPixmap(); - QDirectFBPixmapData *data = new QDirectFBPixmapData(QPixmapData::PixmapType); + QDirectFBPixmapData *data = new QDirectFBPixmapData(screen, QPixmapData::PixmapType); DFBSurfaceBlittingFlags flags = DSBLIT_NOFX; data->alpha = alpha; if (alpha) { @@ -373,6 +372,8 @@ QImage QDirectFBPixmapData::toImage() const return img->copy(); } +/* This is QPixmapData::paintEngine(), not QPaintDevice::paintEngine() */ + QPaintEngine *QDirectFBPixmapData::paintEngine() const { if (!engine) { diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h index 0204061..df3c679 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h +++ b/src/plugins/gfxdrivers/directfb/qdirectfbpixmap.h @@ -56,7 +56,7 @@ class QDirectFBPaintEngine; class QDirectFBPixmapData : public QPixmapData, public QDirectFBPaintDevice { public: - QDirectFBPixmapData(PixelType pixelType); + QDirectFBPixmapData(QDirectFBScreen *screen, PixelType pixelType); ~QDirectFBPixmapData(); // Re-implemented from QPixmapData: @@ -79,7 +79,6 @@ public: inline bool hasAlphaChannel() const { return alpha; } private: void invalidate(); - QDirectFBPaintEngine *engine; QImage::Format format; bool alpha; }; diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbscreen.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.cpp index 030e51f..4f8fa2f 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbscreen.cpp +++ b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.cpp @@ -57,7 +57,7 @@ class QDirectFBScreenPrivate : public QObject, public QWSGraphicsSystem { public: - QDirectFBScreenPrivate(QDirectFBScreen*); + QDirectFBScreenPrivate(QDirectFBScreen *qptr); ~QDirectFBScreenPrivate(); void setFlipFlags(const QStringList &args); @@ -82,10 +82,11 @@ public: QDirectFBScreen::DirectFBFlags directFBFlags; QImage::Format alphaPixmapFormat; QColor backgroundColor; + QDirectFBScreen *q; }; -QDirectFBScreenPrivate::QDirectFBScreenPrivate(QDirectFBScreen *screen) - : QWSGraphicsSystem(screen), dfb(0), dfbSurface(0), flipFlags(DSFLIP_NONE) +QDirectFBScreenPrivate::QDirectFBScreenPrivate(QDirectFBScreen *qptr) + : QWSGraphicsSystem(qptr), dfb(0), dfbSurface(0), flipFlags(DSFLIP_NONE) #ifndef QT_NO_DIRECTFB_LAYER , dfbLayer(0) #endif @@ -98,6 +99,7 @@ QDirectFBScreenPrivate::QDirectFBScreenPrivate(QDirectFBScreen *screen) #endif , directFBFlags(QDirectFBScreen::NoFlags) , alphaPixmapFormat(QImage::Format_Invalid) + , q(qptr) { #ifndef QT_NO_QWS_SIGNALHANDLER QWSSignalHandler::instance()->addObject(this); @@ -742,7 +744,7 @@ QPixmapData *QDirectFBScreenPrivate::createPixmapData(QPixmapData::PixelType typ if (type == QPixmapData::BitmapType) return QWSGraphicsSystem::createPixmapData(type); - return new QDirectFBPixmapData(type); + return new QDirectFBPixmapData(q, type); } #if (Q_DIRECTFB_VERSION >= 0x000923) @@ -914,9 +916,6 @@ bool QDirectFBScreen::connect(const QString &displaySpec) QDirectFBPaintEngine::initImageCache(imageCacheSize); #endif - if (displayArgs.contains(QLatin1String("ignoresystemclip"), Qt::CaseInsensitive)) - d_ptr->directFBFlags |= IgnoreSystemClip; - #ifndef QT_NO_DIRECTFB_WM if (displayArgs.contains(QLatin1String("fullscreen"))) #endif @@ -1328,15 +1327,15 @@ void QDirectFBScreen::flipSurface(IDirectFBSurface *surface, DFBSurfaceFlipFlags for (int i=0; i<rects.size(); ++i) { const QRect &r = rects.at(i); const DFBRegion dfbReg = { r.x() + offset.x(), r.y() + offset.y(), - r.x() + r.width() + offset.x(), - r.y() + r.height() + offset.y() }; + r.right() + offset.x(), + r.bottom() + offset.y() }; surface->Flip(surface, &dfbReg, i + 1 < rects.size() ? nonWaitFlags : flipFlags); } } else { const QRect r = region.boundingRect(); const DFBRegion dfbReg = { r.x() + offset.x(), r.y() + offset.y(), - r.x() + r.width() + offset.x(), - r.y() + r.height() + offset.y() }; + r.right() + offset.x(), + r.bottom() + offset.y() }; surface->Flip(surface, &dfbReg, flipFlags); } } diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbscreen.h b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.h index 63c608e..8884a06 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbscreen.h +++ b/src/plugins/gfxdrivers/directfb/qdirectfbscreen.h @@ -79,8 +79,7 @@ public: NoFlags = 0x00, VideoOnly = 0x01, SystemOnly = 0x02, - IgnoreSystemClip = 0x04, - BoundingRectFlip = 0x08 + BoundingRectFlip = 0x04 }; Q_DECLARE_FLAGS(DirectFBFlags, DirectFBFlag); diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.cpp b/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.cpp index a1009ac..34168bc 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.cpp +++ b/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.cpp @@ -55,7 +55,7 @@ QDirectFBWindowSurface::QDirectFBWindowSurface(DFBSurfaceFlipFlags flip, QDirect #ifndef QT_NO_DIRECTFB_WM , dfbWindow(0) #endif - , engine(0) + , engineHeight(-1) , flipFlags(flip) , boundingRectFlip(scr->directFBFlags() & QDirectFBScreen::BoundingRectFlip) { @@ -76,7 +76,7 @@ QDirectFBWindowSurface::QDirectFBWindowSurface(DFBSurfaceFlipFlags flip, QDirect #ifndef QT_NO_DIRECTFB_WM , dfbWindow(0) #endif - , engine(0) + , engineHeight(-1) , flipFlags(flip) , boundingRectFlip(scr->directFBFlags() & QDirectFBScreen::BoundingRectFlip) { @@ -209,7 +209,6 @@ void QDirectFBWindowSurface::setGeometry(const QRect &rect) if (!dfbWindow) createWindow(); ::setGeometry(dfbWindow, oldRect, rect); - // ### do I need to release and get the surface again here? #endif break; case Offscreen: { @@ -227,7 +226,10 @@ void QDirectFBWindowSurface::setGeometry(const QRect &rect) if (result != DFB_OK) DirectFBErrorFatal("QDirectFBWindowSurface::setGeometry()", result); } - + if (engine) { + delete engine; + engine = 0; + } QWSWindowSurface::setGeometry(rect); } @@ -293,16 +295,6 @@ bool QDirectFBWindowSurface::move(const QPoint &moveBy) return true; } -QPaintEngine *QDirectFBWindowSurface::paintEngine() const -{ - if (!engine) { - QDirectFBWindowSurface *that = const_cast<QDirectFBWindowSurface*>(this); - that->engine = new QDirectFBPaintEngine(that); - return that->engine; - } - return engine; -} - // hw: XXX: copied from QWidgetPrivate::isOpaque() inline bool isWidgetOpaque(const QWidget *w) { @@ -427,6 +419,12 @@ void QDirectFBWindowSurface::flush(QWidget *, const QRegion ®ion, void QDirectFBWindowSurface::beginPaint(const QRegion &) { + const int h = height(); + if (h > engineHeight) { + engineHeight = h; + delete engine; + engine = new QDirectFBPaintEngine(this); + } } void QDirectFBWindowSurface::endPaint(const QRegion &) diff --git a/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.h b/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.h index c46d93b..fefe9f3 100644 --- a/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.h +++ b/src/plugins/gfxdrivers/directfb/qdirectfbwindowsurface.h @@ -61,8 +61,8 @@ QT_MODULE(Gui) class QDirectFBWindowSurface : public QWSWindowSurface, public QDirectFBPaintDevice { public: - QDirectFBWindowSurface(DFBSurfaceFlipFlags flipFlags, QDirectFBScreen* scr); - QDirectFBWindowSurface(DFBSurfaceFlipFlags flipFlags, QDirectFBScreen* scr, QWidget *widget); + QDirectFBWindowSurface(DFBSurfaceFlipFlags flipFlags, QDirectFBScreen *scr); + QDirectFBWindowSurface(DFBSurfaceFlipFlags flipFlags, QDirectFBScreen *scr, QWidget *widget); ~QDirectFBWindowSurface(); bool isValid() const; @@ -79,7 +79,6 @@ public: QImage image() const { return QImage(); } QPaintDevice *paintDevice() { return this; } - QPaintEngine *paintEngine() const; void flush(QWidget *widget, const QRegion ®ion, const QPoint &offset); @@ -92,7 +91,7 @@ private: void createWindow(); IDirectFBWindow *dfbWindow; #endif - QDirectFBPaintEngine *engine; + int engineHeight; enum Mode { Primary, diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index cf4edf1..45669e4 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -10,3 +10,4 @@ unix { embedded:SUBDIRS *= gfxdrivers decorations mousedrivers kbddrivers !win32:!embedded:!mac:SUBDIRS *= inputmethods contains(QT_CONFIG, phonon): SUBDIRS *= phonon +contains(QT_CONFIG, multimedia): SUBDIRS *= audio diff --git a/src/sql/drivers/mysql/qsql_mysql.cpp b/src/sql/drivers/mysql/qsql_mysql.cpp index bfd65fc..39ef1ef 100644 --- a/src/sql/drivers/mysql/qsql_mysql.cpp +++ b/src/sql/drivers/mysql/qsql_mysql.cpp @@ -85,11 +85,10 @@ public: #else tc(0), #endif - preparedQuerys(false), preparedQuerysEnabled(false) {} + preparedQuerysEnabled(false) {} MYSQL *mysql; QTextCodec *tc; - bool preparedQuerys; bool preparedQuerysEnabled; }; @@ -172,6 +171,7 @@ public: #if MYSQL_VERSION_ID >= 40108 , stmt(0), meta(0), inBinds(0), outBinds(0) #endif + , preparedQuery(false) { connect(dp, SIGNAL(destroyed()), this, SLOT(driverDestroyed())); } @@ -209,6 +209,9 @@ public: MYSQL_BIND *inBinds; MYSQL_BIND *outBinds; #endif + + bool preparedQuery; + private Q_SLOTS: void driverDestroyed() { driver = NULL; } }; @@ -399,7 +402,7 @@ QMYSQLResult::~QMYSQLResult() QVariant QMYSQLResult::handle() const { #if MYSQL_VERSION_ID >= 40108 - if(d->driver && d->driver->d->preparedQuerys) + if(d->preparedQuery) return d->meta ? qVariantFromValue(d->meta) : qVariantFromValue(d->stmt); else #endif @@ -454,9 +457,6 @@ void QMYSQLResult::cleanup() d->row = NULL; setAt(-1); setActive(false); - - if(d->driver) - d->driver->d->preparedQuerys = d->driver->d->preparedQuerysEnabled; } bool QMYSQLResult::fetch(int i) @@ -474,7 +474,7 @@ bool QMYSQLResult::fetch(int i) } if (at() == i) return true; - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { #if MYSQL_VERSION_ID >= 40108 mysql_stmt_data_seek(d->stmt, i); @@ -507,7 +507,7 @@ bool QMYSQLResult::fetchNext() { if(!d->driver) return false; - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { #if MYSQL_VERSION_ID >= 40108 if (mysql_stmt_fetch(d->stmt)) return false; @@ -534,7 +534,7 @@ bool QMYSQLResult::fetchLast() } my_ulonglong numRows; - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { #if MYSQL_VERSION_ID >= 40108 numRows = mysql_stmt_num_rows(d->stmt); #else @@ -574,7 +574,7 @@ QVariant QMYSQLResult::data(int field) int fieldLength = 0; const QMYSQLResultPrivate::QMyField &f = d->fields.at(field); QString val; - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { if (f.nullIndicator) return QVariant(f.type); @@ -634,7 +634,7 @@ QVariant QMYSQLResult::data(int field) case QVariant::ByteArray: { QByteArray ba; - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { ba = QByteArray(f.outField, f.bufLength); } else { ba = QByteArray(d->row[field], fieldLength); @@ -651,7 +651,7 @@ QVariant QMYSQLResult::data(int field) bool QMYSQLResult::isNull(int field) { - if (d->driver->d->preparedQuerys) + if (d->preparedQuery) return d->fields.at(field).nullIndicator; else return d->row[field] == NULL; @@ -662,11 +662,9 @@ bool QMYSQLResult::reset (const QString& query) if (!driver() || !driver()->isOpen() || driver()->isOpenError() || !d->driver) return false; - if(d->driver->d->preparedQuerysEnabled && prepare(query)) { - d->driver->d->preparedQuerys = true; - return exec(); - } - d->driver->d->preparedQuerys = false; + d->preparedQuery = false; + + cleanup(); const QByteArray encQuery(fromUnicode(d->driver->d->tc, query)); if (mysql_real_query(d->driver->d->mysql, encQuery.data(), encQuery.length())) { @@ -699,7 +697,7 @@ bool QMYSQLResult::reset (const QString& query) int QMYSQLResult::size() { if (d->driver && isSelect()) - if (d->driver->d->preparedQuerys) + if (d->preparedQuery) #if MYSQL_VERSION_ID >= 40108 return mysql_stmt_num_rows(d->stmt); #else @@ -721,7 +719,7 @@ QVariant QMYSQLResult::lastInsertId() const if (!isActive() || !d->driver) return QVariant(); - if (d->driver->d->preparedQuerys) { + if (d->preparedQuery) { #if MYSQL_VERSION_ID >= 40108 quint64 id = mysql_stmt_insert_id(d->stmt); if (id) @@ -743,7 +741,7 @@ QSqlRecord QMYSQLResult::record() const return info; #if MYSQL_VERSION_ID >= 40108 - res = d->driver->d->preparedQuerys ? d->meta : d->result; + res = d->preparedQuery ? d->meta : d->result; #else res = d->result; #endif @@ -856,7 +854,7 @@ bool QMYSQLResult::prepare(const QString& query) return false; #if MYSQL_VERSION_ID >= 40108 cleanup(); - if (!d->driver->d->preparedQuerys) + if (!d->driver->d->preparedQuerysEnabled) return QSqlResult::prepare(query); int r; @@ -886,6 +884,7 @@ bool QMYSQLResult::prepare(const QString& query) } setSelect(d->bindInValues()); + d->preparedQuery = true; return true; #else return false; @@ -896,7 +895,7 @@ bool QMYSQLResult::exec() { if (!d->driver) return false; - if (!d->driver->d->preparedQuerys) + if (!d->preparedQuery) return QSqlResult::exec(); if (!d->stmt) return false; @@ -1338,9 +1337,6 @@ QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const if (!isOpen()) return idx; - prepQ = d->preparedQuerys; - d->preparedQuerys = false; - QSqlQuery i(createResult()); QString stmt(QLatin1String("show index from %1;")); QSqlRecord fil = record(tablename); @@ -1353,7 +1349,6 @@ QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const } } - d->preparedQuerys = prepQ; return idx; } diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp index c61c526..4fd0c11 100644 --- a/src/sql/drivers/psql/qsql_psql.cpp +++ b/src/sql/drivers/psql/qsql_psql.cpp @@ -940,7 +940,7 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const case QPSQLDriver::Version6: stmt = QLatin1String("select pg_att1.attname, int(pg_att1.atttypid), pg_cl.relname " "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " - "where lower(pg_cl.relname) = '%1_pkey' " + "where pg_cl.relname = '%1_pkey' " "and pg_cl.oid = pg_ind.indexrelid " "and pg_att2.attrelid = pg_ind.indexrelid " "and pg_att1.attrelid = pg_ind.indrelid " @@ -951,7 +951,7 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const case QPSQLDriver::Version71: stmt = QLatin1String("select pg_att1.attname, pg_att1.atttypid::int, pg_cl.relname " "from pg_attribute pg_att1, pg_attribute pg_att2, pg_class pg_cl, pg_index pg_ind " - "where lower(pg_cl.relname) = '%1_pkey' " + "where pg_cl.relname = '%1_pkey' " "and pg_cl.oid = pg_ind.indexrelid " "and pg_att2.attrelid = pg_ind.indexrelid " "and pg_att1.attrelid = pg_ind.indrelid " @@ -1016,7 +1016,7 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, " "int(pg_attribute.attrelid), pg_attribute.attnum " "from pg_class, pg_attribute " - "where lower(pg_class.relname) = '%1' " + "where pg_class.relname = '%1' " "and pg_attribute.attnum > 0 " "and pg_attribute.attrelid = pg_class.oid "); break; @@ -1025,7 +1025,7 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, " "pg_attribute.attrelid::int, pg_attribute.attnum " "from pg_class, pg_attribute " - "where lower(pg_class.relname) = '%1' " + "where pg_class.relname = '%1' " "and pg_attribute.attnum > 0 " "and pg_attribute.attrelid = pg_class.oid "); break; @@ -1036,7 +1036,7 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const "from pg_class, pg_attribute " "left join pg_attrdef on (pg_attrdef.adrelid = " "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) " - "where lower(pg_class.relname) = '%1' " + "where pg_class.relname = '%1' " "and pg_attribute.attnum > 0 " "and pg_attribute.attrelid = pg_class.oid " "order by pg_attribute.attnum "); diff --git a/src/src.pro b/src/src.pro index 34db728..54ed6da 100644 --- a/src/src.pro +++ b/src/src.pro @@ -22,6 +22,7 @@ contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, o contains(QT_CONFIG, openvg): SRC_SUBDIRS += src_openvg contains(QT_CONFIG, xmlpatterns): SRC_SUBDIRS += src_xmlpatterns contains(QT_CONFIG, phonon): SRC_SUBDIRS += src_phonon +contains(QT_CONFIG, multimedia): SRC_SUBDIRS += src_multimedia contains(QT_CONFIG, svg): SRC_SUBDIRS += src_svg contains(QT_CONFIG, webkit) { #exists($$QT_SOURCE_TREE/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pro): SRC_SUBDIRS += src_javascriptcore @@ -68,6 +69,8 @@ src_qt3support.subdir = $$QT_SOURCE_TREE/src/qt3support src_qt3support.target = sub-qt3support src_phonon.subdir = $$QT_SOURCE_TREE/src/phonon src_phonon.target = sub-phonon +src_multimedia.subdir = $$QT_SOURCE_TREE/src/multimedia +src_multimedia.target = sub-multimedia src_tools_uic3.subdir = $$QT_SOURCE_TREE/src/tools/uic3 src_tools_uic3.target = sub-uic3 src_activeqt.subdir = $$QT_SOURCE_TREE/src/activeqt @@ -104,6 +107,7 @@ src_webkit.target = sub-webkit src_testlib.depends = src_corelib src_qt3support.depends = src_gui src_xml src_network src_sql src_phonon.depends = src_gui + src_multimedia.depends = src_gui src_tools_uic3.depends = src_qt3support src_xml src_tools_idc.depends = src_corelib src_tools_activeqt.depends = src_tools_idc src_gui diff --git a/src/svg/qsvggraphics.cpp b/src/svg/qsvggraphics.cpp index e09f382..2be7559 100644 --- a/src/svg/qsvggraphics.cpp +++ b/src/svg/qsvggraphics.cpp @@ -266,21 +266,23 @@ void QSvgRect::draw(QPainter *p, QSvgExtraStates &states) } } +QSvgTspan * const QSvgText::LINEBREAK = 0; + QSvgText::QSvgText(QSvgNode *parent, const QPointF &coord) : QSvgNode(parent) , m_coord(coord) - , m_textAlignment(Qt::AlignLeft) - , m_scale(1) - , m_appendSpace(false) , m_type(TEXT) , m_size(0, 0) + , m_mode(Default) { - m_paragraphs.push_back(QString()); - m_formatRanges.push_back(QList<QTextLayout::FormatRange>()); } QSvgText::~QSvgText() { + for (int i = 0; i < m_tspans.size(); ++i) { + if (m_tspans[i] != LINEBREAK) + delete m_tspans[i]; + } } void QSvgText::setTextArea(const QSizeF &size) @@ -295,175 +297,173 @@ void QSvgText::draw(QPainter *p, QSvgExtraStates &states) { applyStyle(p, states); - QSvgFontStyle *fontStyle = static_cast<QSvgFontStyle*>( - styleProperty(QSvgStyleProperty::FONT)); - if (fontStyle && fontStyle->svgFont()) { - // SVG fonts not fully supported... - QString text = m_paragraphs.front(); - for (int i = 1; i < m_paragraphs.size(); ++i) { - text.append(QLatin1Char('\n')); - text.append(m_paragraphs[i]); - } - fontStyle->svgFont()->draw(p, m_coord, text, fontStyle->pointSize(), m_textAlignment); - revertStyle(p, states); - return; - } + // Force the font to have a size of 100 pixels to avoid truncation problems + // when the font is very small. + qreal scale = 100.0 / p->font().pointSizeF(); + Qt::Alignment alignment = states.textAnchor; - // Scale the font to its correct size. QTransform oldTransform = p->worldTransform(); - p->scale(1 / m_scale, 1 / m_scale); + p->scale(1 / scale, 1 / scale); qreal y = 0; bool initial = true; - qreal px = m_coord.x() * m_scale; - qreal py = m_coord.y() * m_scale; - QSizeF scaledSize = m_size * m_scale; + qreal px = m_coord.x() * scale; + qreal py = m_coord.y() * scale; + QSizeF scaledSize = m_size * scale; if (m_type == TEXTAREA) { - if (m_textAlignment == Qt::AlignHCenter) + if (alignment == Qt::AlignHCenter) px += scaledSize.width() / 2; - else if (m_textAlignment == Qt::AlignRight) + else if (alignment == Qt::AlignRight) px += scaledSize.width(); } QRectF bounds; if (m_size.height() != 0) - bounds = QRectF(0, 0, 1, scaledSize.height()); - - for (int i = 0; i < m_paragraphs.size(); ++i) { - QTextLayout tl(m_paragraphs[i]); - QTextOption op = tl.textOption(); - op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - tl.setTextOption(op); - tl.setAdditionalFormats(m_formatRanges[i]); - tl.beginLayout(); - forever { - QTextLine line = tl.createLine(); - if (!line.isValid()) - break; - - if (m_size.width() != 0) - line.setLineWidth(scaledSize.width()); - } - tl.endLayout(); - - bool endOfBoundsReached = false; - for (int i = 0; i < tl.lineCount(); ++i) { - QTextLine line = tl.lineAt(i); - - qreal x = 0; - if (m_textAlignment == Qt::AlignHCenter) - x -= line.naturalTextWidth() / 2; - else if (m_textAlignment == Qt::AlignRight) - x -= line.naturalTextWidth(); - - if (initial && m_type == TEXT) - y -= line.ascent(); - initial = false; - - line.setPosition(QPointF(x, y)); - if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width()) - || (m_size.height() != 0 && y + line.height() > scaledSize.height())) { - bounds.setHeight(y); - endOfBoundsReached = true; - break; + bounds = QRectF(0, py, 1, scaledSize.height()); // x and width are not used. + + bool appendSpace = false; + QVector<QString> paragraphs; + QStack<QTextCharFormat> formats; + QVector<QList<QTextLayout::FormatRange> > formatRanges; + paragraphs.push_back(QString()); + formatRanges.push_back(QList<QTextLayout::FormatRange>()); + + for (int i = 0; i < m_tspans.size(); ++i) { + if (m_tspans[i] == LINEBREAK) { + if (m_type == TEXTAREA) { + if (paragraphs.back().isEmpty()) { + QFont font = p->font(); + font.setPixelSize(font.pointSizeF() * scale); + + QTextLayout::FormatRange range; + range.start = 0; + range.length = 1; + range.format.setFont(font); + formatRanges.back().append(range); + + paragraphs.back().append(QLatin1Char(' '));; + } + appendSpace = false; + paragraphs.push_back(QString()); + formatRanges.push_back(QList<QTextLayout::FormatRange>()); } + } else { + WhitespaceMode mode = m_tspans[i]->whitespaceMode(); + m_tspans[i]->applyStyle(p, states); - y += 1.1 * line.height(); - } - tl.draw(p, QPointF(px, py), QVector<QTextLayout::FormatRange>(), bounds); + QFont font = p->font(); + font.setPixelSize(font.pointSizeF() * scale); - if (endOfBoundsReached) - break; - } + QString newText(m_tspans[i]->text()); + newText.replace(QLatin1Char('\t'), QLatin1Char(' ')); + newText.replace(QLatin1Char('\n'), QLatin1Char(' ')); - p->setWorldTransform(oldTransform, false); - revertStyle(p, states); -} + bool prependSpace = !appendSpace && !m_tspans[i]->isTspan() && (mode == Default) && !paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' ')); + if (appendSpace || prependSpace) + paragraphs.back().append(QLatin1Char(' ')); -void QSvgText::insertText(const QString &text, WhitespaceMode mode) -{ - bool isTSpan = (m_formats.count() == 2); - QString newText(text); - newText.replace(QLatin1Char('\t'), QLatin1Char(' ')); - newText.replace(QLatin1Char('\n'), QLatin1Char(' ')); + bool appendSpaceNext = (!m_tspans[i]->isTspan() && (mode == Default) && newText.endsWith(QLatin1Char(' '))); - bool prependSpace = !m_appendSpace && !isTSpan && (mode == Default) && !m_paragraphs.back().isEmpty() && newText.startsWith(QLatin1Char(' ')); - if (m_appendSpace || prependSpace) - m_paragraphs.back().append(QLatin1Char(' ')); + if (mode == Default) { + newText = newText.simplified(); + if (newText.isEmpty()) + appendSpaceNext = false; + } - bool appendSpaceNext = (!isTSpan && (mode == Default) && newText.endsWith(QLatin1Char(' '))); + QTextLayout::FormatRange range; + range.start = paragraphs.back().length(); + range.length = newText.length(); + range.format.setFont(font); + range.format.setTextOutline(p->pen()); + range.format.setForeground(p->brush()); + + if (appendSpace) { + Q_ASSERT(!formatRanges.back().isEmpty()); + ++formatRanges.back().back().length; + } else if (prependSpace) { + --range.start; + ++range.length; + } + formatRanges.back().append(range); - if (mode == Default) { - newText = newText.simplified(); - if (newText.isEmpty()) - appendSpaceNext = false; - } + appendSpace = appendSpaceNext; + paragraphs.back() += newText; - if (!m_formats.isEmpty()) { - QTextLayout::FormatRange range; - range.start = m_paragraphs.back().length(); - range.length = newText.length(); - range.format = m_formats.top(); - if (m_appendSpace) { - Q_ASSERT(!m_formatRanges.back().isEmpty()); - ++m_formatRanges.back().back().length; - } else if (prependSpace) { - --range.start; - ++range.length; + m_tspans[i]->revertStyle(p, states); } - m_formatRanges.back().append(range); } - m_appendSpace = appendSpaceNext; - m_paragraphs.back() += newText; -} - -void QSvgText::insertFormat(const QTextCharFormat &format) -{ - QTextCharFormat mergedFormat = format; - if (!m_formats.isEmpty()) { - mergedFormat = m_formats.top(); - mergedFormat.merge(format); - } - m_formats.push(mergedFormat); -} + if (states.svgFont) { + // SVG fonts not fully supported... + QString text = paragraphs.front(); + for (int i = 1; i < paragraphs.size(); ++i) { + text.append(QLatin1Char('\n')); + text.append(paragraphs[i]); + } + states.svgFont->draw(p, m_coord, text, p->font().pointSizeF() * scale, states.textAnchor); + } else { + for (int i = 0; i < paragraphs.size(); ++i) { + QTextLayout tl(paragraphs[i]); + QTextOption op = tl.textOption(); + op.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + tl.setTextOption(op); + tl.setAdditionalFormats(formatRanges[i]); + tl.beginLayout(); + + forever { + QTextLine line = tl.createLine(); + if (!line.isValid()) + break; + if (m_size.width() != 0) + line.setLineWidth(scaledSize.width()); + } + tl.endLayout(); + + bool endOfBoundsReached = false; + for (int i = 0; i < tl.lineCount(); ++i) { + QTextLine line = tl.lineAt(i); + + qreal x = 0; + if (alignment == Qt::AlignHCenter) + x -= 0.5 * line.naturalTextWidth(); + else if (alignment == Qt::AlignRight) + x -= line.naturalTextWidth(); + + if (initial && m_type == TEXT) + y -= line.ascent(); + initial = false; + + line.setPosition(QPointF(x, y)); + + // Check if the current line fits into the bounding rectangle. + if ((m_size.width() != 0 && line.naturalTextWidth() > scaledSize.width()) + || (m_size.height() != 0 && y + line.height() > scaledSize.height())) { + // I need to set the bounds height to 'y-epsilon' to avoid drawing the current + // line. Since the font is scaled to 100 units, 1 should be a safe epsilon. + bounds.setHeight(y - 1); + endOfBoundsReached = true; + break; + } + + y += 1.1 * line.height(); + } + tl.draw(p, QPointF(px, py), QVector<QTextLayout::FormatRange>(), bounds); -void QSvgText::insertLineBreak() -{ - if (m_type == TEXTAREA) { - if (m_paragraphs.back().isEmpty()) - insertText(QLatin1String(" "), Preserve); - m_appendSpace = false; - m_paragraphs.push_back(QString()); - m_formatRanges.push_back(QList<QTextLayout::FormatRange>()); + if (endOfBoundsReached) + break; + } } -} -void QSvgText::popFormat() -{ - if (m_formats.count() > 1) - m_formats.pop(); -} - -qreal QSvgText::scale() const -{ - return m_scale; -} - -void QSvgText::setScale(qreal scale) -{ - m_scale = scale; -} - -const QTextCharFormat &QSvgText::topFormat() const -{ - return m_formats.top(); + p->setWorldTransform(oldTransform, false); + revertStyle(p, states); } -void QSvgText::setTextAlignment(const Qt::Alignment &alignment) +void QSvgText::addText(const QString &text) { - m_textAlignment = alignment; + m_tspans.append(new QSvgTspan(this, false)); + m_tspans.back()->setWhitespaceMode(m_mode); + m_tspans.back()->addText(text); } QSvgUse::QSvgUse(const QPointF &start, QSvgNode *parent, QSvgNode *node) diff --git a/src/svg/qsvggraphics_p.h b/src/svg/qsvggraphics_p.h index 4a19c7e..345b90b 100644 --- a/src/svg/qsvggraphics_p.h +++ b/src/svg/qsvggraphics_p.h @@ -187,6 +187,8 @@ private: int m_rx, m_ry; }; +class QSvgTspan; + class QSvgText : public QSvgNode { public: @@ -202,26 +204,47 @@ public: virtual void draw(QPainter *p, QSvgExtraStates &states); virtual Type type() const; - void insertText(const QString &text, WhitespaceMode mode); - void insertFormat(const QTextCharFormat &format); - void insertLineBreak(); - void popFormat(); - void setTextAlignment(const Qt::Alignment &alignment); - const QTextCharFormat &topFormat() const; - qreal scale() const; - void setScale(qreal scale); + + void addTspan(QSvgTspan *tspan) {m_tspans.append(tspan);} + void addText(const QString &text); + void addLineBreak() {m_tspans.append(LINEBREAK);} + void setWhitespaceMode(WhitespaceMode mode) {m_mode = mode;} + //virtual QRectF bounds() const; private: + static QSvgTspan * const LINEBREAK; + QPointF m_coord; - QVector<QString> m_paragraphs; - QStack<QTextCharFormat> m_formats; - Qt::Alignment m_textAlignment; - QVector<QList<QTextLayout::FormatRange> > m_formatRanges; - qreal m_scale; - bool m_appendSpace; + // 'm_tspans' is also used to store characters outside tspans and line breaks. + // If a 'm_tspan' item is null, it indicates a line break. + QVector<QSvgTspan *> m_tspans; + Type m_type; QSizeF m_size; + WhitespaceMode m_mode; +}; + +class QSvgTspan : public QSvgNode +{ +public: + // tspans are also used to store normal text, so the 'isProperTspan' is used to separate text from tspan. + QSvgTspan(QSvgNode *parent, bool isProperTspan = true) + : QSvgNode(parent), m_mode(QSvgText::Default), m_isTspan(isProperTspan) + { + } + ~QSvgTspan() { }; + virtual Type type() const {return TSPAN;} + virtual void draw(QPainter *, QSvgExtraStates &) {Q_ASSERT(!"Tspans should be drawn through QSvgText::draw().");} + void addText(const QString &text) {m_text += text;} + const QString &text() const {return m_text;} + bool isTspan() const {return m_isTspan;} + void setWhitespaceMode(QSvgText::WhitespaceMode mode) {m_mode = mode;} + QSvgText::WhitespaceMode whitespaceMode() const {return m_mode;} +private: + QString m_text; + QSvgText::WhitespaceMode m_mode; + bool m_isTspan; }; class QSvgUse : public QSvgNode diff --git a/src/svg/qsvghandler.cpp b/src/svg/qsvghandler.cpp index f10b26f..73c8b01 100644 --- a/src/svg/qsvghandler.cpp +++ b/src/svg/qsvghandler.cpp @@ -70,6 +70,8 @@ QT_BEGIN_NAMESPACE +static const char *qt_inherit_text = "inherit"; +#define QT_INHERIT QLatin1String(qt_inherit_text) double qstrtod(const char *s00, char const **se, bool *ok); @@ -492,7 +494,7 @@ static bool resolveColor(const QString &colorStr, QColor &color, QSvgHandler *ha int(compo[2])); return true; } else if (colorStr == QLatin1String("inherited") || - colorStr == QLatin1String("inherit")) { + colorStr == QT_INHERIT) { return false; } else if (colorStr == QLatin1String("currentColor")) { color = handler->currentColor(); @@ -646,138 +648,48 @@ static void parseBrush(QSvgNode *node, QString opacity = attributes.value(QLatin1String("fill-opacity")).toString(); QString myId = someId(attributes); - QSvgFillStyle *prop = new QSvgFillStyle(0); + if (!value.isEmpty() || !fillRule.isEmpty() || !opacity.isEmpty()) { + QSvgFillStyle *prop = new QSvgFillStyle(0); - //fill-rule attribute handling - if (!fillRule.isEmpty() && fillRule != QLatin1String("inherit")) { - if (fillRule == QLatin1String("evenodd")) - prop->setFillRule(Qt::OddEvenFill); - else if (fillRule == QLatin1String("nonzero")) - prop->setFillRule(Qt::WindingFill); - } - - //fill-opacity atttribute handling - if (!opacity.isEmpty() && opacity != QLatin1String("inherit")) { - prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity)))); - } - - //fill attribute handling - if ((!value.isEmpty()) && (value != QLatin1String("inherit")) ) { - if (value.startsWith(QLatin1String("url"))) { - value = value.remove(0, 3); - QSvgStyleProperty *style = styleFromUrl(node, value); - if (style) { - prop->setFillStyle(style); - } else { - QString id = idFromUrl(value); - prop->setGradientId(id); - prop->setGradientResolved(false); - } - } else if (value != QLatin1String("none")) { - QColor color; - if (resolveColor(value, color, handler)) - prop->setBrush(QBrush(color)); - } else { - prop->setBrush(QBrush(Qt::NoBrush)); + //fill-rule attribute handling + if (!fillRule.isEmpty() && fillRule != QT_INHERIT) { + if (fillRule == QLatin1String("evenodd")) + prop->setFillRule(Qt::OddEvenFill); + else if (fillRule == QLatin1String("nonzero")) + prop->setFillRule(Qt::WindingFill); } - } - node->appendStyleProperty(prop,myId); -} - -static void parseQPen(QPen &pen, QSvgNode *node, - const QSvgAttributes &attributes, - QSvgHandler *handler) -{ - QString value = attributes.value(QLatin1String("stroke")).toString(); - QString dashArray = attributes.value(QLatin1String("stroke-dasharray")).toString(); - QString dashOffset = attributes.value(QLatin1String("stroke-dashoffset")).toString(); - QString linecap = attributes.value(QLatin1String("stroke-linecap")).toString(); - QString linejoin = attributes.value(QLatin1String("stroke-linejoin")).toString(); - QString miterlimit = attributes.value(QLatin1String("stroke-miterlimit")).toString(); - QString opacity = attributes.value(QLatin1String("stroke-opacity")).toString(); - QString width = attributes.value(QLatin1String("stroke-width")).toString(); - QString myId = someId(attributes); + //fill-opacity atttribute handling + if (!opacity.isEmpty() && opacity != QT_INHERIT) { + prop->setFillOpacity(qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity)))); + } - if (!value.isEmpty() || !width.isEmpty()) { - if (value != QLatin1String("none")) { - if (!value.isEmpty()) { - if (node && value.startsWith(QLatin1String("url"))) { - value = value.remove(0, 3); - QSvgStyleProperty *style = styleFromUrl(node, value); - if (style) { - if (style->type() == QSvgStyleProperty::GRADIENT) { - QBrush b(*((QSvgGradientStyle*)style)->qgradient()); - pen.setBrush(b); - } else if (style->type() == QSvgStyleProperty::SOLID_COLOR) { - pen.setColor( - ((QSvgSolidColorStyle*)style)->qcolor()); - } - } else { - qWarning()<<"QSvgHandler::parsePen could not resolve property" << idFromUrl(value); - } + //fill attribute handling + if ((!value.isEmpty()) && (value != QT_INHERIT) ) { + if (value.startsWith(QLatin1String("url"))) { + value = value.remove(0, 3); + QSvgStyleProperty *style = styleFromUrl(node, value); + if (style) { + prop->setFillStyle(style); } else { - QColor color; - if (constructColor(value, opacity, color, handler)) - pen.setColor(color); + QString id = idFromUrl(value); + prop->setGradientId(id); + prop->setGradientResolved(false); } - //since we could inherit stroke="none" - //we need to reset the style of our stroke to something - pen.setStyle(Qt::SolidLine); - } - if (!width.isEmpty()) { - QSvgHandler::LengthType lt; - qreal widthF = parseLength(width, lt, handler); - //### fixme - if (!widthF) { - pen.setStyle(Qt::NoPen); - return; - } - pen.setWidthF(widthF); - } - qreal penw = pen.widthF(); - - if (!linejoin.isEmpty()) { - if (linejoin == QLatin1String("miter")) - pen.setJoinStyle(Qt::SvgMiterJoin); - else if (linejoin == QLatin1String("round")) - pen.setJoinStyle(Qt::RoundJoin); - else if (linejoin == QLatin1String("bevel")) - pen.setJoinStyle(Qt::BevelJoin); - } - if (!miterlimit.isEmpty()) - pen.setMiterLimit(toDouble(miterlimit)); - - if (!linecap.isEmpty()) { - if (linecap == QLatin1String("butt")) - pen.setCapStyle(Qt::FlatCap); - else if (linecap == QLatin1String("round")) - pen.setCapStyle(Qt::RoundCap); - else if (linecap == QLatin1String("square")) - pen.setCapStyle(Qt::SquareCap); - } - - if (!dashArray.isEmpty()) { - const QChar *s = dashArray.constData(); - QVector<qreal> dashes = parseNumbersList(s); - qreal *d = dashes.data(); - if (penw != 0) - for (int i = 0; i < dashes.size(); ++i) { - *d /= penw; - ++d; - } - pen.setDashPattern(dashes); - } - if (!dashOffset.isEmpty()) { - pen.setDashOffset(toDouble(dashOffset)); + } else if (value != QLatin1String("none")) { + QColor color; + if (resolveColor(value, color, handler)) + prop->setBrush(QBrush(color)); + } else { + prop->setBrush(QBrush(Qt::NoBrush)); } - - } else { - pen.setStyle(Qt::NoPen); } + node->appendStyleProperty(prop, myId); } } + + static QMatrix parseTransformationMatrix(const QString &value) { QMatrix matrix; @@ -917,18 +829,15 @@ static void parsePen(QSvgNode *node, QString miterlimit = attributes.value(QLatin1String("stroke-miterlimit")).toString(); QString opacity = attributes.value(QLatin1String("stroke-opacity")).toString(); QString width = attributes.value(QLatin1String("stroke-width")).toString(); + QString vectorEffect = attributes.value(QLatin1String("vector-effect")).toString(); QString myId = someId(attributes); //qDebug()<<"Node "<<node->type()<<", attrs are "<<value<<width; - /* All the below checks needed because g (or any container element) can have only one of these attributes. - * If it doesn't has any one of these, then processing below not needed */ - if (!value.isEmpty() || !width.isEmpty() || !dashArray.isEmpty() || !linecap.isEmpty() || - !linejoin.isEmpty() || !miterlimit.isEmpty() || !opacity.isEmpty() || !dashOffset.isEmpty() ) { - //if (value != QLatin1String("none")) { - /* If stroke = "none" then also, we need to parse below, because elements like 'g' may - have other defined stroke attributes, which will be inherited by its children */ + !linejoin.isEmpty() || !miterlimit.isEmpty() || !opacity.isEmpty() || + !dashOffset.isEmpty() || !vectorEffect.isEmpty()) { + QSvgStrokeStyle *inherited = static_cast<QSvgStrokeStyle*>(node->parent()->styleProperty( QSvgStyleProperty::STROKE)); @@ -942,14 +851,14 @@ static void parsePen(QSvgNode *node, // stroke-opacity attribute handling qreal strokeAlpha; - if (!opacity.isEmpty() && opacity != QLatin1String("inherit")) { + if (!opacity.isEmpty() && opacity != QT_INHERIT) { strokeAlpha = qMin(qreal(1.0), qMax(qreal(0.0), toDouble(opacity))); } else { strokeAlpha = pen.color().alphaF(); } //stroke attribute handling - if (!value.isEmpty() && value != QLatin1String("inherit")) { + if (!value.isEmpty() && value != QT_INHERIT) { if (value.startsWith(QLatin1String("url"))) { value = value.remove(0, 3); QSvgStyleProperty *style = styleFromUrl(node, value); @@ -961,8 +870,7 @@ static void parsePen(QSvgNode *node, pen.setColor( ((QSvgSolidColorStyle*)style)->qcolor()); } - pen.setStyle(Qt::SolidLine); - stroke = true; + stroke = true; } else { qWarning() << "QSvgHandler::parsePen could not resolve property" << idFromUrl(value); } @@ -986,14 +894,11 @@ static void parsePen(QSvgNode *node, } //stroke-width handling - if (!width.isEmpty() && width != QLatin1String("inherit")) { + if (!width.isEmpty() && width != QT_INHERIT) { qreal widthF; QSvgHandler::LengthType lt; widthF = parseLength(width, lt, handler); pen.setWidthF(widthF); - } else if (inherited){ - qreal widthF = inherited->qpen().widthF(); - pen.setWidthF(widthF); } //stroke-linejoin attribute handling @@ -1018,7 +923,7 @@ static void parsePen(QSvgNode *node, //strok-dasharray attribute handling qreal penw = pen.widthF(); - if (!dashArray.isEmpty() && dashArray != QLatin1String("inherit")) { + if (!dashArray.isEmpty() && dashArray != QT_INHERIT) { const QChar *s = dashArray.constData(); QVector<qreal> dashes = parseNumbersList(s); qreal *d = dashes.data(); @@ -1048,7 +953,7 @@ static void parsePen(QSvgNode *node, //stroke-dashoffset attribute handling - if (!dashOffset.isEmpty() && dashOffset != QLatin1String("inherit")) { + if (!dashOffset.isEmpty() && dashOffset != QT_INHERIT) { qreal doffset = toDouble(dashOffset); if (penw != 0) doffset /= penw; @@ -1064,7 +969,15 @@ static void parsePen(QSvgNode *node, pen.setDashOffset(doffset); } - if (!miterlimit.isEmpty() && miterlimit != QLatin1String("inherit")) + //vector-effect attribute handling + if (!vectorEffect.isEmpty()) { + if (vectorEffect == QLatin1String("non-scaling-stroke")) + pen.setCosmetic(true); + else if (vectorEffect == QLatin1String("none")) + pen.setCosmetic(false); + } + + if (!miterlimit.isEmpty() && miterlimit != QT_INHERIT) pen.setMiterLimit(toDouble(miterlimit)); QSvgStrokeStyle *prop = new QSvgStrokeStyle(pen); @@ -1073,182 +986,84 @@ static void parsePen(QSvgNode *node, } } - -static bool parseQBrush(const QSvgAttributes &attributes, QSvgNode *node, - QBrush &brush, QSvgHandler *handler) -{ - QString value = attributes.value(QLatin1String("fill")).toString(); - QString opacity = attributes.value(QLatin1String("fill-opacity")).toString(); - - QColor color; - if (!value.isEmpty() || !opacity.isEmpty()) { - if (value.startsWith(QLatin1String("url"))) { - value = value.remove(0, 3); - QSvgStyleProperty *style = styleFromUrl(node, value); - if (style) { - switch (style->type()) { - case QSvgStyleProperty::FILL: - { - brush = static_cast<QSvgFillStyle*>(style)->qbrush(); - break; - } - case QSvgStyleProperty::SOLID_COLOR: - { - brush = QBrush(static_cast<QSvgSolidColorStyle*>(style)->qcolor()); - break; - } - case QSvgStyleProperty::GRADIENT: - { - brush = QBrush(*static_cast<QSvgGradientStyle*>(style)->qgradient()); - break; - } - default: - qWarning("Cannot use property \"%s\" as brush.", qPrintable(idFromUrl(value))); - } - } else { - qWarning("Couldn't resolve property: %s", qPrintable(idFromUrl(value))); - } - } else if (value != QLatin1String("none")) { - if (constructColor(value, opacity, color, handler)) { - brush.setStyle(Qt::SolidPattern); - brush.setColor(color); - } - } else { - brush = QBrush(Qt::NoBrush); - } - return true; - } - return false; -} - -static bool parseQFont(const QSvgAttributes &attributes, - QFont &font, qreal &fontSize, QSvgHandler *handler) +static void parseFont(QSvgNode *node, + const QSvgAttributes &attributes, + QSvgHandler *handler) { QString family = attributes.value(QLatin1String("font-family")).toString(); QString size = attributes.value(QLatin1String("font-size")).toString(); QString style = attributes.value(QLatin1String("font-style")).toString(); QString weight = attributes.value(QLatin1String("font-weight")).toString(); + QString variant = attributes.value(QLatin1String("font-variant")).toString(); + QString anchor = attributes.value(QLatin1String("text-anchor")).toString(); - if (!family.isEmpty() || !size.isEmpty() || - !style.isEmpty() || !weight.isEmpty()) { + if (family.isEmpty() && size.isEmpty() && style.isEmpty() && weight.isEmpty() && variant.isEmpty() && anchor.isEmpty()) + return; - if (!family.isEmpty()) { - font.setFamily(family.trimmed()); - } - if (!size.isEmpty()) { - if (size == QLatin1String("inherit")) { - //inherited already - } else { - QSvgHandler::LengthType dummy; // should always be pixel size - fontSize = parseLength(size, dummy, handler); - if (fontSize <= 0) - fontSize = 1; - } - font.setPixelSize(qMax(1, int(fontSize))); - } - if (!style.isEmpty()) { - if (style == QLatin1String("normal")) { - font.setStyle(QFont::StyleNormal); - } else if (style == QLatin1String("italic")) { - font.setStyle(QFont::StyleItalic); - } else if (style == QLatin1String("oblique")) { - font.setStyle(QFont::StyleOblique); - } else if (style == QLatin1String("inherit")) { - //inherited already - } - } - if (!weight.isEmpty()) { - bool ok = false; - int weightNum = weight.toInt(&ok); - if (ok) { - switch (weightNum) { - case 100: - case 200: - font.setWeight(QFont::Light); - break; - case 300: - case 400: - font.setWeight(QFont::Normal); - break; - case 500: - case 600: - font.setWeight(QFont::DemiBold); - break; - case 700: - case 800: - font.setWeight(QFont::Bold); - break; - case 900: - font.setWeight(QFont::Black); - break; - default: - break; - } - } else { - if (weight == QLatin1String("normal")) { - font.setWeight(QFont::Normal); - } else if (weight == QLatin1String("bold")) { - font.setWeight(QFont::Bold); - } else if (weight == QLatin1String("bolder")) { - font.setWeight(QFont::DemiBold); - } else if (weight == QLatin1String("lighter")) { - font.setWeight(QFont::Light); - } - } - } - // QFontInfo fi(font); - // font.setPointSize(fi.pointSize()); - return true; + QString id = someId(attributes); + QSvgTinyDocument *doc = node->document(); + QSvgFontStyle *fontStyle = 0; + if (!family.isEmpty()) { + QSvgFont *svgFont = doc->svgFont(family); + if (svgFont) + fontStyle = new QSvgFontStyle(svgFont, doc); } + if (!fontStyle) + fontStyle = new QSvgFontStyle; - return false; -} + if (!family.isEmpty() && family != QT_INHERIT) + fontStyle->setFamily(family.trimmed()); -static void parseFont(QSvgNode *node, - const QSvgAttributes &attributes, - QSvgHandler *handler) -{ - QFont font; - font.setPixelSize(12); - qreal fontSize = font.pixelSize(); - - QSvgFontStyle *inherited = - static_cast<QSvgFontStyle*>(node->styleProperty( - QSvgStyleProperty::FONT)); - if (!inherited) - inherited = - static_cast<QSvgFontStyle*>(node->parent()->styleProperty( - QSvgStyleProperty::FONT)); - if (inherited) { - font = inherited->qfont(); - fontSize = inherited->pointSize(); - } - // group or any container element can have only text-anchor and should - // be processed, because its children can inherit from it. - // So checking for text-anchor before parseQfont() - QString anchor = attributes.value(QLatin1String("text-anchor")).toString(); - if (parseQFont(attributes, font, fontSize, handler) || (!anchor.isEmpty())) { - QString myId = someId(attributes); - //QString anchor = attributes.value(QLatin1String("text-anchor")).toString(); - QSvgTinyDocument *doc = node->document(); - QSvgFontStyle *fontStyle = 0; - QString family = (font.family().isEmpty())?myId:font.family(); - if (!family.isEmpty()) { - QSvgFont *svgFont = doc->svgFont(family); - if (svgFont) { - fontStyle = new QSvgFontStyle(svgFont, doc); - fontStyle->setPointSize(fontSize); - } + if (!size.isEmpty() && size != QT_INHERIT) { + QSvgHandler::LengthType dummy; // should always be pixel size + fontStyle->setSize(parseLength(size, dummy, handler)); + } + + if (!style.isEmpty() && style != QT_INHERIT) { + if (style == QLatin1String("normal")) { + fontStyle->setStyle(QFont::StyleNormal); + } else if (style == QLatin1String("italic")) { + fontStyle->setStyle(QFont::StyleItalic); + } else if (style == QLatin1String("oblique")) { + fontStyle->setStyle(QFont::StyleOblique); } - if (!fontStyle) { - fontStyle = new QSvgFontStyle(font, node->document()); - fontStyle->setPointSize(fontSize); + } + + if (!weight.isEmpty() && weight != QT_INHERIT) { + bool ok = false; + int weightNum = weight.toInt(&ok); + if (ok) { + fontStyle->setWeight(weightNum); + } else { + if (weight == QLatin1String("normal")) { + fontStyle->setWeight(400); + } else if (weight == QLatin1String("bold")) { + fontStyle->setWeight(700); + } else if (weight == QLatin1String("bolder")) { + fontStyle->setWeight(QSvgFontStyle::BOLDER); + } else if (weight == QLatin1String("lighter")) { + fontStyle->setWeight(QSvgFontStyle::LIGHTER); + } } - if (!anchor.isEmpty()) - fontStyle->setTextAnchor(anchor); + } - node->appendStyleProperty(fontStyle, myId); + if (!variant.isEmpty() && variant != QT_INHERIT) { + if (variant == QLatin1String("normal")) + fontStyle->setVariant(QFont::MixedCase); + else if (variant == QLatin1String("small-caps")) + fontStyle->setVariant(QFont::SmallCaps); } + + if (!anchor.isEmpty() && anchor != QT_INHERIT) { + if (anchor == QLatin1String("start")) + fontStyle->setTextAnchor(Qt::AlignLeft); + if (anchor == QLatin1String("middle")) + fontStyle->setTextAnchor(Qt::AlignHCenter); + else if (anchor == QLatin1String("end")) + fontStyle->setTextAnchor(Qt::AlignRight); + } + + node->appendStyleProperty(fontStyle, id); } static void parseTransform(QSvgNode *node, @@ -1275,7 +1090,7 @@ static void parseVisibility(QSvgNode *node, QString value = attributes.value(QLatin1String("visibility")).toString(); QSvgNode *parent = node->parent(); - if (parent && (value.isEmpty() || value == QLatin1String("inherit"))) + if (parent && (value.isEmpty() || value == QT_INHERIT)) node->setVisible(parent->isVisible()); else if (value == QLatin1String("hidden") || value == QLatin1String("collapse")) { node->setVisible(false); @@ -1886,105 +1701,6 @@ static void cssStyleLookup(QSvgNode *node, parseStyle(node, attributes, handler); } -static bool parseDefaultTextStyle(QSvgNode *node, - const QXmlStreamAttributes &attributes, - bool initial, - QSvgHandler *handler) -{ - Q_ASSERT(node->type() == QSvgText::TEXT || node->type() == QSvgNode::TEXTAREA); - QSvgText *textNode = static_cast<QSvgText*>(node); - - QSvgAttributes attrs(attributes, handler); - - QString fontFamily = attrs.value(QString(), QLatin1String("font-family")).toString(); - - QString anchor = attrs.value(QString(), QLatin1String("text-anchor")).toString(); - - QSvgFontStyle *fontStyle = static_cast<QSvgFontStyle*>( - node->styleProperty(QSvgStyleProperty::FONT)); - if (fontStyle) { - QSvgTinyDocument *doc = fontStyle->doc(); - if (doc && fontStyle->svgFont()) { - cssStyleLookup(node, handler, handler->selector()); - parseStyle(node, attrs, handler); - return true; - } - } else if (!fontFamily.isEmpty()) { - QSvgTinyDocument *doc = node->document(); - QSvgFont *svgFont = doc->svgFont(fontFamily); - if (svgFont) { - cssStyleLookup(node, handler, handler->selector()); - parseStyle(node, attrs, handler); - return true; - } - } - - QTextCharFormat format; - QBrush brush(QColor(0, 0, 0)); - QFont font; - font.setPixelSize(12); - qreal fontSize = font.pixelSize(); - - if (!initial) { - font = textNode->topFormat().font(); - fontSize = font.pixelSize() / textNode->scale(); - brush = textNode->topFormat().foreground(); - } else { - QSvgFontStyle *fontStyle = static_cast<QSvgFontStyle*>( - node->styleProperty(QSvgStyleProperty::FONT)); - if (!fontStyle) - fontStyle = static_cast<QSvgFontStyle*>( - node->parent()->styleProperty(QSvgStyleProperty::FONT)); - if (fontStyle) { - font = fontStyle->qfont(); - fontSize = fontStyle->pointSize(); - if (anchor.isEmpty() || anchor == QLatin1String("inherit")) - anchor = fontStyle->textAnchor(); - } - - Qt::Alignment align = Qt::AlignLeft; - if (anchor == QLatin1String("middle")) - align = Qt::AlignHCenter; - else if (anchor == QLatin1String("end")) - align = Qt::AlignRight; - textNode->setTextAlignment(align); - - QSvgFillStyle *fillStyle = static_cast<QSvgFillStyle*>( - node->styleProperty(QSvgStyleProperty::FILL)); - if (fillStyle) - brush = fillStyle->qbrush(); - } - - if (parseQFont(attrs, font, fontSize, handler) || initial) { - if (initial) - textNode->setScale(100 / fontSize); - font.setPixelSize(qMax(1, int(fontSize * textNode->scale()))); - format.setFont(font); - } - - if (parseQBrush(attrs, node, brush, handler) || initial) { - if (brush.style() != Qt::NoBrush || initial) - format.setForeground(brush); - } - - QPen pen(Qt::NoPen); -// QSvgStrokeStyle *inherited = -// static_cast<QSvgStrokeStyle*>(node->parent()->styleProperty( -// QSvgStyleProperty::STROKE)); -// if (inherited) -// pen = inherited->qpen(); - parseQPen(pen, node, attrs, handler); - if (pen.style() != Qt::NoPen) { - format.setTextOutline(pen); - } - - parseTransform(node, attrs, handler); - - textNode->insertFormat(format); - - return true; -} - static inline QStringList stringToList(const QString &str) { QStringList lst = str.split(QLatin1Char(','), QString::SkipEmptyParts); @@ -2143,7 +1859,7 @@ static inline QSvgNode::DisplayMode displayStringToEnum(const QString &str) return QSvgNode::TableCaptionMode; } else if (str == QLatin1String("none")) { return QSvgNode::NoneMode; - } else if (str == QLatin1String("inherit")) { + } else if (str == QT_INHERIT) { return QSvgNode::InheritMode; } return QSvgNode::BlockMode; @@ -3107,7 +2823,6 @@ static QSvgNode *createSvgNode(QSvgNode *parent, node->setHeight(int(height), type == QSvgHandler::LT_PERCENT); } - if (!viewBoxStr.isEmpty()) { QStringList lst = viewBoxStr.split(QLatin1Char(' '), QString::SkipEmptyParts); if (lst.count() != 4) @@ -3157,7 +2872,7 @@ static bool parseTbreakNode(QSvgNode *parent, { if (parent->type() != QSvgNode::TEXTAREA) return false; - static_cast<QSvgText*>(parent)->insertLineBreak(); + static_cast<QSvgText*>(parent)->addLineBreak(); return true; } @@ -3172,12 +2887,6 @@ static QSvgNode *createTextNode(QSvgNode *parent, qreal nx = parseLength(x, type, handler); qreal ny = parseLength(y, type, handler); - //### not to pixels but to the default coordinate system - // and text should be already in the correct coordinate - // system here - //nx = convertToPixels(nx, true, type); - //ny = convertToPixels(ny, true, type); - QSvgNode *text = new QSvgText(parent, QPointF(nx, ny)); return text; } @@ -3196,6 +2905,13 @@ static QSvgNode *createTextAreaNode(QSvgNode *parent, return node; } +static QSvgNode *createTspanNode(QSvgNode *parent, + const QXmlStreamAttributes &, + QSvgHandler *) +{ + return new QSvgTspan(parent); +} + static bool parseTitleNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *) @@ -3204,15 +2920,6 @@ static bool parseTitleNode(QSvgNode *parent, return true; } -static bool parseTspanNode(QSvgNode *parent, - const QXmlStreamAttributes &attributes, - QSvgHandler *handler) -{ - - cssStyleLookup(parent, handler, handler->selector()); - return parseDefaultTextStyle(parent, attributes, false, handler); -} - static QSvgNode *createUseNode(QSvgNode *parent, const QXmlStreamAttributes &attributes, QSvgHandler *handler) @@ -3326,6 +3033,7 @@ static FactoryMethod findGraphicsFactory(const QString &name) case 't': if (ref == QLatin1String("ext")) return createTextNode; if (ref == QLatin1String("extArea")) return createTextAreaNode; + if (ref == QLatin1String("span")) return createTspanNode; break; case 'u': if (ref == QLatin1String("se")) return createUseNode; @@ -3382,7 +3090,6 @@ static ParseMethod findUtilFactory(const QString &name) case 't': if (ref == QLatin1String("break")) return parseTbreakNode; if (ref == QLatin1String("itle")) return parseTitleNode; - if (ref == QLatin1String("span")) return parseTspanNode; break; default: break; @@ -3591,16 +3298,33 @@ bool QSvgHandler::startElement(const QString &localName, group->addChild(node, someId(attributes)); } break; + case QSvgNode::TEXT: + case QSvgNode::TEXTAREA: + if (node->type() == QSvgNode::TSPAN) { + static_cast<QSvgText *>(m_nodes.top())->addTspan(static_cast<QSvgTspan *>(node)); + } else { + qWarning("\'text\' or \'textArea\' element contains invalid element type."); + delete node; + node = 0; + } + break; default: - Q_ASSERT(!"not a grouping element is the parent"); + qWarning("Could not add child element to parent element because the types are incorrect."); + delete node; + node = 0; + break; } - parseCoreNode(node, attributes); - cssStyleLookup(node, this, m_selector); - if (node->type() != QSvgNode::TEXT && node->type() != QSvgNode::TEXTAREA) + if (node) { + parseCoreNode(node, attributes); + cssStyleLookup(node, this, m_selector); parseStyle(node, attributes, this); - else - parseDefaultTextStyle(node, attributes, true, this); + if (node->type() == QSvgNode::TEXT || node->type() == QSvgNode::TEXTAREA) { + static_cast<QSvgText *>(node)->setWhitespaceMode(m_whitespaceMode.top()); + } else if (node->type() == QSvgNode::TSPAN) { + static_cast<QSvgTspan *>(node)->setWhitespaceMode(m_whitespaceMode.top()); + } + } } } else if (ParseMethod method = findUtilFactory(localName)) { Q_ASSERT(!m_nodes.isEmpty()); @@ -3657,13 +3381,8 @@ bool QSvgHandler::endElement(const QStringRef &localName) return true; } - if (m_inStyle && localName == QLatin1String("style")) { + if (m_inStyle && localName == QLatin1String("style")) m_inStyle = false; - } else if (m_nodes.top()->type() == QSvgNode::TEXT || m_nodes.top()->type() == QSvgNode::TEXTAREA) { - QSvgText *node = static_cast<QSvgText*>(m_nodes.top()); - if (localName == QLatin1String("tspan")) - node->popFormat(); - } if (node == Graphics) { // Iterate through the m_renderers to resolve any unresolved gradients. @@ -3711,8 +3430,9 @@ bool QSvgHandler::characters(const QStringRef &str) return true; if (m_nodes.top()->type() == QSvgNode::TEXT || m_nodes.top()->type() == QSvgNode::TEXTAREA) { - QSvgText *node = static_cast<QSvgText*>(m_nodes.top()); - node->insertText(str.toString(), m_whitespaceMode.top()); + static_cast<QSvgText*>(m_nodes.top())->addText(str.toString()); + } else if (m_nodes.top()->type() == QSvgNode::TSPAN) { + static_cast<QSvgTspan*>(m_nodes.top())->addText(str.toString()); } return true; diff --git a/src/svg/qsvgnode_p.h b/src/svg/qsvgnode_p.h index f203ea7..315f228 100644 --- a/src/svg/qsvgnode_p.h +++ b/src/svg/qsvgnode_p.h @@ -86,6 +86,7 @@ public: RECT, TEXT, TEXTAREA, + TSPAN, USE, VIDEO }; diff --git a/src/svg/qsvgstyle.cpp b/src/svg/qsvgstyle.cpp index c3c0a68..6ed3dc2 100644 --- a/src/svg/qsvgstyle.cpp +++ b/src/svg/qsvgstyle.cpp @@ -59,6 +59,9 @@ QT_BEGIN_NAMESPACE QSvgExtraStates::QSvgExtraStates() : fillOpacity(1.0) + , svgFont(0) + , textAnchor(Qt::AlignLeft) + , fontWeight(400) { } @@ -191,39 +194,94 @@ void QSvgViewportFillStyle::revert(QPainter *p, QSvgExtraStates &) } QSvgFontStyle::QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc) - : m_font(font), m_pointSize(24), m_doc(doc) -{ -} - -QSvgFontStyle::QSvgFontStyle(const QFont &font, QSvgTinyDocument *doc) - : m_font(0), m_pointSize(24), m_doc(doc), m_qfont(font) -{ -} - - -void QSvgFontStyle::setPointSize(qreal size) -{ - m_pointSize = size; -} - -qreal QSvgFontStyle::pointSize() const -{ - return m_pointSize; -} - -void QSvgFontStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &) -{ - if (!m_font) { - m_oldFont = p->font(); - p->setFont(m_qfont); + : m_svgFont(font) + , m_doc(doc) + , m_familySet(0) + , m_sizeSet(0) + , m_styleSet(0) + , m_variantSet(0) + , m_weightSet(0) + , m_textAnchorSet(0) +{ +} + +QSvgFontStyle::QSvgFontStyle() + : m_svgFont(0) + , m_doc(0) + , m_familySet(0) + , m_sizeSet(0) + , m_styleSet(0) + , m_variantSet(0) + , m_weightSet(0) + , m_textAnchorSet(0) +{ +} + +int QSvgFontStyle::SVGToQtWeight(int weight) { + switch (weight) { + case 100: + case 200: + return QFont::Light; + case 300: + case 400: + return QFont::Normal; + case 500: + case 600: + return QFont::DemiBold; + case 700: + case 800: + return QFont::Bold; + case 900: + return QFont::Black; + } + return QFont::Normal; +} + +void QSvgFontStyle::apply(QPainter *p, const QRectF &, QSvgNode *, QSvgExtraStates &states) +{ + m_oldQFont = p->font(); + m_oldSvgFont = states.svgFont; + m_oldTextAnchor = states.textAnchor; + m_oldWeight = states.fontWeight; + + if (m_textAnchorSet) + states.textAnchor = m_textAnchor; + + QFont font = m_oldQFont; + if (m_familySet) { + states.svgFont = m_svgFont; + font.setFamily(m_qfont.family()); + } + + if (m_sizeSet) + font.setPointSize(m_qfont.pointSizeF()); + + if (m_styleSet) + font.setStyle(m_qfont.style()); + + if (m_variantSet) + font.setCapitalization(m_qfont.capitalization()); + + if (m_weightSet) { + if (m_weight == BOLDER) { + states.fontWeight = qMin(states.fontWeight + 100, 900); + } else if (m_weight == LIGHTER) { + states.fontWeight = qMax(states.fontWeight - 100, 100); + } else { + states.fontWeight = m_weight; + } + font.setWeight(SVGToQtWeight(states.fontWeight)); } + + p->setFont(font); } -void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &) +void QSvgFontStyle::revert(QPainter *p, QSvgExtraStates &states) { - if (!m_font) { - p->setFont(m_oldFont); - } + p->setFont(m_oldQFont); + states.svgFont = m_oldSvgFont; + states.textAnchor = m_oldTextAnchor; + states.fontWeight = m_oldWeight; } QSvgStrokeStyle::QSvgStrokeStyle(const QPen &pen) @@ -799,16 +857,6 @@ QSvgStyleProperty::Type QSvgAnimateColor::type() const return ANIMATE_COLOR; } -QString QSvgFontStyle::textAnchor() const -{ - return m_textAnchor; -} - -void QSvgFontStyle::setTextAnchor(const QString &anchor) -{ - m_textAnchor = anchor; -} - QSvgOpacityStyle::QSvgOpacityStyle(qreal opacity) : m_opacity(opacity) { diff --git a/src/svg/qsvgstyle_p.h b/src/svg/qsvgstyle_p.h index 70ecf5b..bd28bb6 100644 --- a/src/svg/qsvgstyle_p.h +++ b/src/svg/qsvgstyle_p.h @@ -141,7 +141,11 @@ private: struct QSvgExtraStates { QSvgExtraStates(); + qreal fillOpacity; + QSvgFont *svgFont; + Qt::Alignment textAnchor; + int fontWeight; }; class QSvgStyleProperty : public QSvgRefCounted @@ -307,41 +311,85 @@ private: class QSvgFontStyle : public QSvgStyleProperty { public: + static const int LIGHTER = -1; + static const int BOLDER = 1; + QSvgFontStyle(QSvgFont *font, QSvgTinyDocument *doc); - QSvgFontStyle(const QFont &font, QSvgTinyDocument *doc); + QSvgFontStyle(); virtual void apply(QPainter *p, const QRectF &, QSvgNode *node, QSvgExtraStates &states); virtual void revert(QPainter *p, QSvgExtraStates &states); virtual Type type() const; - void setPointSize(qreal size); - qreal pointSize() const; + void setSize(qreal size) + { + // Store the _pixel_ size in the font. Since QFont::setPixelSize() only takes an int, call + // QFont::SetPointSize() instead. Set proper font size just before rendering. + m_qfont.setPointSize(size); + m_sizeSet = 1; + } - //### hack to avoid having a separate style element for text-anchor - QString textAnchor() const; - void setTextAnchor(const QString &anchor); + void setTextAnchor(Qt::Alignment anchor) + { + m_textAnchor = anchor; + m_textAnchorSet = 1; + } - QSvgFont * svgFont() const + void setFamily(const QString &family) { - return m_font; + m_qfont.setFamily(family); + m_familySet = 1; + } + + void setStyle(QFont::Style fontStyle) { + m_qfont.setStyle(fontStyle); + m_styleSet = 1; } - QSvgTinyDocument *doc() const + + void setVariant(QFont::Capitalization fontVariant) { - return m_doc; + m_qfont.setCapitalization(fontVariant); + m_variantSet = 1; } - const QFont & qfont() const + static int SVGToQtWeight(int weight); + + void setWeight(int weight) + { + m_weight = weight; + m_weightSet = 1; + } + + QSvgFont * svgFont() const + { + return m_svgFont; + } + + const QFont &qfont() const { return m_qfont; } + + QSvgTinyDocument *doc() const {return m_doc;} + private: - QSvgFont *m_font; - qreal m_pointSize; + QSvgFont *m_svgFont; QSvgTinyDocument *m_doc; + QFont m_qfont; - QString m_textAnchor; + int m_weight; + Qt::Alignment m_textAnchor; - QFont m_qfont; - QFont m_oldFont; + QSvgFont *m_oldSvgFont; + QFont m_oldQFont; + Qt::Alignment m_oldTextAnchor; + int m_oldWeight; + + unsigned m_familySet : 1; + unsigned m_sizeSet : 1; + unsigned m_styleSet : 1; + unsigned m_variantSet : 1; + unsigned m_weightSet : 1; + unsigned m_textAnchorSet : 1; }; class QSvgStrokeStyle : public QSvgStyleProperty diff --git a/src/testlib/qabstracttestlogger_p.h b/src/testlib/qabstracttestlogger_p.h index a0e8a1e..588184e 100644 --- a/src/testlib/qabstracttestlogger_p.h +++ b/src/testlib/qabstracttestlogger_p.h @@ -99,6 +99,42 @@ public: static bool isTtyOutput(); }; +struct QTestCharBuffer +{ + inline QTestCharBuffer() + : buf(0) + {} + + inline ~QTestCharBuffer() + { + delete[] buf; + buf = 0; + } + + inline operator void*() + { + return buf; + } + + inline operator char*() + { + return buf; + } + + inline operator char**() + { + return &buf; + } + + inline const char* constData() const + { + return buf; + } + +private: + char* buf; +}; + QT_END_NAMESPACE #endif diff --git a/src/testlib/qplaintestlogger.cpp b/src/testlib/qplaintestlogger.cpp index 071b55e..7bebaa1 100644 --- a/src/testlib/qplaintestlogger.cpp +++ b/src/testlib/qplaintestlogger.cpp @@ -168,7 +168,7 @@ namespace QTest { QTEST_ASSERT(type); QTEST_ASSERT(msg); - char buf[1024]; + QTestCharBuffer buf; const char *fn = QTestResult::currentTestFunction() ? QTestResult::currentTestFunction() : "UnknownTestFunc"; @@ -178,7 +178,7 @@ namespace QTest { : ""; const char *filler = (tag[0] && gtag[0]) ? ":" : ""; if (file) { - QTest::qt_snprintf(buf, sizeof(buf), "%s: %s::%s(%s%s%s)%s%s\n" + QTest::qt_asprintf(buf, "%s: %s::%s(%s%s%s)%s%s\n" #ifdef Q_OS_WIN "%s(%d) : failure location\n" #else @@ -187,10 +187,12 @@ namespace QTest { , type, QTestResult::currentTestObjectName(), fn, gtag, filler, tag, msg[0] ? " " : "", msg, file, line); } else { - QTest::qt_snprintf(buf, sizeof(buf), "%s: %s::%s(%s%s%s)%s%s\n", + QTest::qt_asprintf(buf, "%s: %s::%s(%s%s%s)%s%s\n", type, QTestResult::currentTestObjectName(), fn, gtag, filler, tag, msg[0] ? " " : "", msg); } + // In colored mode, printf above stripped our nonprintable control characters. + // Put them back. memcpy(buf, type, strlen(type)); outputMessage(buf); } diff --git a/src/testlib/qtest_global.h b/src/testlib/qtest_global.h index b5b0fc0..c40f0ad 100644 --- a/src/testlib/qtest_global.h +++ b/src/testlib/qtest_global.h @@ -82,6 +82,7 @@ namespace QTest enum TestFailMode { Abort = 1, Continue = 2 }; int Q_TESTLIB_EXPORT qt_snprintf(char *str, int size, const char *format, ...); + int qt_asprintf(char **str, const char *format, ...); } QT_END_NAMESPACE diff --git a/src/testlib/qtestbasicstreamer.cpp b/src/testlib/qtestbasicstreamer.cpp index 5fe9d4d..aac57ba 100644 --- a/src/testlib/qtestbasicstreamer.cpp +++ b/src/testlib/qtestbasicstreamer.cpp @@ -68,39 +68,39 @@ QTestBasicStreamer::QTestBasicStreamer() QTestBasicStreamer::~QTestBasicStreamer() {} -void QTestBasicStreamer::formatStart(const QTestElement *element, char *formatted) const +void QTestBasicStreamer::formatStart(const QTestElement *element, char **formatted) const { if(!element || !formatted ) return; - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } -void QTestBasicStreamer::formatEnd(const QTestElement *element, char *formatted) const +void QTestBasicStreamer::formatEnd(const QTestElement *element, char **formatted) const { if(!element || !formatted ) return; - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } -void QTestBasicStreamer::formatBeforeAttributes(const QTestElement *element, char *formatted) const +void QTestBasicStreamer::formatBeforeAttributes(const QTestElement *element, char **formatted) const { if(!element || !formatted ) return; - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } -void QTestBasicStreamer::formatAfterAttributes(const QTestElement *element, char *formatted) const +void QTestBasicStreamer::formatAfterAttributes(const QTestElement *element, char **formatted) const { if(!element || !formatted ) return; - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } -void QTestBasicStreamer::formatAttributes(const QTestElement *, const QTestElementAttribute *attribute, char *formatted) const +void QTestBasicStreamer::formatAttributes(const QTestElement *, const QTestElementAttribute *attribute, char **formatted) const { if(!attribute || !formatted ) return; - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } void QTestBasicStreamer::output(QTestElement *element) const @@ -113,7 +113,7 @@ void QTestBasicStreamer::output(QTestElement *element) const void QTestBasicStreamer::outputElements(QTestElement *element, bool) const { - char buf[1024]; + QTestCharBuffer buf; bool hasChildren; /* Elements are in reverse order of occurrence, so start from the end and work @@ -148,7 +148,7 @@ void QTestBasicStreamer::outputElements(QTestElement *element, bool) const void QTestBasicStreamer::outputElementAttributes(const QTestElement* element, QTestElementAttribute *attribute) const { - char buf[1024]; + QTestCharBuffer buf; while(attribute){ formatAttributes(element, attribute, buf); outputString(buf); diff --git a/src/testlib/qtestbasicstreamer.h b/src/testlib/qtestbasicstreamer.h index 84d1bce..432dd22 100644 --- a/src/testlib/qtestbasicstreamer.h +++ b/src/testlib/qtestbasicstreamer.h @@ -71,11 +71,11 @@ class QTestBasicStreamer const QTestLogger *logger() const; protected: - virtual void formatStart(const QTestElement *element = 0, char *formatted = 0) const; - virtual void formatEnd(const QTestElement *element = 0, char *formatted = 0) const; - virtual void formatBeforeAttributes(const QTestElement *element = 0, char *formatted = 0) const; - virtual void formatAfterAttributes(const QTestElement *element = 0, char *formatted = 0) const; - virtual void formatAttributes(const QTestElement *element = 0, const QTestElementAttribute *attribute = 0, char *formatted = 0) const; + virtual void formatStart(const QTestElement *element, char **formatted) const; + virtual void formatEnd(const QTestElement *element, char **formatted) const; + virtual void formatBeforeAttributes(const QTestElement *element, char **formatted) const; + virtual void formatAfterAttributes(const QTestElement *element, char **formatted) const; + virtual void formatAttributes(const QTestElement *element, const QTestElementAttribute *attribute, char **formatted) const; virtual void outputElements(QTestElement *element, bool isChildElement = false) const; virtual void outputElementAttributes(const QTestElement *element, QTestElementAttribute *attribute) const; diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index cb05400..ac4ca83 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -808,6 +808,53 @@ namespace QTest static int eventDelay = -1; static int keyVerbose = -1; +void filter_unprintable(char *str) +{ + char *idx = str; + while (*idx) { + if (((*idx < 0x20 && *idx != '\n' && *idx != '\t') || *idx > 0x7e)) + *idx = '?'; + ++idx; + } +} + +/*! \internal + */ +int qt_asprintf(char **str, const char *format, ...) +{ + static const int MAXSIZE = 1024*1024*2; + + int size = 32; + delete[] *str; + *str = new char[size]; + + va_list ap; + int res = 0; + + for (;;) { + va_start(ap, format); + res = qvsnprintf(*str, size, format, ap); + va_end(ap); + (*str)[size - 1] = '\0'; + if (res >= 0 && res < size) { + // We succeeded + break; + } + // buffer wasn't big enough, try again. + // Note, we're assuming that a result of -1 is always due to running out of space. + size *= 2; + if (size > MAXSIZE) { + break; + } + delete[] *str; + *str = new char[size]; + } + + filter_unprintable(*str); + + return res; +} + /*! \internal */ int qt_snprintf(char *str, int size, const char *format, ...) @@ -820,12 +867,8 @@ int qt_snprintf(char *str, int size, const char *format, ...) va_end(ap); str[size - 1] = '\0'; - char *idx = str; - while (*idx) { - if (((*idx < 0x20 && *idx != '\n' && *idx != '\t') || *idx > 0x7e)) - *idx = '?'; - ++idx; - } + filter_unprintable(str); + return res; } diff --git a/src/testlib/qtestlightxmlstreamer.cpp b/src/testlib/qtestlightxmlstreamer.cpp index 170938a..e176201 100644 --- a/src/testlib/qtestlightxmlstreamer.cpp +++ b/src/testlib/qtestlightxmlstreamer.cpp @@ -59,61 +59,55 @@ QTestLightXmlStreamer::QTestLightXmlStreamer() QTestLightXmlStreamer::~QTestLightXmlStreamer() {} -void QTestLightXmlStreamer::formatStart(const QTestElement *element, char *formatted) const +void QTestLightXmlStreamer::formatStart(const QTestElement *element, char **formatted) const { if(!element || !formatted) return; switch(element->elementType()){ case QTest::LET_TestCase: { - char quotedTf[950]; - QXmlTestLogger::xmlQuote(quotedTf, element->attributeValue(QTest::AI_Name), - sizeof(quotedTf)); + QTestCharBuffer quotedTf; + QXmlTestLogger::xmlQuote(quotedTf, element->attributeValue(QTest::AI_Name)); - QTest::qt_snprintf(formatted, 1024, "<TestFunction name=\"%s\">\n", quotedTf); + QTest::qt_asprintf(formatted, "<TestFunction name=\"%s\">\n", quotedTf.constData()); break; } case QTest::LET_Failure: { - char cdataDesc[900]; - QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description), - sizeof(cdataDesc)); + QTestCharBuffer cdataDesc; + QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description)); - QTest::qt_snprintf(formatted, 1024, " <Description><![CDATA[%s]]></Description>\n", - cdataDesc); + QTest::qt_asprintf(formatted, " <Description><![CDATA[%s]]></Description>\n", + cdataDesc.constData()); break; } case QTest::LET_Error: { // assuming type and attribute names don't need quoting - char quotedFile[128]; - char cdataDesc[700]; - QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File), - sizeof(quotedFile)); - QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description), - sizeof(cdataDesc)); - - QTest::qt_snprintf(formatted, 1024, "<Message type=\"%s\" %s=\"%s\" %s=\"%s\">\n <Description><![CDATA[%s]]></Description>\n</Message>\n", + QTestCharBuffer quotedFile; + QTestCharBuffer cdataDesc; + QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File)); + QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description)); + + QTest::qt_asprintf(formatted, "<Message type=\"%s\" %s=\"%s\" %s=\"%s\">\n <Description><![CDATA[%s]]></Description>\n</Message>\n", element->attributeValue(QTest::AI_Type), element->attributeName(QTest::AI_File), - quotedFile, + quotedFile.constData(), element->attributeName(QTest::AI_Line), element->attributeValue(QTest::AI_Line), - cdataDesc); + cdataDesc.constData()); break; } case QTest::LET_Benchmark: { // assuming value and iterations don't need quoting - char quotedMetric[256]; - char quotedTag[256]; - QXmlTestLogger::xmlQuote(quotedMetric, element->attributeValue(QTest::AI_Metric), - sizeof(quotedMetric)); - QXmlTestLogger::xmlQuote(quotedTag, element->attributeValue(QTest::AI_Tag), - sizeof(quotedTag)); - - QTest::qt_snprintf(formatted, 1024, "<BenchmarkResult %s=\"%s\" %s=\"%s\" %s=\"%s\" %s=\"%s\" />\n", + QTestCharBuffer quotedMetric; + QTestCharBuffer quotedTag; + QXmlTestLogger::xmlQuote(quotedMetric, element->attributeValue(QTest::AI_Metric)); + QXmlTestLogger::xmlQuote(quotedTag, element->attributeValue(QTest::AI_Tag)); + + QTest::qt_asprintf(formatted, "<BenchmarkResult %s=\"%s\" %s=\"%s\" %s=\"%s\" %s=\"%s\" />\n", element->attributeName(QTest::AI_Metric), - quotedMetric, + quotedMetric.constData(), element->attributeName(QTest::AI_Tag), - quotedTag, + quotedTag.constData(), element->attributeName(QTest::AI_Value), element->attributeValue(QTest::AI_Value), element->attributeName(QTest::AI_Iterations), @@ -121,61 +115,60 @@ void QTestLightXmlStreamer::formatStart(const QTestElement *element, char *forma break; } default: - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } } -void QTestLightXmlStreamer::formatEnd(const QTestElement *element, char *formatted) const +void QTestLightXmlStreamer::formatEnd(const QTestElement *element, char **formatted) const { if(!element || !formatted) return; if (element->elementType() == QTest::LET_TestCase) { if( element->attribute(QTest::AI_Result) && element->childElements()) - QTest::qt_snprintf(formatted, 1024, "</Incident>\n</TestFunction>\n"); + QTest::qt_asprintf(formatted, "</Incident>\n</TestFunction>\n"); else - QTest::qt_snprintf(formatted, 1024, "</TestFunction>\n"); + QTest::qt_asprintf(formatted, "</TestFunction>\n"); } else - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } -void QTestLightXmlStreamer::formatBeforeAttributes(const QTestElement *element, char *formatted) const +void QTestLightXmlStreamer::formatBeforeAttributes(const QTestElement *element, char **formatted) const { if(!element || !formatted) return; if (element->elementType() == QTest::LET_TestCase && element->attribute(QTest::AI_Result)){ - char buf[900]; - char quotedFile[700]; - QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File), - sizeof(quotedFile)); + QTestCharBuffer buf; + QTestCharBuffer quotedFile; + QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File)); - QTest::qt_snprintf(buf, sizeof(buf), "%s=\"%s\" %s=\"%s\"", + QTest::qt_asprintf(buf, "%s=\"%s\" %s=\"%s\"", element->attributeName(QTest::AI_File), - quotedFile, + quotedFile.constData(), element->attributeName(QTest::AI_Line), element->attributeValue(QTest::AI_Line)); if( !element->childElements() ) - QTest::qt_snprintf(formatted, 1024, "<Incident type=\"%s\" %s/>\n", - element->attributeValue(QTest::AI_Result), buf); + QTest::qt_asprintf(formatted, "<Incident type=\"%s\" %s/>\n", + element->attributeValue(QTest::AI_Result), buf.constData()); else - QTest::qt_snprintf(formatted, 1024, "<Incident type=\"%s\" %s>\n", - element->attributeValue(QTest::AI_Result), buf); + QTest::qt_asprintf(formatted, "<Incident type=\"%s\" %s>\n", + element->attributeValue(QTest::AI_Result), buf.constData()); }else{ - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } } void QTestLightXmlStreamer::output(QTestElement *element) const { - char buf[1024]; - QTest::qt_snprintf(buf, sizeof(buf), "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n", + QTestCharBuffer buf; + QTest::qt_asprintf(buf, "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n", qVersion(), QTEST_VERSION_STR ); outputString(buf); - QTest::qt_snprintf(buf, sizeof(buf), "</Environment>\n"); + QTest::qt_asprintf(buf, "</Environment>\n"); outputString(buf); QTestBasicStreamer::output(element); diff --git a/src/testlib/qtestlightxmlstreamer.h b/src/testlib/qtestlightxmlstreamer.h index 5a16327..6dafdcc 100644 --- a/src/testlib/qtestlightxmlstreamer.h +++ b/src/testlib/qtestlightxmlstreamer.h @@ -59,9 +59,9 @@ class QTestLightXmlStreamer: public QTestBasicStreamer QTestLightXmlStreamer(); ~QTestLightXmlStreamer(); - void formatStart(const QTestElement *element = 0, char *formatted = 0) const; - void formatEnd(const QTestElement *element = 0, char *formatted = 0) const; - void formatBeforeAttributes(const QTestElement *element = 0, char *formatted = 0) const; + void formatStart(const QTestElement *element, char **formatted) const; + void formatEnd(const QTestElement *element, char **formatted) const; + void formatBeforeAttributes(const QTestElement *element, char **formatted) const; void output(QTestElement *element) const; }; diff --git a/src/testlib/qtestxmlstreamer.cpp b/src/testlib/qtestxmlstreamer.cpp index 5172bcd..1b6e674 100644 --- a/src/testlib/qtestxmlstreamer.cpp +++ b/src/testlib/qtestxmlstreamer.cpp @@ -60,86 +60,78 @@ QTestXmlStreamer::QTestXmlStreamer() QTestXmlStreamer::~QTestXmlStreamer() {} -void QTestXmlStreamer::formatStart(const QTestElement *element, char *formatted) const +void QTestXmlStreamer::formatStart(const QTestElement *element, char **formatted) const { if(!element || !formatted) return; switch(element->elementType()){ case QTest::LET_TestCase: { - char quotedTf[950]; - QXmlTestLogger::xmlQuote(quotedTf, element->attributeValue(QTest::AI_Name), - sizeof(quotedTf)); + QTestCharBuffer quotedTf; + QXmlTestLogger::xmlQuote(quotedTf, element->attributeValue(QTest::AI_Name)); - QTest::qt_snprintf(formatted, 1024, "<TestFunction name=\"%s\">\n", quotedTf); + QTest::qt_asprintf(formatted, "<TestFunction name=\"%s\">\n", quotedTf.constData()); break; } case QTest::LET_Failure: { - char cdataDesc[800]; - QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description), - sizeof(cdataDesc)); + QTestCharBuffer cdataDesc; + QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description)); - char location[100]; - char quotedFile[70]; - QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File), - sizeof(quotedFile)); + QTestCharBuffer location; + QTestCharBuffer quotedFile; + QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File)); - QTest::qt_snprintf(location, sizeof(location), "%s=\"%s\" %s=\"%s\"", + QTest::qt_asprintf(location, "%s=\"%s\" %s=\"%s\"", element->attributeName(QTest::AI_File), - quotedFile, + quotedFile.constData(), element->attributeName(QTest::AI_Line), element->attributeValue(QTest::AI_Line)); if (element->attribute(QTest::AI_Tag)) { - char cdataTag[100]; - QXmlTestLogger::xmlCdata(cdataTag, element->attributeValue(QTest::AI_Tag), - sizeof(cdataTag)); - QTest::qt_snprintf(formatted, 1024, "<Incident type=\"%s\" %s>\n" + QTestCharBuffer cdataTag; + QXmlTestLogger::xmlCdata(cdataTag, element->attributeValue(QTest::AI_Tag)); + QTest::qt_asprintf(formatted, "<Incident type=\"%s\" %s>\n" " <DataTag><![CDATA[%s]]></DataTag>\n" " <Description><![CDATA[%s]]></Description>\n" "</Incident>\n", element->attributeValue(QTest::AI_Result), - location, cdataTag, cdataDesc); + location.constData(), cdataTag.constData(), cdataDesc.constData()); } else { - QTest::qt_snprintf(formatted, 1024, "<Incident type=\"%s\" %s>\n" + QTest::qt_asprintf(formatted, "<Incident type=\"%s\" %s>\n" " <Description><![CDATA[%s]]></Description>\n" "</Incident>\n", element->attributeValue(QTest::AI_Result), - location, cdataDesc); + location.constData(), cdataDesc.constData()); } break; } case QTest::LET_Error: { // assuming type and attribute names don't need quoting - char quotedFile[128]; - char cdataDesc[700]; - QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File), - sizeof(quotedFile)); - QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description), - sizeof(cdataDesc)); - - QTest::qt_snprintf(formatted, 1024, "<Message type=\"%s\" %s=\"%s\" %s=\"%s\">\n <Description><![CDATA[%s]]></Description>\n</Message>\n", + QTestCharBuffer quotedFile; + QTestCharBuffer cdataDesc; + QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File)); + QXmlTestLogger::xmlCdata(cdataDesc, element->attributeValue(QTest::AI_Description)); + + QTest::qt_asprintf(formatted, "<Message type=\"%s\" %s=\"%s\" %s=\"%s\">\n <Description><![CDATA[%s]]></Description>\n</Message>\n", element->attributeValue(QTest::AI_Type), element->attributeName(QTest::AI_File), - quotedFile, + quotedFile.constData(), element->attributeName(QTest::AI_Line), element->attributeValue(QTest::AI_Line), - cdataDesc); + cdataDesc.constData()); break; } case QTest::LET_Benchmark: { // assuming value and iterations don't need quoting - char quotedMetric[256]; - char quotedTag[256]; - QXmlTestLogger::xmlQuote(quotedMetric, element->attributeValue(QTest::AI_Metric), - sizeof(quotedMetric)); - QXmlTestLogger::xmlQuote(quotedTag, element->attributeValue(QTest::AI_Tag), - sizeof(quotedTag)); - - QTest::qt_snprintf(formatted, 1024, "<BenchmarkResult %s=\"%s\" %s=\"%s\" %s=\"%s\" %s=\"%s\" />\n", + QTestCharBuffer quotedMetric; + QTestCharBuffer quotedTag; + QXmlTestLogger::xmlQuote(quotedMetric, element->attributeValue(QTest::AI_Metric)); + QXmlTestLogger::xmlQuote(quotedTag, element->attributeValue(QTest::AI_Tag)); + + QTest::qt_asprintf(formatted, "<BenchmarkResult %s=\"%s\" %s=\"%s\" %s=\"%s\" %s=\"%s\" />\n", element->attributeName(QTest::AI_Metric), - quotedMetric, + quotedMetric.constData(), element->attributeName(QTest::AI_Tag), - quotedTag, + quotedTag.constData(), element->attributeName(QTest::AI_Value), element->attributeValue(QTest::AI_Value), element->attributeName(QTest::AI_Iterations), @@ -147,71 +139,70 @@ void QTestXmlStreamer::formatStart(const QTestElement *element, char *formatted) break; } default: - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } } -void QTestXmlStreamer::formatEnd(const QTestElement *element, char *formatted) const +void QTestXmlStreamer::formatEnd(const QTestElement *element, char **formatted) const { if(!element || !formatted) return; if (element->elementType() == QTest::LET_TestCase) { - QTest::qt_snprintf(formatted, 1024, "</TestFunction>\n"); + QTest::qt_asprintf(formatted, "</TestFunction>\n"); } else - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } -void QTestXmlStreamer::formatBeforeAttributes(const QTestElement *element, char *formatted) const +void QTestXmlStreamer::formatBeforeAttributes(const QTestElement *element, char **formatted) const { if(!element || !formatted) return; if (element->elementType() == QTest::LET_TestCase && element->attribute(QTest::AI_Result)){ - char buf[900]; - char quotedFile[700]; - QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File), - sizeof(quotedFile)); + QTestCharBuffer buf; + QTestCharBuffer quotedFile; + QXmlTestLogger::xmlQuote(quotedFile, element->attributeValue(QTest::AI_File)); - QTest::qt_snprintf(buf, sizeof(buf), "%s=\"%s\" %s=\"%s\"", + QTest::qt_asprintf(buf, "%s=\"%s\" %s=\"%s\"", element->attributeName(QTest::AI_File), - quotedFile, + quotedFile.constData(), element->attributeName(QTest::AI_Line), element->attributeValue(QTest::AI_Line)); if( !element->childElements() ) { - QTest::qt_snprintf(formatted, 1024, "<Incident type=\"%s\" %s/>\n", - element->attributeValue(QTest::AI_Result), buf); + QTest::qt_asprintf(formatted, "<Incident type=\"%s\" %s/>\n", + element->attributeValue(QTest::AI_Result), buf.constData()); } else { - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } }else{ - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } } void QTestXmlStreamer::output(QTestElement *element) const { - char buf[1024]; - char quotedTc[800]; - QXmlTestLogger::xmlQuote(quotedTc, QTestResult::currentTestObjectName(), sizeof(quotedTc)); + QTestCharBuffer buf; + QTestCharBuffer quotedTc; + QXmlTestLogger::xmlQuote(quotedTc, QTestResult::currentTestObjectName()); - QTest::qt_snprintf(buf, sizeof(buf), "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<TestCase name=\"%s\">\n", - quotedTc); + QTest::qt_asprintf(buf, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<TestCase name=\"%s\">\n", + quotedTc.constData()); outputString(buf); - QTest::qt_snprintf(buf, sizeof(buf), "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n", + QTest::qt_asprintf(buf, "<Environment>\n <QtVersion>%s</QtVersion>\n <QTestVersion>%s</QTestVersion>\n", qVersion(), QTEST_VERSION_STR ); outputString(buf); - QTest::qt_snprintf(buf, sizeof(buf), "</Environment>\n"); + QTest::qt_asprintf(buf, "</Environment>\n"); outputString(buf); QTestBasicStreamer::output(element); - QTest::qt_snprintf(buf, sizeof(buf), "</TestCase>\n"); + QTest::qt_asprintf(buf, "</TestCase>\n"); outputString(buf); } diff --git a/src/testlib/qtestxmlstreamer.h b/src/testlib/qtestxmlstreamer.h index 3cb1c60..a601f60 100644 --- a/src/testlib/qtestxmlstreamer.h +++ b/src/testlib/qtestxmlstreamer.h @@ -59,9 +59,9 @@ class QTestXmlStreamer: public QTestBasicStreamer QTestXmlStreamer(); ~QTestXmlStreamer(); - void formatStart(const QTestElement *element = 0, char *formatted = 0) const; - void formatEnd(const QTestElement *element = 0, char *formatted = 0) const; - void formatBeforeAttributes(const QTestElement *element = 0, char *formatted = 0) const; + void formatStart(const QTestElement *element, char **formatted) const; + void formatEnd(const QTestElement *element, char **formatted) const; + void formatBeforeAttributes(const QTestElement *element, char **formatted) const; void output(QTestElement *element) const; }; diff --git a/src/testlib/qtestxunitstreamer.cpp b/src/testlib/qtestxunitstreamer.cpp index 6690cec..d5d2631 100644 --- a/src/testlib/qtestxunitstreamer.cpp +++ b/src/testlib/qtestxunitstreamer.cpp @@ -73,7 +73,7 @@ void QTestXunitStreamer::indentForElement(const QTestElement* element, char* buf } } -void QTestXunitStreamer::formatStart(const QTestElement *element, char *formatted) const +void QTestXunitStreamer::formatStart(const QTestElement *element, char **formatted) const { if(!element || !formatted ) return; @@ -84,34 +84,34 @@ void QTestXunitStreamer::formatStart(const QTestElement *element, char *formatte // Errors are written as CDATA within system-err, comments elsewhere if (element->elementType() == QTest::LET_Error) { if (element->parentElement()->elementType() == QTest::LET_SystemError) { - QTest::qt_snprintf(formatted, 1024, "<![CDATA["); + QTest::qt_asprintf(formatted, "<![CDATA["); } else { - QTest::qt_snprintf(formatted, 1024, "%s<!--", indent); + QTest::qt_asprintf(formatted, "%s<!--", indent); } return; } - QTest::qt_snprintf(formatted, 1024, "%s<%s", indent, element->elementName()); + QTest::qt_asprintf(formatted, "%s<%s", indent, element->elementName()); } -void QTestXunitStreamer::formatEnd(const QTestElement *element, char *formatted) const +void QTestXunitStreamer::formatEnd(const QTestElement *element, char **formatted) const { if(!element || !formatted ) return; if(!element->childElements()){ - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); return; } char indent[20]; indentForElement(element, indent, sizeof(indent)); - QTest::qt_snprintf(formatted, 1024, "%s</%s>\n", indent, element->elementName()); + QTest::qt_asprintf(formatted, "%s</%s>\n", indent, element->elementName()); } -void QTestXunitStreamer::formatAttributes(const QTestElement* element, const QTestElementAttribute *attribute, char *formatted) const +void QTestXunitStreamer::formatAttributes(const QTestElement* element, const QTestElementAttribute *attribute, char **formatted) const { if(!attribute || !formatted ) return; @@ -124,7 +124,7 @@ void QTestXunitStreamer::formatAttributes(const QTestElement* element, const QTe if (attrindex != QTest::AI_Description) return; - QXmlTestLogger::xmlCdata(formatted, attribute->value(), 1024); + QXmlTestLogger::xmlCdata(formatted, attribute->value()); return; } @@ -135,16 +135,16 @@ void QTestXunitStreamer::formatAttributes(const QTestElement* element, const QTe key = attribute->name(); if (key) { - char quotedValue[900]; - QXmlTestLogger::xmlQuote(quotedValue, attribute->value(), sizeof(quotedValue)); - QTest::qt_snprintf(formatted, 1024, " %s=\"%s\"", key, quotedValue); + QTestCharBuffer quotedValue; + QXmlTestLogger::xmlQuote(quotedValue, attribute->value()); + QTest::qt_asprintf(formatted, " %s=\"%s\"", key, quotedValue.constData()); } else { - QTest::qt_snprintf(formatted, 10, ""); + QTest::qt_asprintf(formatted, ""); } } -void QTestXunitStreamer::formatAfterAttributes(const QTestElement *element, char *formatted) const +void QTestXunitStreamer::formatAfterAttributes(const QTestElement *element, char **formatted) const { if(!element || !formatted ) return; @@ -152,31 +152,29 @@ void QTestXunitStreamer::formatAfterAttributes(const QTestElement *element, char // Errors are written as CDATA within system-err, comments elsewhere if (element->elementType() == QTest::LET_Error) { if (element->parentElement()->elementType() == QTest::LET_SystemError) { - QTest::qt_snprintf(formatted, 1024, "]]>\n"); + QTest::qt_asprintf(formatted, "]]>\n"); } else { - QTest::qt_snprintf(formatted, 1024, " -->\n"); + QTest::qt_asprintf(formatted, " -->\n"); } return; } if(!element->childElements()) - QTest::qt_snprintf(formatted, 10, "/>\n"); + QTest::qt_asprintf(formatted, "/>\n"); else - QTest::qt_snprintf(formatted, 10, ">\n"); + QTest::qt_asprintf(formatted, ">\n"); } void QTestXunitStreamer::output(QTestElement *element) const { - char buf[1024]; - QTest::qt_snprintf(buf, sizeof(buf), "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); - outputString(buf); + outputString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); QTestBasicStreamer::output(element); } void QTestXunitStreamer::outputElements(QTestElement *element, bool) const { - char buf[1024]; + QTestCharBuffer buf; bool hasChildren; /* Elements are in reverse order of occurrence, so start from the end and work diff --git a/src/testlib/qtestxunitstreamer.h b/src/testlib/qtestxunitstreamer.h index 9817fd3..044307f 100644 --- a/src/testlib/qtestxunitstreamer.h +++ b/src/testlib/qtestxunitstreamer.h @@ -58,10 +58,10 @@ class QTestXunitStreamer: public QTestBasicStreamer QTestXunitStreamer(); ~QTestXunitStreamer(); - void formatStart(const QTestElement *element = 0, char *formatted = 0) const; - void formatEnd(const QTestElement *element = 0, char *formatted = 0) const; - void formatAfterAttributes(const QTestElement *element = 0, char *formatted = 0) const; - void formatAttributes(const QTestElement *element = 0, const QTestElementAttribute *attribute = 0, char *formatted = 0) const; + void formatStart(const QTestElement *element, char **formatted) const; + void formatEnd(const QTestElement *element, char **formatted) const; + void formatAfterAttributes(const QTestElement *element, char **formatted) const; + void formatAttributes(const QTestElement *element, const QTestElementAttribute *attribute, char **formatted) const; void output(QTestElement *element) const; void outputElements(QTestElement *element, bool isChildElement = false) const; diff --git a/src/testlib/qxmltestlogger.cpp b/src/testlib/qxmltestlogger.cpp index 85eb0dd..fca7bfc 100644 --- a/src/testlib/qxmltestlogger.cpp +++ b/src/testlib/qxmltestlogger.cpp @@ -104,18 +104,18 @@ QXmlTestLogger::~QXmlTestLogger() void QXmlTestLogger::startLogging() { QAbstractTestLogger::startLogging(); - char buf[1024]; + QTestCharBuffer buf; if (xmlmode == QXmlTestLogger::Complete) { - char quotedTc[900]; - xmlQuote(quotedTc, QTestResult::currentTestObjectName(), sizeof(quotedTc)); - QTest::qt_snprintf(buf, sizeof(buf), + QTestCharBuffer quotedTc; + xmlQuote(quotedTc, QTestResult::currentTestObjectName()); + QTest::qt_asprintf(buf, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n" - "<TestCase name=\"%s\">\n", quotedTc); + "<TestCase name=\"%s\">\n", quotedTc.constData()); outputString(buf); } - QTest::qt_snprintf(buf, sizeof(buf), + QTest::qt_asprintf(buf, "<Environment>\n" " <QtVersion>%s</QtVersion>\n" " <QTestVersion>"QTEST_VERSION_STR"</QTestVersion>\n" @@ -134,10 +134,10 @@ void QXmlTestLogger::stopLogging() void QXmlTestLogger::enterTestFunction(const char *function) { - char buf[1024]; - char quotedFunction[950]; - xmlQuote(quotedFunction, function, sizeof(quotedFunction)); - QTest::qt_snprintf(buf, sizeof(buf), "<TestFunction name=\"%s\">\n", quotedFunction); + QTestCharBuffer buf; + QTestCharBuffer quotedFunction; + xmlQuote(quotedFunction, function); + QTest::qt_asprintf(buf, "<TestFunction name=\"%s\">\n", quotedFunction.constData()); outputString(buf); } @@ -208,51 +208,49 @@ static const char *messageFormatString(bool noDescription, bool noTag) void QXmlTestLogger::addIncident(IncidentTypes type, const char *description, const char *file, int line) { - // buffer must be large enough to hold all quoted/cdata buffers plus the format string itself - char buf[5000]; + QTestCharBuffer buf; const char *tag = QTestResult::currentDataTag(); const char *gtag = QTestResult::currentGlobalDataTag(); const char *filler = (tag && gtag) ? ":" : ""; const bool notag = QTest::isEmpty(tag) && QTest::isEmpty(gtag); - char quotedFile[1024]; - char cdataGtag[1024]; - char cdataTag[1024]; - char cdataDescription[1024]; + QTestCharBuffer quotedFile; + QTestCharBuffer cdataGtag; + QTestCharBuffer cdataTag; + QTestCharBuffer cdataDescription; - xmlQuote(quotedFile, file, sizeof(quotedFile)); - xmlCdata(cdataGtag, gtag, sizeof(cdataGtag)); - xmlCdata(cdataTag, tag, sizeof(cdataTag)); - xmlCdata(cdataDescription, description, sizeof(cdataDescription)); + xmlQuote(quotedFile, file); + xmlCdata(cdataGtag, gtag); + xmlCdata(cdataTag, tag); + xmlCdata(cdataDescription, description); - QTest::qt_snprintf(buf, sizeof(buf), + QTest::qt_asprintf(buf, QTest::incidentFormatString(QTest::isEmpty(description), notag), QTest::xmlIncidentType2String(type), - quotedFile, line, - cdataGtag, + quotedFile.constData(), line, + cdataGtag.constData(), filler, - cdataTag, - cdataDescription); + cdataTag.constData(), + cdataDescription.constData()); outputString(buf); } void QXmlTestLogger::addBenchmarkResult(const QBenchmarkResult &result) { - char buf[1536]; - char quotedMetric[64]; - char quotedTag[1024]; + QTestCharBuffer buf; + QTestCharBuffer quotedMetric; + QTestCharBuffer quotedTag; xmlQuote(quotedMetric, - QBenchmarkGlobalData::current->measurer->metricText().toAscii().constData(), - sizeof(quotedMetric)); - xmlQuote(quotedTag, result.context.tag.toAscii().constData(), sizeof(quotedTag)); + QBenchmarkGlobalData::current->measurer->metricText().toAscii().constData()); + xmlQuote(quotedTag, result.context.tag.toAscii().constData()); - QTest::qt_snprintf( - buf, sizeof(buf), + QTest::qt_asprintf( + buf, QTest::benchmarkResultFormatString(), - quotedMetric, - quotedTag, + quotedMetric.constData(), + quotedTag.constData(), QByteArray::number(result.value).constData(), //no 64-bit qt_snprintf support result.iterations); outputString(buf); @@ -261,30 +259,30 @@ void QXmlTestLogger::addBenchmarkResult(const QBenchmarkResult &result) void QXmlTestLogger::addMessage(MessageTypes type, const char *message, const char *file, int line) { - char buf[5000]; + QTestCharBuffer buf; const char *tag = QTestResult::currentDataTag(); const char *gtag = QTestResult::currentGlobalDataTag(); const char *filler = (tag && gtag) ? ":" : ""; const bool notag = QTest::isEmpty(tag) && QTest::isEmpty(gtag); - char quotedFile[1024]; - char cdataGtag[1024]; - char cdataTag[1024]; - char cdataDescription[1024]; + QTestCharBuffer quotedFile; + QTestCharBuffer cdataGtag; + QTestCharBuffer cdataTag; + QTestCharBuffer cdataDescription; - xmlQuote(quotedFile, file, sizeof(quotedFile)); - xmlCdata(cdataGtag, gtag, sizeof(cdataGtag)); - xmlCdata(cdataTag, tag, sizeof(cdataTag)); - xmlCdata(cdataDescription, message, sizeof(cdataDescription)); + xmlQuote(quotedFile, file); + xmlCdata(cdataGtag, gtag); + xmlCdata(cdataTag, tag); + xmlCdata(cdataDescription, message); - QTest::qt_snprintf(buf, sizeof(buf), + QTest::qt_asprintf(buf, QTest::messageFormatString(QTest::isEmpty(message), notag), QTest::xmlMessageType2String(type), - quotedFile, line, - cdataGtag, + quotedFile.constData(), line, + cdataGtag.constData(), filler, - cdataTag, - cdataDescription); + cdataTag.constData(), + cdataDescription.constData()); outputString(buf); } @@ -294,29 +292,30 @@ void QXmlTestLogger::addMessage(MessageTypes type, const char *message, XML characters as necessary so that dest is suitable for use in an XML quoted attribute string. */ -void QXmlTestLogger::xmlQuote(char* dest, char const* src, size_t n) +int QXmlTestLogger::xmlQuote(char* dest, char const* src, size_t n) { - if (n == 0) return; + if (n == 0) return 0; *dest = 0; - if (!src) return; + if (!src) return 0; + char* begin = dest; char* end = dest + n; while (dest < end) { switch (*src) { #define MAP_ENTITY(chr, ent) \ - case chr: \ - if (dest + sizeof(ent) < end) { \ - strcpy(dest, ent); \ - dest += sizeof(ent) - 1; \ - } \ - else { \ - *dest = 0; \ - return; \ - } \ - ++src; \ + case chr: \ + if (dest + sizeof(ent) < end) { \ + strcpy(dest, ent); \ + dest += sizeof(ent) - 1; \ + } \ + else { \ + *dest = 0; \ + return (dest+sizeof(ent)-begin); \ + } \ + ++src; \ break; MAP_ENTITY('>', ">"); @@ -333,7 +332,7 @@ void QXmlTestLogger::xmlQuote(char* dest, char const* src, size_t n) case 0: *dest = 0; - return; + return (dest-begin); default: *dest = *src; @@ -345,29 +344,31 @@ void QXmlTestLogger::xmlQuote(char* dest, char const* src, size_t n) // If we get here, dest was completely filled (dest == end) *(dest-1) = 0; + return (dest-begin); } /* Copy up to n characters from the src string into dest, escaping any special strings such that dest is suitable for use in an XML CDATA section. */ -void QXmlTestLogger::xmlCdata(char* dest, char const* src, size_t n) +int QXmlTestLogger::xmlCdata(char* dest, char const* src, size_t n) { - if (!n) return; + if (!n) return 0; if (!src || n == 1) { *dest = 0; - return; + return 0; } - char const CDATA_END[] = "]]>"; - char const CDATA_END_ESCAPED[] = "]]]><![CDATA[]>"; + static char const CDATA_END[] = "]]>"; + static char const CDATA_END_ESCAPED[] = "]]]><![CDATA[]>"; + char* begin = dest; char* end = dest + n; while (dest < end) { if (!*src) { *dest = 0; - return; + return (dest-begin); } if (!strncmp(src, CDATA_END, sizeof(CDATA_END)-1)) { @@ -378,7 +379,7 @@ void QXmlTestLogger::xmlCdata(char* dest, char const* src, size_t n) } else { *dest = 0; - return; + return (dest+sizeof(CDATA_END_ESCAPED)-begin); } continue; } @@ -390,6 +391,52 @@ void QXmlTestLogger::xmlCdata(char* dest, char const* src, size_t n) // If we get here, dest was completely filled (dest == end) *(dest-1) = 0; + return (dest-begin); +} + +typedef int (*StringFormatFunction)(char*,char const*,size_t); + +/* + A wrapper for string functions written to work with a fixed size buffer so they can be called + with a dynamically allocated buffer. +*/ +int allocateStringFn(char** str, char const* src, StringFormatFunction func) +{ + static const int MAXSIZE = 1024*1024*2; + + int size = 32; + delete[] *str; + *str = new char[size]; + + int res = 0; + + for (;;) { + res = func(*str, src, size); + (*str)[size - 1] = '\0'; + if (res < size) { + // We succeeded or fatally failed + break; + } + // buffer wasn't big enough, try again + size *= 2; + if (size > MAXSIZE) { + break; + } + delete[] *str; + *str = new char[size]; + } + + return res; +} + +int QXmlTestLogger::xmlQuote(char** str, char const* src) +{ + return allocateStringFn(str, src, QXmlTestLogger::xmlQuote); +} + +int QXmlTestLogger::xmlCdata(char** str, char const* src) +{ + return allocateStringFn(str, src, QXmlTestLogger::xmlCdata); } QT_END_NAMESPACE diff --git a/src/testlib/qxmltestlogger_p.h b/src/testlib/qxmltestlogger_p.h index 526b471..a7cc00a 100644 --- a/src/testlib/qxmltestlogger_p.h +++ b/src/testlib/qxmltestlogger_p.h @@ -79,8 +79,10 @@ public: void addMessage(MessageTypes type, const char *message, const char *file = 0, int line = 0); - static void xmlCdata(char* dest, char const* src, size_t n); - static void xmlQuote(char* dest, char const* src, size_t n); + static int xmlCdata(char** dest, char const* src); + static int xmlQuote(char** dest, char const* src); + static int xmlCdata(char* dest, char const* src, size_t n); + static int xmlQuote(char* dest, char const* src, size_t n); private: XmlMode xmlmode; diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index b872dfd..e3ce2ec 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -56,6 +56,8 @@ enum PropertyFlags { EnumOrFlag = 0x00000008, StdCppSet = 0x00000100, // Override = 0x00000200, + Constant = 0x00000400, + Final = 0x00000800, Designable = 0x00001000, ResolveDesignable = 0x00002000, Scriptable = 0x00004000, @@ -68,6 +70,7 @@ enum PropertyFlags { ResolveUser = 0x00200000, Notify = 0x00400000 }; + enum MethodFlags { AccessPrivate = 0x00, AccessProtected = 0x01, @@ -202,10 +205,10 @@ void Generator::generateCode() QByteArray qualifiedClassNameIdentifier = cdef->qualified; qualifiedClassNameIdentifier.replace(':', '_'); - int index = 12; + int index = 13; fprintf(out, "static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData()); fprintf(out, "\n // content:\n"); - fprintf(out, " %4d, // revision\n", 2); + fprintf(out, " %4d, // revision\n", 3); fprintf(out, " %4d, // classname\n", strreg(cdef->qualified)); fprintf(out, " %4d, %4d, // classinfo\n", cdef->classInfoList.count(), cdef->classInfoList.count() ? index : 0); index += cdef->classInfoList.count() * 2; @@ -225,6 +228,9 @@ void Generator::generateCode() fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? cdef->constructorList.count() : 0, isConstructible ? index : 0); + fprintf(out, " %4d, // flags\n", 0); + + // // Build classinfo array // @@ -379,7 +385,7 @@ void Generator::generateCode() if (isQt || !cdef->hasQObject) return; - fprintf(out, "\nconst QMetaObject *%s::metaObject() const\n{\n return &staticMetaObject;\n}\n", + fprintf(out, "\nconst QMetaObject *%s::metaObject() const\n{\n return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;\n}\n", cdef->qualified.constData()); // // Generate smart cast function @@ -597,6 +603,11 @@ void Generator::generateProperties() if (p.notifyId != -1) flags |= Notify; + if (p.constant) + flags |= Constant; + if (p.final) + flags |= Final; + fprintf(out, " %4d, %4d, ", strreg(p.name), strreg(p.type)); diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 797595f..74b1a67 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -429,7 +429,7 @@ bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def) { def->isVirtual = false; //skip modifiers and attributes - while (test(INLINE) || test(STATIC) || + while (test(EXPLICIT) || test(INLINE) || test(STATIC) || (test(VIRTUAL) && (def->isVirtual = true)) //mark as virtual || testFunctionAttribute(def)) {} bool tilde = test(TILDE); @@ -908,6 +908,15 @@ void Moc::parseProperty(ClassDef *def) propDef.name = lexem(); while (test(IDENTIFIER)) { QByteArray l = lexem(); + + if (l[0] == 'C' && l == "CONSTANT") { + propDef.constant = true; + continue; + } else if(l[0] == 'F' && l == "FINAL") { + propDef.final = true; + continue; + } + QByteArray v, v2; if (test(LPAREN)) { v = lexemUntil(RPAREN); @@ -963,6 +972,23 @@ void Moc::parseProperty(ClassDef *def) msg += " has no READ accessor function. The property will be invalid."; warning(msg.constData()); } + if (propDef.constant && !propDef.write.isNull()) { + QByteArray msg; + msg += "Property declaration "; + msg += propDef.name; + msg += " is both WRITEable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } + if (propDef.constant && !propDef.notify.isNull()) { + QByteArray msg; + msg += "Property declaration "; + msg += propDef.name; + msg += " is both NOTIFYable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } + if(!propDef.notify.isEmpty()) def->notifyableProperties++; diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index 9fa9ac2..767f84e 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -115,9 +115,11 @@ struct FunctionDef struct PropertyDef { - PropertyDef():notifyId(-1), gspec(ValueSpec){} + PropertyDef():notifyId(-1), constant(false), final(false), gspec(ValueSpec){} QByteArray name, type, read, write, reset, designable, scriptable, editable, stored, user, notify; int notifyId; + bool constant; + bool final; enum Specification { ValueSpec, ReferenceSpec, PointerSpec }; Specification gspec; bool stdCppSet() const { diff --git a/src/xmlpatterns/acceltree/qacceltreeresourceloader.cpp b/src/xmlpatterns/acceltree/qacceltreeresourceloader.cpp index 1cd84db..8019551 100644 --- a/src/xmlpatterns/acceltree/qacceltreeresourceloader.cpp +++ b/src/xmlpatterns/acceltree/qacceltreeresourceloader.cpp @@ -125,7 +125,7 @@ QNetworkReply *AccelTreeResourceLoader::load(const QUrl &uri, networkLoop.connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(error(QNetworkReply::NetworkError))); networkLoop.connect(reply, SIGNAL(finished()), SLOT(finished())); - if(networkLoop.exec()) + if(networkLoop.exec(QEventLoop::ExcludeUserInputEvents)) { const QString errorMessage(escape(reply->errorString())); diff --git a/tests/arthur/data/1.2/textArea01.svg b/tests/arthur/data/1.2/textArea01.svg index 79fcd0a..945c34c 100644 --- a/tests/arthur/data/1.2/textArea01.svg +++ b/tests/arthur/data/1.2/textArea01.svg @@ -6,5 +6,5 @@ height="300">Tomorrow, and tomorrow, and tomorrow; creeps in this petty pace from day to day, until the last syll­able of recorded time. And all our yesterdays have lighted fools the way to dusty death.</textArea> - <rect x="5" y="5" width="210" height="310" stroke-width="3" stroke="#777"/> + <rect x="5" y="5" width="210" height="310" stroke-width="3" stroke="#777" fill="none"/> </svg> diff --git a/tests/arthur/data/qps/linear_gradients_perspectives_qps.png b/tests/arthur/data/qps/linear_gradients_perspectives_qps.png Binary files differindex 42ba872..3315ebe 100644 --- a/tests/arthur/data/qps/linear_gradients_perspectives_qps.png +++ b/tests/arthur/data/qps/linear_gradients_perspectives_qps.png diff --git a/tests/arthur/data/qps/linear_gradients_qps.png b/tests/arthur/data/qps/linear_gradients_qps.png Binary files differindex dc5223b..a4cdc43 100644 --- a/tests/arthur/data/qps/linear_gradients_qps.png +++ b/tests/arthur/data/qps/linear_gradients_qps.png diff --git a/tests/arthur/data/qps/lineconsistency_qps.png b/tests/arthur/data/qps/lineconsistency_qps.png Binary files differindex 48768ef..9d19a5d 100644 --- a/tests/arthur/data/qps/lineconsistency_qps.png +++ b/tests/arthur/data/qps/lineconsistency_qps.png diff --git a/tests/arthur/data/qps/linedashes2_aa_qps.png b/tests/arthur/data/qps/linedashes2_aa_qps.png Binary files differindex 0cb5b40..54c84b3 100644 --- a/tests/arthur/data/qps/linedashes2_aa_qps.png +++ b/tests/arthur/data/qps/linedashes2_aa_qps.png diff --git a/tests/arthur/data/qps/paths_aa_qps.png b/tests/arthur/data/qps/paths_aa_qps.png Binary files differindex 45b4b74..e51a4ac 100644 --- a/tests/arthur/data/qps/paths_aa_qps.png +++ b/tests/arthur/data/qps/paths_aa_qps.png diff --git a/tests/arthur/data/qps/paths_qps.png b/tests/arthur/data/qps/paths_qps.png Binary files differindex a9a8743..094a84f 100644 --- a/tests/arthur/data/qps/paths_qps.png +++ b/tests/arthur/data/qps/paths_qps.png diff --git a/tests/arthur/data/qps/pixmap_scaling.qps b/tests/arthur/data/qps/pixmap_scaling.qps index 81b8fae..c5053ad 100644 --- a/tests/arthur/data/qps/pixmap_scaling.qps +++ b/tests/arthur/data/qps/pixmap_scaling.qps @@ -3,8 +3,8 @@ translate 5 25 setFont "arial" 8 -save - drawText 15 0 "opaque image" +save + drawText 15 0 "opaque image" translate 50 50 save translate 1 1 @@ -16,23 +16,23 @@ save scale -20 20 drawImage solid2x2.png 0 0 restore - save + save translate 1 -1 scale 20 -20 drawImage solid2x2.png 0 0 restore - save + save translate -1 -1 scale -20 -20 drawImage solid2x2.png 0 0 restore - -restore + +restore save translate 150 0 - drawText 15 0 "alpha image" + drawText 15 0 "alpha image" translate 50 50 save translate 1 1 @@ -44,12 +44,12 @@ save scale -20 20 drawImage alpha2x2.png 0 0 restore - save + save translate 1 -1 scale 20 -20 drawImage alpha2x2.png 0 0 restore - save + save translate -1 -1 scale -20 -20 drawImage alpha2x2.png 0 0 @@ -59,7 +59,7 @@ restore save translate 0 150 - drawText 15 0 "solid pixmap" + drawText 15 0 "solid pixmap" translate 50 50 save translate 1 1 @@ -71,12 +71,12 @@ save scale -20 20 drawPixmap solid2x2.png 0 0 restore - save + save translate 1 -1 scale 20 -20 drawPixmap solid2x2.png 0 0 restore - save + save translate -1 -1 scale -20 -20 drawPixmap solid2x2.png 0 0 @@ -85,9 +85,9 @@ restore save - translate 150 150 + translate 150 150 drawText 15 0 "alpha pixmap" - translate 50 50 + translate 50 50 save translate 1 1 scale 20 20 @@ -98,12 +98,12 @@ save scale -20 20 drawPixmap alpha2x2.png 0 0 restore - save + save translate 1 -1 scale 20 -20 drawPixmap alpha2x2.png 0 0 restore - save + save translate -1 -1 scale -20 -20 drawPixmap alpha2x2.png 0 0 @@ -111,7 +111,7 @@ save restore -save +save translate 300 10 save drawText 0 -10 "subrect solid image" @@ -138,7 +138,7 @@ save drawImage solid2x2.png 45 0 5 50 1.8 0 0.2 2 restore - save + save translate 150 0 drawText 0 -10 "subrect solid image" drawImage alpha2x2.png 0 0 50 5 0 0.0 2 0.2 @@ -151,7 +151,7 @@ save drawImage alpha2x2.png 0 35 50 5 0 1.4 2 0.2 drawImage alpha2x2.png 0 40 50 5 0 1.6 2 0.2 drawImage alpha2x2.png 0 45 50 5 0 1.8 2 0.2 - translate 60 0 + translate 60 0 drawImage alpha2x2.png 0 0 5 50 0.0 0 0.2 2 drawImage alpha2x2.png 5 0 5 50 0.2 0 0.2 2 drawImage alpha2x2.png 10 0 5 50 0.4 0 0.2 2 @@ -217,3 +217,311 @@ save restore restore + + +
+image_load alpha2x2.png alpha2x2
+image_convertToFormat alpha2x2 alpha2x2_argb32 ARGB32
+image_convertToFormat alpha2x2 alpha2x2_argb32pre ARGB32_Premultiplied
+image_convertToFormat alpha2x2 alpha2x2_rgb888 Format_RGB888
+image_convertToFormat alpha2x2 alpha2x2_rgb32 RGB32
+image_convertToFormat alpha2x2 alpha2x2_rgb16 Format_RGB16
+
+save
+ translate 0 300
+ drawText 15 0 "drawImage - argb32"
+ translate 50 50
+ save
+ translate 0.5 0.5
+ scale 20 20
+ drawImage alpha2x2_argb32 0 0
+ restore
+ save
+ translate -0.5 0.5
+ scale -20 20
+ drawImage alpha2x2_argb32 0 0
+ restore
+ save
+ translate 0.5 -0.5
+ scale 20 -20
+ drawImage alpha2x2_argb32 0 0
+ restore
+ save
+ translate -0.5 -0.5
+ scale -20 -20
+ drawImage alpha2x2_argb32 0 0
+ restore
+restore
+
+save
+ translate 150 300
+ drawText 15 0 "drawImage - argb32premul"
+ translate 50 50
+ save
+ translate 0.5 0.5
+ scale 20 20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+ save
+ translate -0.5 0.5
+ scale -20 20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+ save
+ translate 0.5 -0.5
+ scale 20 -20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+ save
+ translate -0.5 -0.5
+ scale -20 -20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+restore
+
+
+
+save
+ translate 0 450
+ drawText 15 0 "drawImage - rgb888"
+ translate 50 50
+ save
+ translate 0.5 0.5
+ scale 20 20
+ drawImage alpha2x2_rgb888 0 0
+ restore
+ save
+ translate -0.5 0.5
+ scale -20 20
+ drawImage alpha2x2_rgb888 0 0
+ restore
+ save
+ translate 0.5 -0.5
+ scale 20 -20
+ drawImage alpha2x2_rgb888 0 0
+ restore
+ save
+ translate -0.5 -0.5
+ scale -20 -20
+ drawImage alpha2x2_rgb888 0 0
+ restore
+restore
+
+save
+ translate 150 450
+ drawText 15 0 "drawImage - rgb32"
+ translate 50 50
+ save
+ translate 0.5 0.5
+ scale 20 20
+ drawImage alpha2x2_rgb32 0 0
+ restore
+ save
+ translate -0.5 0.5
+ scale -20 20
+ drawImage alpha2x2_rgb32 0 0
+ restore
+ save
+ translate 0.5 -0.5
+ scale 20 -20
+ drawImage alpha2x2_rgb32 0 0
+ restore
+ save
+ translate -0.5 -0.5
+ scale -20 -20
+ drawImage alpha2x2_rgb32 0 0
+ restore
+restore
+
+save
+ translate 300 450
+ drawText 15 0 "drawImage - rgb16"
+ translate 50 50
+ save
+ translate 0.5 0.5
+ scale 20 20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+ save
+ translate -0.5 0.5
+ scale -20 20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+ save
+ translate 0.5 -0.5
+ scale 20 -20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+ save
+ translate -0.5 -0.5
+ scale -20 -20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+restore
+
+
+
+save
+ translate 0 600
+ drawText 15 0 "clipping - argb32"
+ translate 50 50
+ setClipRect -30 -30 60 60 ReplaceClip
+ save
+ translate 0.5 0.5
+ scale 20 20
+ drawImage alpha2x2_argb32 0 0
+ restore
+ save
+ translate -0.5 0.5
+ scale -20 20
+ drawImage alpha2x2_argb32 0 0
+ restore
+ save
+ translate 0.5 -0.5
+ scale 20 -20
+ drawImage alpha2x2_argb32 0 0
+ restore
+ save
+ translate -0.5 -0.5
+ scale -20 -20
+ drawImage alpha2x2_argb32 0 0
+ restore
+restore
+
+save
+ translate 150 600
+ drawText 15 0 "clipping - argb32premul"
+ translate 50 50
+ setClipRect -30 -30 60 60 ReplaceClip
+ save
+ translate 0.5 0.5
+ scale 20 20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+ save
+ translate -0.5 0.5
+ scale -20 20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+ save
+ translate 0.5 -0.5
+ scale 20 -20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+ save
+ translate -0.5 -0.5
+ scale -20 -20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+restore
+
+save
+ translate 300 600
+ drawText 15 0 "clipping - rgb16"
+ translate 50 50
+ setClipRect -30 -30 60 60 ReplaceClip
+ save
+ translate 0.5 0.5
+ scale 20 20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+ save
+ translate -0.5 0.5
+ scale -20 20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+ save
+ translate 0.5 -0.5
+ scale 20 -20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+ save
+ translate -0.5 -0.5
+ scale -20 -20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+restore
+
+
+path_addEllipse ellipse -30 -30 60 60
+
+save
+ translate 0 750
+ drawText 15 0 "clipping - argb32"
+ translate 50 50
+ setClipPath ellipse ReplaceClip
+ save
+ translate 0.5 0.5
+ scale 20 20
+ drawImage alpha2x2_argb32 0 0
+ restore
+ save
+ translate -0.5 0.5
+ scale -20 20
+ drawImage alpha2x2_argb32 0 0
+ restore
+ save
+ translate 0.5 -0.5
+ scale 20 -20
+ drawImage alpha2x2_argb32 0 0
+ restore
+ save
+ translate -0.5 -0.5
+ scale -20 -20
+ drawImage alpha2x2_argb32 0 0
+ restore
+restore
+
+save
+ translate 150 750
+ drawText 15 0 "clipping - argb32premul"
+ translate 50 50
+ setClipPath ellipse ReplaceClip
+ save
+ translate 0.5 0.5
+ scale 20 20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+ save
+ translate -0.5 0.5
+ scale -20 20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+ save
+ translate 0.5 -0.5
+ scale 20 -20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+ save
+ translate -0.5 -0.5
+ scale -20 -20
+ drawImage alpha2x2_argb32pre 0 0
+ restore
+restore
+
+save
+ translate 300 750
+ drawText 15 0 "clipping - rgb16"
+ translate 50 50
+ setClipPath ellipse ReplaceClip
+ save
+ translate 0.5 0.5
+ scale 20 20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+ save
+ translate -0.5 0.5
+ scale -20 20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+ save
+ translate 0.5 -0.5
+ scale 20 -20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+ save
+ translate -0.5 -0.5
+ scale -20 -20
+ drawImage alpha2x2_rgb16 0 0
+ restore
+restore
diff --git a/tests/arthur/data/qps/radial_gradients_perspectives_qps.png b/tests/arthur/data/qps/radial_gradients_perspectives_qps.png Binary files differindex 0e28aae..d833250 100644 --- a/tests/arthur/data/qps/radial_gradients_perspectives_qps.png +++ b/tests/arthur/data/qps/radial_gradients_perspectives_qps.png diff --git a/tests/arthur/data/qps/radial_gradients_qps.png b/tests/arthur/data/qps/radial_gradients_qps.png Binary files differindex 66fcfc4..11043e8 100644 --- a/tests/arthur/data/qps/radial_gradients_qps.png +++ b/tests/arthur/data/qps/radial_gradients_qps.png diff --git a/tests/arthur/data/qps/rasterops.qps b/tests/arthur/data/qps/rasterops.qps index b79ec0d..ee3deca 100644 --- a/tests/arthur/data/qps/rasterops.qps +++ b/tests/arthur/data/qps/rasterops.qps @@ -1,83 +1,84 @@ -setPen NoPen - -setBrush black -drawRect 10 10 1200 140 - -setCompositionMode SourceOrDestination -translate 20 20 -begin_block drawShape -setBrush 0xffff0000 -drawEllipse 10 10 80 80 -setBrush 0xff00ff00 -drawRect 0 0 50 50 -setBrush 0xff0000ff -drawRect 50 50 50 50 -end_block - -begin_block loop -setCompositionMode SourceAndDestination -translate 120 0 -repeat_block drawShape - -setCompositionMode SourceXorDestination -translate 120 0 -repeat_block drawShape - -setCompositionMode NotSourceAndNotDestination -translate 120 0 -repeat_block drawShape - -setCompositionMode NotSourceOrNotDestination -translate 120 0 -repeat_block drawShape - -setCompositionMode NotSourceXorDestination -translate 120 0 -repeat_block drawShape - -setCompositionMode NotSource -translate 120 0 -repeat_block drawShape - -setCompositionMode NotSourceAndDestination -translate 120 0 -repeat_block drawShape - -setCompositionMode SourceAndNotDestination -translate 120 0 -repeat_block drawShape -end_block - -resetMatrix -setCompositionMode Source -setBrush white -drawRect 10 160 1200 140 -translate 20 170 -repeat_block loop - -resetMatrix -setCompositionMode Source -translate 20 320 -repeat_block loop - -resetMatrix -setPen black -setCompositionMode SourceOver -translate 20 470 -drawText 20 0 "Or ROP" -translate 120 0 -drawText 20 0 "And ROP" -translate 120 0 -drawText 20 0 "Xor ROP" -translate 120 0 -drawText 20 0 "Nor ROP" -translate 120 0 -drawText 20 0 "Nand ROP" -translate 120 0 -drawText 0 0 "NSrcXorDst ROP" -translate 120 0 -drawText 20 0 "NSrc ROP" -translate 120 0 -drawText 0 0 "NSrcAndDst ROP" -translate 120 0 -drawText 0 0 "SrcAndNDst ROP" +setPen NoPen
+
+setBrush black
+drawRect 10 10 60 500
+
+setCompositionMode SourceOrDestination
+translate 20 20
+
+begin_block drawShape
+ setBrush 0xffff0000
+ drawEllipse 5 5 30 30
+ setBrush 0xff00ff00
+ drawRect 0 0 20 20
+ setBrush 0xff0000ff
+ drawRect 20 20 20 20
+end_block
+
+begin_block loop
+ setCompositionMode SourceAndDestination
+ translate 0 50
+repeat_block drawShape
+
+setCompositionMode SourceXorDestination
+translate 0 50
+repeat_block drawShape
+
+setCompositionMode NotSourceAndNotDestination
+translate 0 50
+repeat_block drawShape
+
+setCompositionMode NotSourceOrNotDestination
+translate 0 50
+repeat_block drawShape
+
+setCompositionMode NotSourceXorDestination
+translate 0 50
+repeat_block drawShape
+
+setCompositionMode NotSource
+translate 0 50
+repeat_block drawShape
+
+setCompositionMode NotSourceAndDestination
+translate 0 50
+repeat_block drawShape
+
+setCompositionMode SourceAndNotDestination
+translate 0 50
+repeat_block drawShape
+end_block
+
+resetMatrix
+setCompositionMode Source
+setBrush white
+drawRect 100 10 60 500
+translate 110 20
+repeat_block loop
+
+resetMatrix
+setCompositionMode Source
+translate 190 20
+repeat_block loop
+
+resetMatrix
+setPen black
+setCompositionMode SourceOver
+translate 250 45
+drawText 20 0 "Or ROP"
+translate 0 50
+drawText 20 0 "And ROP"
+translate 0 50
+drawText 20 0 "Xor ROP"
+translate 0 50
+drawText 20 0 "Nor ROP"
+translate 0 50
+drawText 20 0 "Nand ROP"
+translate 0 50
+drawText 0 0 "NSrcXorDst ROP"
+translate 0 50
+drawText 20 0 "NSrc ROP"
+translate 0 50
+drawText 0 0 "NSrcAndDst ROP"
+translate 0 50
+drawText 0 0 "SrcAndNDst ROP"
diff --git a/tests/arthur/data/qps/rasterops_qps.png b/tests/arthur/data/qps/rasterops_qps.png Binary files differindex 80ca8ae..7b93001 100644 --- a/tests/arthur/data/qps/rasterops_qps.png +++ b/tests/arthur/data/qps/rasterops_qps.png diff --git a/tests/arthur/data/qps/text_perspectives_qps.png b/tests/arthur/data/qps/text_perspectives_qps.png Binary files differindex 483ccc0..183ad6f 100644 --- a/tests/arthur/data/qps/text_perspectives_qps.png +++ b/tests/arthur/data/qps/text_perspectives_qps.png diff --git a/tests/arthur/data/qps/text_qps.png b/tests/arthur/data/qps/text_qps.png Binary files differindex 2c2ebf2..9a95493 100644 --- a/tests/arthur/data/qps/text_qps.png +++ b/tests/arthur/data/qps/text_qps.png diff --git a/tests/arthur/lance/interactivewidget.cpp b/tests/arthur/lance/interactivewidget.cpp index 0febbed..0c2c7a6 100644 --- a/tests/arthur/lance/interactivewidget.cpp +++ b/tests/arthur/lance/interactivewidget.cpp @@ -43,7 +43,7 @@ InteractiveWidget::InteractiveWidget() { - m_onScreenWidget = new OnScreenWidget<QWidget>(); + m_onScreenWidget = new OnScreenWidget<QWidget>(""); m_onScreenWidget->setMinimumSize(320, 240); setCentralWidget(m_onScreenWidget); diff --git a/tests/arthur/lance/lance.pro b/tests/arthur/lance/lance.pro index 193dbd2..ee9e08b 100644 --- a/tests/arthur/lance/lance.pro +++ b/tests/arthur/lance/lance.pro @@ -6,7 +6,7 @@ INCLUDEPATH += . # Input HEADERS += widgets.h interactivewidget.h -SOURCES += interactivewidget.cpp main.cpp +SOURCES += interactivewidget.cpp main.cpp RESOURCES += icons.qrc contains(QT_CONFIG, opengl)|contains(QT_CONFIG, opengles1)|contains(QT_CONFIG, opengles2):QT += opengl diff --git a/tests/arthur/lance/main.cpp b/tests/arthur/lance/main.cpp index 07e5180..f95b426 100644 --- a/tests/arthur/lance/main.cpp +++ b/tests/arthur/lance/main.cpp @@ -66,7 +66,7 @@ extern bool qt_show_painter_debug_output = false; //#define CONSOLE_APPLICATION -static const struct { +static const struct { const char *name; QImage::Format format; } imageFormats[] = { @@ -282,7 +282,7 @@ int main(int argc, char **argv) if (imageFormat == QImage::Format_Invalid) { printf("Invalid image format. Available formats are:\n"); - for (int ff = 0; ff < formatCount; ++ff) + for (int ff = 0; ff < formatCount; ++ff) printf("\t%s\n", imageFormats[ff].name); return -1; } @@ -403,12 +403,10 @@ int main(int argc, char **argv) case WidgetType: { OnScreenWidget<QWidget> *qWidget = - new OnScreenWidget<QWidget>; + new OnScreenWidget<QWidget>(files.at(j)); qWidget->setVerboseMode(verboseMode); qWidget->setType(type); qWidget->setCheckersBackground(checkers_background); - qWidget->m_filename = files.at(j); - qWidget->setWindowTitle(fileinfo.filePath()); qWidget->m_commands = content; qWidget->resize(width, height); qWidget->show(); @@ -418,12 +416,10 @@ int main(int argc, char **argv) case ImageWidgetType: { - OnScreenWidget<QWidget> *qWidget = new OnScreenWidget<QWidget>; + OnScreenWidget<QWidget> *qWidget = new OnScreenWidget<QWidget>(files.at(j)); qWidget->setVerboseMode(verboseMode); qWidget->setType(type); qWidget->setCheckersBackground(checkers_background); - qWidget->m_filename = files.at(j); - qWidget->setWindowTitle(fileinfo.filePath()); qWidget->m_commands = content; qWidget->resize(width, height); qWidget->show(); @@ -452,12 +448,10 @@ int main(int argc, char **argv) } case OpenGLType: { - OnScreenWidget<QGLWidget> *qGLWidget = new OnScreenWidget<QGLWidget>; + OnScreenWidget<QGLWidget> *qGLWidget = new OnScreenWidget<QGLWidget>(files.at(j)); qGLWidget->setVerboseMode(verboseMode); qGLWidget->setType(type); qGLWidget->setCheckersBackground(checkers_background); - qGLWidget->m_filename = files.at(j); - qGLWidget->setWindowTitle(fileinfo.filePath()); qGLWidget->m_commands = content; qGLWidget->resize(width, height); qGLWidget->show(); diff --git a/tests/arthur/lance/widgets.h b/tests/arthur/lance/widgets.h index 271c18a..7247608 100644 --- a/tests/arthur/lance/widgets.h +++ b/tests/arthur/lance/widgets.h @@ -57,15 +57,47 @@ #include <QTextStream> #include <QPaintEngine> +#include <private/qwindowsurface_p.h> + #include <qmath.h> const int CP_RADIUS = 10; +class StupidWorkaround : public QObject +{ + Q_OBJECT +public: + StupidWorkaround(QWidget *widget, int *value) + : QObject(widget), w(widget), mode(value) + { + } + +public slots: + void setViewMode(int m) { + *mode = m; + w->update(); + } + +private: + QWidget *w; + int *mode; +}; + template <class T> class OnScreenWidget : public T { public: - OnScreenWidget(QWidget *parent = 0) : T(parent) + + enum ViewMode { + RenderView, + BaselineView, + DifferenceView + }; + + OnScreenWidget(const QString &file, QWidget *parent = 0) + : T(parent), + m_view_mode(RenderView), + m_filename(file) { QSettings settings("Trolltech", "lance"); for (int i=0; i<10; ++i) { @@ -78,11 +110,47 @@ public: m_deviceType = WidgetType; m_checkersBackground = true; m_verboseMode = false; - } - void setVerboseMode(bool v) { m_verboseMode = v; } - void setCheckersBackground(bool b) { m_checkersBackground = b; } - void setType(DeviceType t) { m_deviceType = t; } + m_baseline_name = QString(m_filename).replace(".qps", "_qps") + ".png"; + if (QFileInfo(m_baseline_name).exists()) { + m_baseline = QPixmap(m_baseline_name); + } + + if (m_baseline.isNull()) { + setWindowTitle("Rendering: '" + file + "'. No baseline available"); + } else { + setWindowTitle("Rendering: '" + file + "'. Shortcuts: 1=render, 2=baseline, 3=difference"); + + StupidWorkaround *workaround = new StupidWorkaround(this, &m_view_mode); + + QSignalMapper *mapper = new QSignalMapper(this); + connect(mapper, SIGNAL(mapped(int)), workaround, SLOT(setViewMode(int))); + connect(mapper, SIGNAL(mapped(QString)), this, SLOT(setWindowTitle(QString))); + + QAction *renderViewAction = new QAction("Render View", this); + renderViewAction->setShortcut(Qt::Key_1); + connect(renderViewAction, SIGNAL(triggered()), mapper, SLOT(map())); + mapper->setMapping(renderViewAction, RenderView); + mapper->setMapping(renderViewAction, "Render View: " + file); + addAction(renderViewAction); + + QAction *baselineAction = new QAction("Baseline", this); + baselineAction->setShortcut(Qt::Key_2); + connect(baselineAction, SIGNAL(triggered()), mapper, SLOT(map())); + mapper->setMapping(baselineAction, BaselineView); + mapper->setMapping(baselineAction, "Baseline View: " + file); + addAction(baselineAction); + + QAction *differenceAction = new QAction("Differenfe View", this); + differenceAction->setShortcut(Qt::Key_3); + connect(differenceAction, SIGNAL(triggered()), mapper, SLOT(map())); + mapper->setMapping(differenceAction, DifferenceView); + mapper->setMapping(differenceAction, "Difference View" + file); + addAction(differenceAction); + + } + + } ~OnScreenWidget() { @@ -93,12 +161,24 @@ public: settings.sync(); } + void setVerboseMode(bool v) { m_verboseMode = v; } + void setCheckersBackground(bool b) { m_checkersBackground = b; } + void setType(DeviceType t) { m_deviceType = t; } + void resizeEvent(QResizeEvent *e) { m_image = QImage(); T::resizeEvent(e); } - void paintEvent(QPaintEvent *) + void paintEvent(QPaintEvent *) { + switch (m_view_mode) { + case RenderView: paintRenderView(); break; + case BaselineView: paintBaselineView(); break; + case DifferenceView: paintDifferenceView(); break; + } + } + + void OnScreenWidget<T>::paintRenderView() { QPainter pt; QPaintDevice *dev = this; @@ -152,6 +232,57 @@ public: } } + if (m_render_view.isNull()) { + m_render_view = window()->windowSurface()->grabWidget(this); + m_render_view.save("renderView.png"); + } + } + + void paintBaselineView() { + QPainter p(this); + + if (m_baseline.isNull()) { + p.drawText(rect(), Qt::AlignCenter, + "No baseline found\n" + "file '" + m_baseline_name + "' does not exist..."); + return; + } + + p.drawPixmap(0, 0, m_baseline); + + p.setPen(QColor::fromRgb(0, 0, 0, 0.1)); + p.setFont(QFont("Arial", 128)); + p.rotate(45); + p.drawText(100, 0, "BASELINE"); + } + + QPixmap generateDifference() + { + QImage img(size(), QImage::Format_RGB32); + img.fill(0); + + QPainter p(&img); + p.drawPixmap(0, 0, m_render_view); + + p.setCompositionMode(QPainter::RasterOp_SourceXorDestination); + p.drawPixmap(0, 0, m_baseline); + + p.end(); + + return QPixmap::fromImage(img); + } + + void paintDifferenceView() { + QPainter p(this); + if (m_baseline.isNull()) { + p.drawText(rect(), Qt::AlignCenter, + "No baseline found\n" + "file '" + m_baseline_name + "' does not exist..."); + return; + } + + p.fillRect(rect(), Qt::black); + p.drawPixmap(0, 0, generateDifference()); } @@ -190,8 +321,6 @@ public: T::update(); } - - QSize sizeHint() const { return QSize(800, 800); } QVector<QPointF> m_controlPoints; @@ -200,12 +329,18 @@ public: QStringList m_commands; QString m_filename; + QString m_baseline_name; bool m_verboseMode; bool m_checkersBackground; DeviceType m_deviceType; + int m_view_mode; + QImage m_image; + + QPixmap m_baseline; + QPixmap m_render_view; }; #endif diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index b4a6600..c923dee 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -270,7 +270,8 @@ SUBDIRS += _networkselftest \ qscriptenginedebugger \ qscrollarea \ qsemaphore \ - qsharedpointer \ + qsharedpointer \ + qsharedpointer_and_qwidget \ qsequentialanimationgroup \ qset \ qsettings \ @@ -284,6 +285,10 @@ SUBDIRS += _networkselftest \ qsocks5socketengine \ qsortfilterproxymodel \ qsound \ + qaudiodeviceid \ + qaudioformat \ + qaudiooutput \ + qaudioinput \ qspinbox \ qsplitter \ qsql \ diff --git a/tests/auto/math3d/qmatrixnxn/tst_qmatrixnxn.cpp b/tests/auto/math3d/qmatrixnxn/tst_qmatrixnxn.cpp index d799c1b..fe1d0db 100644 --- a/tests/auto/math3d/qmatrixnxn/tst_qmatrixnxn.cpp +++ b/tests/auto/math3d/qmatrixnxn/tst_qmatrixnxn.cpp @@ -170,6 +170,9 @@ private slots: void mapRect_data(); void mapRect(); + void properties(); + void metaTypes(); + private: static void setMatrix(QMatrix2x2& m, const qreal *values); static void setMatrixDirect(QMatrix2x2& m, const qreal *values); @@ -3331,6 +3334,50 @@ void tst_QMatrix::mapRect() QVERIFY(mri == tri); } +class tst_QMatrix4x4Properties : public QObject +{ + Q_OBJECT + Q_PROPERTY(QMatrix4x4 matrix READ matrix WRITE setMatrix) +public: + tst_QMatrix4x4Properties(QObject *parent = 0) : QObject(parent) {} + + QMatrix4x4 matrix() const { return m; } + void setMatrix(const QMatrix4x4& value) { m = value; } + +private: + QMatrix4x4 m; +}; + +// Test getting and setting matrix properties via the metaobject system. +void tst_QMatrix::properties() +{ + tst_QMatrix4x4Properties obj; + + QMatrix4x4 m1(uniqueValues4); + obj.setMatrix(m1); + + QMatrix4x4 m2 = qVariantValue<QMatrix4x4>(obj.property("matrix")); + QVERIFY(isSame(m2, uniqueValues4)); + + QMatrix4x4 m3(transposedValues4); + obj.setProperty("matrix", qVariantFromValue(m3)); + + m2 = qVariantValue<QMatrix4x4>(obj.property("matrix")); + QVERIFY(isSame(m2, transposedValues4)); +} + +void tst_QMatrix::metaTypes() +{ + QVERIFY(QMetaType::type("QMatrix4x4") == QMetaType::QMatrix4x4); + + QCOMPARE(QByteArray(QMetaType::typeName(QMetaType::QMatrix4x4)), + QByteArray("QMatrix4x4")); + + QVERIFY(QMetaType::isRegistered(QMetaType::QMatrix4x4)); + + QVERIFY(qMetaTypeId<QMatrix4x4>() == QMetaType::QMatrix4x4); +} + QTEST_APPLESS_MAIN(tst_QMatrix) #include "tst_qmatrixnxn.moc" diff --git a/tests/auto/math3d/qquaternion/tst_qquaternion.cpp b/tests/auto/math3d/qquaternion/tst_qquaternion.cpp index 16b87a1..369f5ac 100644 --- a/tests/auto/math3d/qquaternion/tst_qquaternion.cpp +++ b/tests/auto/math3d/qquaternion/tst_qquaternion.cpp @@ -93,6 +93,9 @@ private slots: void nlerp_data(); void nlerp(); + + void properties(); + void metaTypes(); }; // qFuzzyCompare isn't always "fuzzy" enough to handle conversion @@ -825,6 +828,55 @@ void tst_QQuaternion::nlerp() QVERIFY(fuzzyCompare(result.scalar(), q3.scalar())); } +class tst_QQuaternionProperties : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQuaternion quaternion READ quaternion WRITE setQuaternion) +public: + tst_QQuaternionProperties(QObject *parent = 0) : QObject(parent) {} + + QQuaternion quaternion() const { return q; } + void setQuaternion(const QQuaternion& value) { q = value; } + +private: + QQuaternion q; +}; + +// Test getting and setting quaternion properties via the metaobject system. +void tst_QQuaternion::properties() +{ + tst_QQuaternionProperties obj; + + obj.setQuaternion(QQuaternion(6.0f, 7.0f, 8.0f, 9.0f)); + + QQuaternion q = qVariantValue<QQuaternion>(obj.property("quaternion")); + QCOMPARE(q.scalar(), (qreal)6.0f); + QCOMPARE(q.x(), (qreal)7.0f); + QCOMPARE(q.y(), (qreal)8.0f); + QCOMPARE(q.z(), (qreal)9.0f); + + obj.setProperty("quaternion", + qVariantFromValue(QQuaternion(-6.0f, -7.0f, -8.0f, -9.0f))); + + q = qVariantValue<QQuaternion>(obj.property("quaternion")); + QCOMPARE(q.scalar(), (qreal)-6.0f); + QCOMPARE(q.x(), (qreal)-7.0f); + QCOMPARE(q.y(), (qreal)-8.0f); + QCOMPARE(q.z(), (qreal)-9.0f); +} + +void tst_QQuaternion::metaTypes() +{ + QVERIFY(QMetaType::type("QQuaternion") == QMetaType::QQuaternion); + + QCOMPARE(QByteArray(QMetaType::typeName(QMetaType::QQuaternion)), + QByteArray("QQuaternion")); + + QVERIFY(QMetaType::isRegistered(QMetaType::QQuaternion)); + + QVERIFY(qMetaTypeId<QQuaternion>() == QMetaType::QQuaternion); +} + QTEST_APPLESS_MAIN(tst_QQuaternion) #include "tst_qquaternion.moc" diff --git a/tests/auto/math3d/qvectornd/tst_qvectornd.cpp b/tests/auto/math3d/qvectornd/tst_qvectornd.cpp index 9c1ea83..6368874 100644 --- a/tests/auto/math3d/qvectornd/tst_qvectornd.cpp +++ b/tests/auto/math3d/qvectornd/tst_qvectornd.cpp @@ -137,6 +137,9 @@ private slots: void dotProduct3(); void dotProduct4_data(); void dotProduct4(); + + void properties(); + void metaTypes(); }; // qFuzzyCompare isn't always "fuzzy" enough to handle conversion @@ -2040,6 +2043,99 @@ void tst_QVector::dotProduct4() QCOMPARE(QVector4D::dotProduct(v1, v2), d); } +class tst_QVectorProperties : public QObject +{ + Q_OBJECT + Q_PROPERTY(QVector2D vector2D READ vector2D WRITE setVector2D) + Q_PROPERTY(QVector3D vector3D READ vector3D WRITE setVector3D) + Q_PROPERTY(QVector4D vector4D READ vector4D WRITE setVector4D) +public: + tst_QVectorProperties(QObject *parent = 0) : QObject(parent) {} + + QVector2D vector2D() const { return v2; } + void setVector2D(const QVector2D& value) { v2 = value; } + + QVector3D vector3D() const { return v3; } + void setVector3D(const QVector3D& value) { v3 = value; } + + QVector4D vector4D() const { return v4; } + void setVector4D(const QVector4D& value) { v4 = value; } + +private: + QVector2D v2; + QVector3D v3; + QVector4D v4; +}; + +// Test getting and setting vector properties via the metaobject system. +void tst_QVector::properties() +{ + tst_QVectorProperties obj; + + obj.setVector2D(QVector2D(1.0f, 2.0f)); + obj.setVector3D(QVector3D(3.0f, 4.0f, 5.0f)); + obj.setVector4D(QVector4D(6.0f, 7.0f, 8.0f, 9.0f)); + + QVector2D v2 = qVariantValue<QVector2D>(obj.property("vector2D")); + QCOMPARE(v2.x(), (qreal)1.0f); + QCOMPARE(v2.y(), (qreal)2.0f); + + QVector3D v3 = qVariantValue<QVector3D>(obj.property("vector3D")); + QCOMPARE(v3.x(), (qreal)3.0f); + QCOMPARE(v3.y(), (qreal)4.0f); + QCOMPARE(v3.z(), (qreal)5.0f); + + QVector4D v4 = qVariantValue<QVector4D>(obj.property("vector4D")); + QCOMPARE(v4.x(), (qreal)6.0f); + QCOMPARE(v4.y(), (qreal)7.0f); + QCOMPARE(v4.z(), (qreal)8.0f); + QCOMPARE(v4.w(), (qreal)9.0f); + + obj.setProperty("vector2D", + qVariantFromValue(QVector2D(-1.0f, -2.0f))); + obj.setProperty("vector3D", + qVariantFromValue(QVector3D(-3.0f, -4.0f, -5.0f))); + obj.setProperty("vector4D", + qVariantFromValue(QVector4D(-6.0f, -7.0f, -8.0f, -9.0f))); + + v2 = qVariantValue<QVector2D>(obj.property("vector2D")); + QCOMPARE(v2.x(), (qreal)-1.0f); + QCOMPARE(v2.y(), (qreal)-2.0f); + + v3 = qVariantValue<QVector3D>(obj.property("vector3D")); + QCOMPARE(v3.x(), (qreal)-3.0f); + QCOMPARE(v3.y(), (qreal)-4.0f); + QCOMPARE(v3.z(), (qreal)-5.0f); + + v4 = qVariantValue<QVector4D>(obj.property("vector4D")); + QCOMPARE(v4.x(), (qreal)-6.0f); + QCOMPARE(v4.y(), (qreal)-7.0f); + QCOMPARE(v4.z(), (qreal)-8.0f); + QCOMPARE(v4.w(), (qreal)-9.0f); +} + +void tst_QVector::metaTypes() +{ + QVERIFY(QMetaType::type("QVector2D") == QMetaType::QVector2D); + QVERIFY(QMetaType::type("QVector3D") == QMetaType::QVector3D); + QVERIFY(QMetaType::type("QVector4D") == QMetaType::QVector4D); + + QCOMPARE(QByteArray(QMetaType::typeName(QMetaType::QVector2D)), + QByteArray("QVector2D")); + QCOMPARE(QByteArray(QMetaType::typeName(QMetaType::QVector3D)), + QByteArray("QVector3D")); + QCOMPARE(QByteArray(QMetaType::typeName(QMetaType::QVector4D)), + QByteArray("QVector4D")); + + QVERIFY(QMetaType::isRegistered(QMetaType::QVector2D)); + QVERIFY(QMetaType::isRegistered(QMetaType::QVector3D)); + QVERIFY(QMetaType::isRegistered(QMetaType::QVector4D)); + + QVERIFY(qMetaTypeId<QVector2D>() == QMetaType::QVector2D); + QVERIFY(qMetaTypeId<QVector3D>() == QMetaType::QVector3D); + QVERIFY(qMetaTypeId<QVector4D>() == QMetaType::QVector4D); +} + QTEST_APPLESS_MAIN(tst_QVector) #include "tst_qvectornd.moc" diff --git a/tests/auto/moc/os9-newlines.h b/tests/auto/moc/os9-newlines.h index fb9bbe4..85fa671 100644 --- a/tests/auto/moc/os9-newlines.h +++ b/tests/auto/moc/os9-newlines.h @@ -1 +1 @@ -/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite 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 either Technology Preview License Agreement or the
** Beta Release License Agreement.
**
** 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.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QObject>
class Os9Newlines : public QObject
{
Q_OBJECT
public Q_SLOTS:
inline void testSlot() {}
};
+/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite 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 either Technology Preview License Agreement or the
** Beta Release License Agreement.
**
** 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.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QObject>
class Os9Newlines : public QObject
{
Q_OBJECT
public Q_SLOTS:
inline void testSlot() {}
};
\ No newline at end of file diff --git a/tests/auto/moc/tst_moc.cpp b/tests/auto/moc/tst_moc.cpp index d66791f..488f068 100644 --- a/tests/auto/moc/tst_moc.cpp +++ b/tests/auto/moc/tst_moc.cpp @@ -1157,6 +1157,13 @@ void tst_Moc::constructors() QObject *o3 = mo->newInstance(Q_ARG(QString, str)); QVERIFY(o3 != 0); QCOMPARE(qobject_cast<CtorTestClass*>(o3)->m_str, str); + + { + //explicit constructor + QObject *o = QObject::staticMetaObject.newInstance(); + QVERIFY(o); + delete o; + } } #include "task234909.h" diff --git a/tests/auto/q3socket/tst_qsocket.cpp b/tests/auto/q3socket/tst_qsocket.cpp index b07fa2f..854cc75 100644 --- a/tests/auto/q3socket/tst_qsocket.cpp +++ b/tests/auto/q3socket/tst_qsocket.cpp @@ -124,8 +124,10 @@ void tst_Q3Socket::peerAddress_data() QTest::addColumn<bool>("peerClosesConnection"); QTest::addColumn<QString>("peerAddr"); - QTest::newRow( "echo" ) << QString("qt-test-server.troll.no") << (uint)7 << false << QString("10.3.3.69"); - QTest::newRow( "daytime" ) << QString("qt-test-server.troll.no") << (uint)13 << true << QString("10.3.3.69"); + QTest::newRow( "echo" ) << QtNetworkSettings::serverLocalName() << (uint)7 << false + << QtNetworkSettings::serverIP().toString(); + QTest::newRow( "daytime" ) << QtNetworkSettings::serverLocalName() << (uint)13 << true + << QtNetworkSettings::serverIP().toString(); } void tst_Q3Socket::peerAddress() @@ -191,7 +193,7 @@ void tst_Q3Socket::emitConnectionRefused() { Q3Socket sock; connect( &sock, SIGNAL(error(int)), SLOT(emitConnectionRefused_error(int)) ); - sock.connectToHost( "ares.troll.no", 12331 ); + sock.connectToHost( QtNetworkSettings::serverLocalName(), 12331 ); emitConnectionRefused_errorReceived = false; QTestEventLoop::instance().enterLoop( 30 ); @@ -225,8 +227,8 @@ void tst_Q3Socket::connectionAttempts_data() QTest::addColumn<int>("port"); QTest::addColumn<bool>("expectedResult"); - QTest::newRow("fluke port 80") << QString("qt-test-server.troll.no") << 80 << true; - QTest::newRow("fluke port 79") << QString("qt-test-server.troll.no") << 79 << false; + QTest::newRow("fluke port 80") << QtNetworkSettings::serverLocalName() << 80 << true; + QTest::newRow("fluke port 79") << QtNetworkSettings::serverLocalName() << 79 << false; } void tst_Q3Socket::connectionAttempts() @@ -266,7 +268,7 @@ void tst_Q3Socket::canReadLine() connect(&socket, SIGNAL(connected()), &loop, SLOT(quit())); // timeout error will fail in #238 connect(&socket, SIGNAL(error(int)), &loop, SLOT(quit())); - socket.connectToHost("qt-test-server.troll.no", 143); + socket.connectToHost(QtNetworkSettings::serverLocalName(), 143); loop.exec(); diff --git a/tests/auto/q3sqlcursor/tst_q3sqlcursor.cpp b/tests/auto/q3sqlcursor/tst_q3sqlcursor.cpp index e4b1a55..e5a9b01 100644 --- a/tests/auto/q3sqlcursor/tst_q3sqlcursor.cpp +++ b/tests/auto/q3sqlcursor/tst_q3sqlcursor.cpp @@ -548,7 +548,7 @@ void tst_Q3SqlCursor::unicode() else QFAIL( QString( "Strings differ at position %1: orig: %2, db: %3" ).arg( i ).arg( utf8str[ i ].unicode(), 0, 16 ).arg( res[ i ].unicode(), 0, 16 ) ); } - if(db.driverName().startsWith("QMYSQL") || db.driverName().startsWith("QDB2")) + if((db.driverName().startsWith("QMYSQL") || db.driverName().startsWith("QDB2")) && res != utf8str) QEXPECT_FAIL("", "See above message", Continue); QVERIFY( res == utf8str ); } @@ -721,7 +721,9 @@ void tst_Q3SqlCursor::updateNoPK() // Sqlite returns 2, don't ask why. QVERIFY(cur.update() != 0); QString expect = "update " + qTableName("qtestPK") + - " set id = 1 , name = NULL , num = NULL where " + qTableName("qtestPK") + ".id" + " set "+db.driver()->escapeIdentifier("id", QSqlDriver::FieldName)+" = 1 , " + +db.driver()->escapeIdentifier("name", QSqlDriver::FieldName)+" = NULL , " + +db.driver()->escapeIdentifier("num", QSqlDriver::FieldName)+" = NULL where " + qTableName("qtestPK") + ".id" " IS NULL and " + qTableName("qtestPK") + ".name IS NULL and " + qTableName("qtestPK") + ".num IS NULL"; if (!db.driver()->hasFeature(QSqlDriver::PreparedQueries)) { diff --git a/tests/auto/q3urloperator/tst_q3urloperator.cpp b/tests/auto/q3urloperator/tst_q3urloperator.cpp index f1e1059..a9e5096 100644 --- a/tests/auto/q3urloperator/tst_q3urloperator.cpp +++ b/tests/auto/q3urloperator/tst_q3urloperator.cpp @@ -55,6 +55,8 @@ #include <q3valuelist.h> #include <qlist.h> +#include "../network-settings.h" + //TESTED_CLASS= //TESTED_FILES= @@ -150,7 +152,7 @@ void tst_Q3UrlOperator::initTestCase() // prepare: make sure that there is a unique directory for FTP upload // testing (to avoid parallel executed tests interfere with each other) - ftpQtestUpload = "ftp://qt-test-server.troll.no/"; + ftpQtestUpload = QString("ftp://%1/").arg(QtNetworkSettings::serverLocalName()); QString dir = QString( "qtest/upload/%1" ).arg( (ulong)this ); Q3UrlOperator opMkdir( ftpQtestUpload ); @@ -218,8 +220,8 @@ void tst_Q3UrlOperator::cleanup() void tst_Q3UrlOperator::copy_data() { - const QString ftpQtest( "ftp://qt-test-server.troll.no/qtest" ); - const QString httpQtest( "http://qt-test-server.troll.no/qtest" ); + const QString ftpQtest( QString("ftp://%1/qtest").arg(QtNetworkSettings::serverLocalName()) ); + const QString httpQtest( QString("http://%1/qtest").arg(QtNetworkSettings::serverLocalName()) ); // argument for the constructor QTest::addColumn<QString>("url"); @@ -339,8 +341,8 @@ void tst_Q3UrlOperator::slotFinished_copy( Q3NetworkOperation *op ) void tst_Q3UrlOperator::put_data() { - const QString httpQtest( "http://qt-test-server.troll.no/qtest/cgi-bin" ); - const QString httpQtestGet( "http://qt-test-server.troll.no/qtest/cgi-bin/retrieve_testfile.cgi" ); + const QString httpQtest( QString("http://%1/qtest/cgi-bin").arg(QtNetworkSettings::serverLocalName()) ); + const QString httpQtestGet( QString("http://%1/qtest/cgi-bin/retrieve_testfile.cgi").arg(QtNetworkSettings::serverLocalName()) ); QByteArray putData_1( 5 ); putData_1[0] = 'a'; diff --git a/tests/auto/qaudiodeviceid/qaudiodeviceid.pro b/tests/auto/qaudiodeviceid/qaudiodeviceid.pro new file mode 100644 index 0000000..e0c7d4d --- /dev/null +++ b/tests/auto/qaudiodeviceid/qaudiodeviceid.pro @@ -0,0 +1,7 @@ +load(qttest_p4) + +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +SOURCES += tst_qaudiodeviceid.cpp + +QT = core multimedia diff --git a/tests/auto/qaudiodeviceid/tst_qaudiodeviceid.cpp b/tests/auto/qaudiodeviceid/tst_qaudiodeviceid.cpp new file mode 100644 index 0000000..d40118b --- /dev/null +++ b/tests/auto/qaudiodeviceid/tst_qaudiodeviceid.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/qlocale.h> +#include <qaudiodeviceid.h> +#include <qaudiodeviceinfo.h> + +#include <QStringList> +#include <QList> + + +class tst_QAudioDeviceId : public QObject +{ + Q_OBJECT + +public: + tst_QAudioDeviceId(QObject* parent=0) : QObject(parent) {} + +private slots: + void checkNull(); + void checkEquality(); +}; + +void tst_QAudioDeviceId::checkNull() +{ + // Default constructed is null. + QAudioDeviceId deviceId0; + QVERIFY(deviceId0.isNull()); + + // Null is transferred + QAudioDeviceId deviceId1(deviceId0); + QVERIFY(deviceId1.isNull()); +} + +void tst_QAudioDeviceId::checkEquality() +{ + QAudioDeviceId deviceId0; + QAudioDeviceId deviceId1; + + // Null ids are equivalent + QVERIFY(deviceId0 == deviceId1); + QVERIFY(!(deviceId0 != deviceId1)); + + deviceId1 = QAudioDeviceInfo::defaultOutputDevice(); + + // Different + QVERIFY(deviceId0 != deviceId1); + QVERIFY(!(deviceId0 == deviceId1)); + + // Same + deviceId0 = deviceId1; + + QVERIFY(deviceId0 == deviceId1); + QVERIFY(!(deviceId0 != deviceId1)); +} + +QTEST_MAIN(tst_QAudioDeviceId) + +#include "tst_qaudiodeviceid.moc" diff --git a/tests/auto/qaudiodeviceinfo/qaudiodeviceinfo.pro b/tests/auto/qaudiodeviceinfo/qaudiodeviceinfo.pro new file mode 100644 index 0000000..695987c --- /dev/null +++ b/tests/auto/qaudiodeviceinfo/qaudiodeviceinfo.pro @@ -0,0 +1,7 @@ +load(qttest_p4) + +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +SOURCES += tst_qaudiodeviceinfo.cpp + +QT = core multimedia diff --git a/tests/auto/qaudiodeviceinfo/tst_qaudiodeviceinfo.cpp b/tests/auto/qaudiodeviceinfo/tst_qaudiodeviceinfo.cpp new file mode 100644 index 0000000..c2cd97e --- /dev/null +++ b/tests/auto/qaudiodeviceinfo/tst_qaudiodeviceinfo.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtCore/qlocale.h> +#include <qaudiodeviceinfo.h> + +#include <QStringList> +#include <QList> + + +class tst_QAudioDeviceInfo : public QObject +{ + Q_OBJECT +public: + tst_QAudioDeviceInfo(QObject* parent=0) : QObject(parent) {} + +private slots: + void checkAvailableDefaultInput(); + void checkAvailableDefaultOutput(); + void outputList(); + void codecs(); + void channels(); + void sampleSizes(); + void byteOrders(); + void sampleTypes(); + void frequencies(); + void isformat(); + void preferred(); + void nearest(); + +private: + QAudioDeviceInfo* device; +}; + +void tst_QAudioDeviceInfo::checkAvailableDefaultInput() +{ + QVERIFY(!QAudioDeviceInfo::defaultInputDevice().isNull()); +} + +void tst_QAudioDeviceInfo::checkAvailableDefaultOutput() +{ + QVERIFY(!QAudioDeviceInfo::defaultOutputDevice().isNull()); +} + +void tst_QAudioDeviceInfo::outputList() +{ + QList<QAudioDeviceId> devices = QAudioDeviceInfo::deviceList(QAudio::AudioOutput); + QVERIFY(devices.size() > 0); + device = new QAudioDeviceInfo(devices.at(0), this); +} + +void tst_QAudioDeviceInfo::codecs() +{ + QStringList avail = device->supportedCodecs(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::channels() +{ + QList<int> avail = device->supportedChannels(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::sampleSizes() +{ + QList<int> avail = device->supportedSampleSizes(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::byteOrders() +{ + QList<QAudioFormat::Endian> avail = device->supportedByteOrders(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::sampleTypes() +{ + QList<QAudioFormat::SampleType> avail = device->supportedSampleTypes(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::frequencies() +{ + QList<int> avail = device->supportedFrequencies(); + QVERIFY(avail.size() > 0); +} + +void tst_QAudioDeviceInfo::isformat() +{ + QAudioFormat format; + format.setFrequency(44100); + format.setChannels(2); + format.setSampleType(QAudioFormat::SignedInt); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleSize(16); + format.setCodec("audio/pcm"); + + // Should always be true for these format + QVERIFY(device->isFormatSupported(format)); +} + +void tst_QAudioDeviceInfo::preferred() +{ + QAudioFormat format = device->preferredFormat(); + QVERIFY(format.frequency() == 44100); + QVERIFY(format.channels() == 2); +} + +void tst_QAudioDeviceInfo::nearest() +{ + QAudioFormat format1, format2; + format1.setFrequency(8000); + format2 = device->nearestFormat(format1); + QVERIFY(format2.frequency() == 44100); +} + +QTEST_MAIN(tst_QAudioDeviceInfo) + +#include "tst_qaudiodeviceinfo.moc" diff --git a/tests/auto/qaudioformat/qaudioformat.pro b/tests/auto/qaudioformat/qaudioformat.pro new file mode 100644 index 0000000..78962d7 --- /dev/null +++ b/tests/auto/qaudioformat/qaudioformat.pro @@ -0,0 +1,7 @@ +load(qttest_p4) + +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +SOURCES += tst_qaudioformat.cpp + +QT = core multimedia diff --git a/tests/auto/qaudioformat/tst_qaudioformat.cpp b/tests/auto/qaudioformat/tst_qaudioformat.cpp new file mode 100644 index 0000000..bcfc78f --- /dev/null +++ b/tests/auto/qaudioformat/tst_qaudioformat.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtCore/qlocale.h> +#include <qaudioformat.h> + +#include <QStringList> +#include <QList> + + +class tst_QAudioFormat : public QObject +{ + Q_OBJECT + +public: + tst_QAudioFormat(QObject* parent=0) : QObject(parent) {} + +private slots: + void checkNull(); + void checkFrequency(); + void checkChannels(); + void checkSampleSize(); + void checkCodec(); + void checkByteOrder(); + void checkSampleType(); + void checkEquality(); + void checkAssignment(); +}; + +void tst_QAudioFormat::checkNull() +{ + // Default constructed QAudioFormat is null. + QAudioFormat audioFormat0; + QVERIFY(audioFormat0.isNull()); + + // Null is transferred + QAudioFormat audioFormat1(audioFormat0); + QVERIFY(audioFormat1.isNull()); + + // Null is voided on activity + audioFormat0.setFrequency(44100); + QVERIFY(!audioFormat0.isNull()); +} + +void tst_QAudioFormat::checkFrequency() +{ + QAudioFormat audioFormat; + audioFormat.setFrequency(44100); + QVERIFY(audioFormat.frequency() == 44100); +} + +void tst_QAudioFormat::checkChannels() +{ + QAudioFormat audioFormat; + audioFormat.setChannels(2); + QVERIFY(audioFormat.channels() == 2); +} + +void tst_QAudioFormat::checkSampleSize() +{ + QAudioFormat audioFormat; + audioFormat.setSampleSize(16); + QVERIFY(audioFormat.sampleSize() == 16); +} + +void tst_QAudioFormat::checkCodec() +{ + QAudioFormat audioFormat; + audioFormat.setCodec(QString::fromLatin1("audio/pcm")); + QVERIFY(audioFormat.codec() == QString::fromLatin1("audio/pcm")); +} + +void tst_QAudioFormat::checkByteOrder() +{ + QAudioFormat audioFormat; + audioFormat.setByteOrder(QAudioFormat::LittleEndian); + QVERIFY(audioFormat.byteOrder() == QAudioFormat::LittleEndian); +} + +void tst_QAudioFormat::checkSampleType() +{ + QAudioFormat audioFormat; + audioFormat.setSampleType(QAudioFormat::SignedInt); + QVERIFY(audioFormat.sampleType() == QAudioFormat::SignedInt); +} + +void tst_QAudioFormat::checkEquality() +{ + QAudioFormat audioFormat0; + QAudioFormat audioFormat1; + + // Null formats are equivalent + QVERIFY(audioFormat0 == audioFormat1); + QVERIFY(!(audioFormat0 != audioFormat1)); + + // on filled formats + audioFormat0.setFrequency(8000); + audioFormat0.setChannels(1); + audioFormat0.setSampleSize(8); + audioFormat0.setCodec("audio/pcm"); + audioFormat0.setByteOrder(QAudioFormat::LittleEndian); + audioFormat0.setSampleType(QAudioFormat::UnSignedInt); + + audioFormat1.setFrequency(8000); + audioFormat1.setChannels(1); + audioFormat1.setSampleSize(8); + audioFormat1.setCodec("audio/pcm"); + audioFormat1.setByteOrder(QAudioFormat::LittleEndian); + audioFormat1.setSampleType(QAudioFormat::UnSignedInt); + + QVERIFY(audioFormat0 == audioFormat1); + QVERIFY(!(audioFormat0 != audioFormat1)); + + audioFormat0.setFrequency(44100); + QVERIFY(audioFormat0 != audioFormat1); + QVERIFY(!(audioFormat0 == audioFormat1)); +} + +void tst_QAudioFormat::checkAssignment() +{ + QAudioFormat audioFormat0; + QAudioFormat audioFormat1; + + audioFormat0.setFrequency(8000); + audioFormat0.setChannels(1); + audioFormat0.setSampleSize(8); + audioFormat0.setCodec("audio/pcm"); + audioFormat0.setByteOrder(QAudioFormat::LittleEndian); + audioFormat0.setSampleType(QAudioFormat::UnSignedInt); + + audioFormat1 = audioFormat0; + QVERIFY(audioFormat1 == audioFormat0); + + QAudioFormat audioFormat2(audioFormat0); + QVERIFY(audioFormat2 == audioFormat0); +} + +QTEST_MAIN(tst_QAudioFormat) + +#include "tst_qaudioformat.moc" diff --git a/tests/auto/qaudioinput/qaudioinput.pro b/tests/auto/qaudioinput/qaudioinput.pro new file mode 100644 index 0000000..8a03749 --- /dev/null +++ b/tests/auto/qaudioinput/qaudioinput.pro @@ -0,0 +1,7 @@ +load(qttest_p4) + +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +SOURCES += tst_qaudioinput.cpp + +QT = core multimedia diff --git a/tests/auto/qaudioinput/tst_qaudioinput.cpp b/tests/auto/qaudioinput/tst_qaudioinput.cpp new file mode 100644 index 0000000..6e16320 --- /dev/null +++ b/tests/auto/qaudioinput/tst_qaudioinput.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/qlocale.h> +#include <qaudioinput.h> +#include <qaudiodeviceinfo.h> +#include <qaudio.h> +#include <qaudioformat.h> + + +class tst_QAudioInput : public QObject +{ + Q_OBJECT +public: + tst_QAudioInput(QObject* parent=0) : QObject(parent) {} + +private slots: + void initTestCase(); + void settings(); + void notifyInterval(); + void pullFile(); + +private: + QAudioFormat format; + QAudioInput* audio; +}; + +void tst_QAudioInput::initTestCase() +{ + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::UnSignedInt); + + audio = new QAudioInput(format, this); +} + +void tst_QAudioInput::settings() +{ + QAudioFormat f = audio->format(); + + QVERIFY(format.channels() == f.channels()); + QVERIFY(format.frequency() == f.frequency()); + QVERIFY(format.sampleSize() == f.sampleSize()); + QVERIFY(format.codec() == f.codec()); + QVERIFY(format.byteOrder() == f.byteOrder()); + QVERIFY(format.sampleType() == f.sampleType()); +} + +void tst_QAudioInput::notifyInterval() +{ + QVERIFY(audio->notifyInterval() == 1000); // Default + + audio->setNotifyInterval(500); + QVERIFY(audio->notifyInterval() == 500); // Custom + + audio->setNotifyInterval(1000); // reset +} + +void tst_QAudioInput::pullFile() +{ + QFile filename(SRCDIR "test.raw"); + filename.open( QIODevice::WriteOnly | QIODevice::Truncate ); + + QSignalSpy readSignal(audio, SIGNAL(notify())); + audio->start(&filename); + + QTest::qWait(5000); + + QVERIFY(readSignal.count() > 0); + QVERIFY(audio->totalTime() > 0); + + audio->stop(); + filename.close(); +} + +QTEST_MAIN(tst_QAudioInput) + +#include "tst_qaudioinput.moc" diff --git a/tests/auto/qaudiooutput/4.wav b/tests/auto/qaudiooutput/4.wav Binary files differnew file mode 100644 index 0000000..e31b0609 --- /dev/null +++ b/tests/auto/qaudiooutput/4.wav diff --git a/tests/auto/qaudiooutput/qaudiooutput.pro b/tests/auto/qaudiooutput/qaudiooutput.pro new file mode 100644 index 0000000..6c07c64 --- /dev/null +++ b/tests/auto/qaudiooutput/qaudiooutput.pro @@ -0,0 +1,12 @@ +load(qttest_p4) + +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +SOURCES += tst_qaudiooutput.cpp + +QT = core multimedia + +wince*: { + deploy.sources += 4.wav + DEPLOYMENT = deploy +} diff --git a/tests/auto/qaudiooutput/tst_qaudiooutput.cpp b/tests/auto/qaudiooutput/tst_qaudiooutput.cpp new file mode 100644 index 0000000..0f94faa --- /dev/null +++ b/tests/auto/qaudiooutput/tst_qaudiooutput.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + + +#include <QtTest/QtTest> +#include <QtCore/qlocale.h> +#include <qaudiooutput.h> +#include <qaudiodeviceinfo.h> +#include <qaudio.h> +#include <qaudioformat.h> + + +class tst_QAudioOutput : public QObject +{ + Q_OBJECT +public: + tst_QAudioOutput(QObject* parent=0) : QObject(parent) {} + +private slots: + void initTestCase(); + void settings(); + void notifyInterval(); + void pullFile(); + void pushFile(); + +private: + QAudioFormat format; + QAudioOutput* audio; +}; + +void tst_QAudioOutput::initTestCase() +{ + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::UnSignedInt); + + audio = new QAudioOutput(format, this); +} + +void tst_QAudioOutput::settings() +{ + QAudioFormat f = audio->format(); + + QVERIFY(format.channels() == f.channels()); + QVERIFY(format.frequency() == f.frequency()); + QVERIFY(format.sampleSize() == f.sampleSize()); + QVERIFY(format.codec() == f.codec()); + QVERIFY(format.byteOrder() == f.byteOrder()); + QVERIFY(format.sampleType() == f.sampleType()); +} + +void tst_QAudioOutput::notifyInterval() +{ + QVERIFY(audio->notifyInterval() == 1000); // Default + + audio->setNotifyInterval(500); + QVERIFY(audio->notifyInterval() == 500); // Custom + + audio->setNotifyInterval(1000); // reset +} + +void tst_QAudioOutput::pullFile() +{ + QFile filename(SRCDIR "4.wav"); + QVERIFY(filename.exists()); + filename.open(QIODevice::ReadOnly); + + QSignalSpy readSignal(audio, SIGNAL(notify())); + audio->setNotifyInterval(100); + audio->start(&filename); + + QTestEventLoop::instance().enterLoop(1); + // 4.wav is a little less than 700ms, so notify should fire 6 times! + QVERIFY(readSignal.count() >= 6); + QVERIFY(audio->totalTime() == 692250); + + audio->stop(); + filename.close(); +} + +void tst_QAudioOutput::pushFile() +{ + QFile filename(SRCDIR "4.wav"); + QVERIFY(filename.exists()); + filename.open(QIODevice::ReadOnly); + + const qint64 fileSize = filename.size(); + + QIODevice* feed = audio->start(0); + + char* buffer = new char[fileSize]; + filename.read(buffer, fileSize); + + qint64 counter=0; + qint64 written=0; + while(written < fileSize) { + written+=feed->write(buffer+written,fileSize-written); + QTest::qWait(20); + counter++; + } + QTestEventLoop::instance().enterLoop(1); + + QVERIFY(written == fileSize); + QVERIFY(audio->totalTime() == 692250); + + audio->stop(); + filename.close(); + delete [] buffer; + delete audio; +} + +QTEST_MAIN(tst_QAudioOutput) + +#include "tst_qaudiooutput.moc" diff --git a/tests/auto/qcompleter/tst_qcompleter.cpp b/tests/auto/qcompleter/tst_qcompleter.cpp index 0a9c16a..a65490d 100644 --- a/tests/auto/qcompleter/tst_qcompleter.cpp +++ b/tests/auto/qcompleter/tst_qcompleter.cpp @@ -138,6 +138,7 @@ private slots: void setters(); void dynamicSortOrder(); + void disabledItems(); // task-specific tests below me void task178797_activatedOnReturn(); @@ -147,7 +148,6 @@ private slots: void task253125_lineEditCompletion_data(); void task253125_lineEditCompletion(); - void task247560_keyboardNavigation(); private: @@ -1106,6 +1106,31 @@ void tst_QCompleter::dynamicSortOrder() QCOMPARE(completer.completionCount(), 12); } +void tst_QCompleter::disabledItems() +{ + QLineEdit lineEdit; + QStandardItemModel *model = new QStandardItemModel(&lineEdit); + QStandardItem *suggestions = new QStandardItem("suggestions"); + suggestions->setEnabled(false); + model->appendRow(suggestions); + model->appendRow(new QStandardItem("suggestions Enabled")); + QCompleter *completer = new QCompleter(model, &lineEdit); + QSignalSpy spy(completer, SIGNAL(activated(const QString &))); + lineEdit.setCompleter(completer); + lineEdit.show(); + + QTest::keyPress(&lineEdit, Qt::Key_S); + QTest::keyPress(&lineEdit, Qt::Key_U); + QAbstractItemView *view = lineEdit.completer()->popup(); + QVERIFY(view->isVisible()); + QTest::mouseClick(view->viewport(), Qt::LeftButton, 0, view->visualRect(view->model()->index(0, 0)).center()); + QCOMPARE(spy.count(), 0); + QVERIFY(view->isVisible()); + QTest::mouseClick(view->viewport(), Qt::LeftButton, 0, view->visualRect(view->model()->index(1, 0)).center()); + QCOMPARE(spy.count(), 1); + QVERIFY(!view->isVisible()); +} + void tst_QCompleter::task178797_activatedOnReturn() { QStringList words; diff --git a/tests/auto/qdom/tst_qdom.cpp b/tests/auto/qdom/tst_qdom.cpp index ea3b64e..5b4787f 100644 --- a/tests/auto/qdom/tst_qdom.cpp +++ b/tests/auto/qdom/tst_qdom.cpp @@ -1386,7 +1386,7 @@ void tst_QDom::roundTripAttributes() const doc.save(stream, 0); stream.flush(); - const QByteArray expected("<localName xmlns=\"\" attr=\" 
	 \" />\n"); + const QByteArray expected("<localName xmlns=\"\" attr=\" 
	 \"/>\n"); QCOMPARE(QString::fromLatin1(serialized.constData()), QString::fromLatin1(expected.constData())); } @@ -1671,7 +1671,7 @@ void tst_QDom::appendDocumentNode() const doc.appendChild(elem); Q_ASSERT(!xml.isNull()); - const QString expected(QLatin1String("<document>\n<test_elem name=\"value\" />\n</document>\n")); + const QString expected(QLatin1String("<document>\n<test_elem name=\"value\"/>\n</document>\n")); elem.appendChild(xml); QCOMPARE(doc.childNodes().count(), 1); @@ -1788,8 +1788,8 @@ void tst_QDom::doubleNamespaceDeclarations() const QXmlInputSource source(&file); QVERIFY(doc.setContent(&source, &reader)); - QVERIFY(doc.toString(0) == QString::fromLatin1("<a>\n<b p:c=\"\" xmlns:p=\"NS\" p:d=\"\" />\n</a>\n") || - doc.toString(0) == QString::fromLatin1("<a>\n<b p:c=\"\" p:d=\"\" xmlns:p=\"NS\" />\n</a>\n")); + QVERIFY(doc.toString(0) == QString::fromLatin1("<a>\n<b p:c=\"\" xmlns:p=\"NS\" p:d=\"\"/>\n</a>\n") || + doc.toString(0) == QString::fromLatin1("<a>\n<b p:c=\"\" p:d=\"\" xmlns:p=\"NS\"/>\n</a>\n")); } void tst_QDom::setContentQXmlReaderOverload() const diff --git a/tests/auto/qfiledialog/tst_qfiledialog.cpp b/tests/auto/qfiledialog/tst_qfiledialog.cpp index c31ecf2..0f307dc 100644 --- a/tests/auto/qfiledialog/tst_qfiledialog.cpp +++ b/tests/auto/qfiledialog/tst_qfiledialog.cpp @@ -161,6 +161,7 @@ private slots: void task251341_sideBarRemoveEntries(); void task254490_selectFileMultipleTimes(); void task257579_sideBarWithNonCleanUrls(); + void task259105_filtersCornerCases(); private: QByteArray userSettings; @@ -2057,6 +2058,48 @@ void tst_QFiledialog::task257579_sideBarWithNonCleanUrls() #endif } +void tst_QFiledialog::task259105_filtersCornerCases() +{ + QNonNativeFileDialog fd(0, "TestFileDialog"); + fd.setNameFilter(QLatin1String("All Files! (*);;Text Files (*.txt)")); + fd.setOption(QFileDialog::HideNameFilterDetails, true); + fd.show(); + QTest::qWait(250); + + //Extensions are hidden + QComboBox *filters = qFindChild<QComboBox*>(&fd, "fileTypeCombo"); + QVERIFY(filters); + QCOMPARE(filters->currentText(), QLatin1String("All Files!")); + filters->setCurrentIndex(1); + QCOMPARE(filters->currentText(), QLatin1String("Text Files")); + + //We should have the full names + fd.setOption(QFileDialog::HideNameFilterDetails, false); + QTest::qWait(250); + filters->setCurrentIndex(0); + QCOMPARE(filters->currentText(), QLatin1String("All Files! (*)")); + filters->setCurrentIndex(1); + QCOMPARE(filters->currentText(), QLatin1String("Text Files (*.txt)")); + + //Corner case undocumented of the task + fd.setNameFilter(QLatin1String("\352 (I like cheese) All Files! (*);;Text Files (*.txt)")); + QCOMPARE(filters->currentText(), QLatin1String("\352 (I like cheese) All Files! (*)")); + filters->setCurrentIndex(1); + QCOMPARE(filters->currentText(), QLatin1String("Text Files (*.txt)")); + + fd.setOption(QFileDialog::HideNameFilterDetails, true); + filters->setCurrentIndex(0); + QTest::qWait(500); + QCOMPARE(filters->currentText(), QLatin1String("\352 (I like cheese) All Files!")); + filters->setCurrentIndex(1); + QCOMPARE(filters->currentText(), QLatin1String("Text Files")); + fd.setOption(QFileDialog::HideNameFilterDetails, true); + filters->setCurrentIndex(0); + QTest::qWait(500); + QCOMPARE(filters->currentText(), QLatin1String("\352 (I like cheese) All Files!")); + filters->setCurrentIndex(1); + QCOMPARE(filters->currentText(), QLatin1String("Text Files")); +} QTEST_MAIN(tst_QFiledialog) #include "tst_qfiledialog.moc" diff --git a/tests/auto/qformlayout/tst_qformlayout.cpp b/tests/auto/qformlayout/tst_qformlayout.cpp index 74384a9..9806557 100644 --- a/tests/auto/qformlayout/tst_qformlayout.cpp +++ b/tests/auto/qformlayout/tst_qformlayout.cpp @@ -125,7 +125,6 @@ private slots: Qt::Orientations expandingDirections() const; */ - void fieldMinimumSize(); }; tst_QFormLayout::tst_QFormLayout() @@ -906,35 +905,6 @@ void tst_QFormLayout::layoutAlone() QTest::qWait(500); } - -void tst_QFormLayout::fieldMinimumSize() -{ - //check that the field with is bigger than its minimumSizeHint for any size of the widget - // even if the label with is not fixed - QWidget w; - QFormLayout layout; - layout.setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); - w.setLayout(&layout); - QLabel label1("Here is a strange test case"); - label1.setWordWrap(true); - QLabel label2("Here is another label"); - label2.setWordWrap(true); - QLabel shortLabel("short"); - QLabel longLabel("Quite long label"); - layout.addRow(&label1, &shortLabel); - layout.addRow(&label2, &longLabel); - w.show(); - int width = w.size().width() + 9; - - do { - w.resize(width, w.size().height()); - layout.activate(); - QVERIFY(shortLabel.size().width() >= shortLabel.minimumSizeHint().width()); - QVERIFY(longLabel.size().width() >= longLabel.minimumSizeHint().width()); - width -= 3; - } while(width >= w.minimumSizeHint().width()); -} - QTEST_MAIN(tst_QFormLayout) #include "tst_qformlayout.moc" diff --git a/tests/auto/qgl/tst_qgl.cpp b/tests/auto/qgl/tst_qgl.cpp index 96f5ddd..31c4a3c 100644 --- a/tests/auto/qgl/tst_qgl.cpp +++ b/tests/auto/qgl/tst_qgl.cpp @@ -45,6 +45,7 @@ #include <qcoreapplication.h> #include <qdebug.h> #include <qgl.h> +#include <qglcolormap.h> #include <QGraphicsView> #include <QGraphicsProxyWidget> @@ -66,6 +67,7 @@ private slots: void graphicsViewClipping(); void partialGLWidgetUpdates_data(); void partialGLWidgetUpdates(); + void colormap(); }; tst_QGL::tst_QGL() @@ -94,6 +96,11 @@ public: void setAutoBufferSwap(bool on) { QGLWidget::setAutoBufferSwap(on); } }; +// Using INT_MIN and INT_MAX will cause failures on systems +// where "int" is 64-bit, so use the explicit values instead. +#define TEST_INT_MIN (-2147483647 - 1) +#define TEST_INT_MAX 2147483647 + // Testing get/set functions void tst_QGL::getSetCheck() { @@ -103,120 +110,286 @@ void tst_QGL::getSetCheck() QGLFormat obj1; // int QGLFormat::depthBufferSize() // void QGLFormat::setDepthBufferSize(int) + QCOMPARE(-1, obj1.depthBufferSize()); obj1.setDepthBufferSize(0); QCOMPARE(0, obj1.depthBufferSize()); - obj1.setDepthBufferSize(INT_MIN); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -2147483648"); + obj1.setDepthBufferSize(TEST_INT_MIN); QCOMPARE(0, obj1.depthBufferSize()); // Makes no sense with a negative buffer size - obj1.setDepthBufferSize(INT_MAX); - QCOMPARE(INT_MAX, obj1.depthBufferSize()); + obj1.setDepthBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -1"); + obj1.setDepthBufferSize(-1); + QCOMPARE(3, obj1.depthBufferSize()); + obj1.setDepthBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.depthBufferSize()); // int QGLFormat::accumBufferSize() // void QGLFormat::setAccumBufferSize(int) + QCOMPARE(-1, obj1.accumBufferSize()); obj1.setAccumBufferSize(0); QCOMPARE(0, obj1.accumBufferSize()); - obj1.setAccumBufferSize(INT_MIN); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -2147483648"); + obj1.setAccumBufferSize(TEST_INT_MIN); QCOMPARE(0, obj1.accumBufferSize()); // Makes no sense with a negative buffer size - obj1.setAccumBufferSize(INT_MAX); - QCOMPARE(INT_MAX, obj1.accumBufferSize()); + obj1.setAccumBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -1"); + obj1.setAccumBufferSize(-1); + QCOMPARE(3, obj1.accumBufferSize()); + obj1.setAccumBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.accumBufferSize()); + + // int QGLFormat::redBufferSize() + // void QGLFormat::setRedBufferSize(int) + QCOMPARE(-1, obj1.redBufferSize()); + obj1.setRedBufferSize(0); + QCOMPARE(0, obj1.redBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -2147483648"); + obj1.setRedBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.redBufferSize()); // Makes no sense with a negative buffer size + obj1.setRedBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -1"); + obj1.setRedBufferSize(-1); + QCOMPARE(3, obj1.redBufferSize()); + obj1.setRedBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.redBufferSize()); + + // int QGLFormat::greenBufferSize() + // void QGLFormat::setGreenBufferSize(int) + QCOMPARE(-1, obj1.greenBufferSize()); + obj1.setGreenBufferSize(0); + QCOMPARE(0, obj1.greenBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -2147483648"); + obj1.setGreenBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.greenBufferSize()); // Makes no sense with a negative buffer size + obj1.setGreenBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -1"); + obj1.setGreenBufferSize(-1); + QCOMPARE(3, obj1.greenBufferSize()); + obj1.setGreenBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.greenBufferSize()); + + // int QGLFormat::blueBufferSize() + // void QGLFormat::setBlueBufferSize(int) + QCOMPARE(-1, obj1.blueBufferSize()); + obj1.setBlueBufferSize(0); + QCOMPARE(0, obj1.blueBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -2147483648"); + obj1.setBlueBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.blueBufferSize()); // Makes no sense with a negative buffer size + obj1.setBlueBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -1"); + obj1.setBlueBufferSize(-1); + QCOMPARE(3, obj1.blueBufferSize()); + obj1.setBlueBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.blueBufferSize()); // int QGLFormat::alphaBufferSize() // void QGLFormat::setAlphaBufferSize(int) + QCOMPARE(-1, obj1.alphaBufferSize()); + QCOMPARE(false, obj1.alpha()); + QVERIFY(!obj1.testOption(QGL::AlphaChannel)); + QVERIFY(obj1.testOption(QGL::NoAlphaChannel)); obj1.setAlphaBufferSize(0); + QCOMPARE(true, obj1.alpha()); // setAlphaBufferSize() enables alpha. QCOMPARE(0, obj1.alphaBufferSize()); - obj1.setAlphaBufferSize(INT_MIN); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -2147483648"); + obj1.setAlphaBufferSize(TEST_INT_MIN); QCOMPARE(0, obj1.alphaBufferSize()); // Makes no sense with a negative buffer size - obj1.setAlphaBufferSize(INT_MAX); - QCOMPARE(INT_MAX, obj1.alphaBufferSize()); + obj1.setAlphaBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -1"); + obj1.setAlphaBufferSize(-1); + QCOMPARE(3, obj1.alphaBufferSize()); + obj1.setAlphaBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.alphaBufferSize()); // int QGLFormat::stencilBufferSize() // void QGLFormat::setStencilBufferSize(int) + QCOMPARE(-1, obj1.stencilBufferSize()); obj1.setStencilBufferSize(0); QCOMPARE(0, obj1.stencilBufferSize()); - obj1.setStencilBufferSize(INT_MIN); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -2147483648"); + obj1.setStencilBufferSize(TEST_INT_MIN); QCOMPARE(0, obj1.stencilBufferSize()); // Makes no sense with a negative buffer size - obj1.setStencilBufferSize(INT_MAX); - QCOMPARE(INT_MAX, obj1.stencilBufferSize()); + obj1.setStencilBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -1"); + obj1.setStencilBufferSize(-1); + QCOMPARE(3, obj1.stencilBufferSize()); + obj1.setStencilBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.stencilBufferSize()); // bool QGLFormat::sampleBuffers() // void QGLFormat::setSampleBuffers(bool) + QCOMPARE(false, obj1.sampleBuffers()); + QVERIFY(!obj1.testOption(QGL::SampleBuffers)); + QVERIFY(obj1.testOption(QGL::NoSampleBuffers)); obj1.setSampleBuffers(false); QCOMPARE(false, obj1.sampleBuffers()); + QVERIFY(obj1.testOption(QGL::NoSampleBuffers)); obj1.setSampleBuffers(true); QCOMPARE(true, obj1.sampleBuffers()); + QVERIFY(obj1.testOption(QGL::SampleBuffers)); // int QGLFormat::samples() // void QGLFormat::setSamples(int) + QCOMPARE(-1, obj1.samples()); obj1.setSamples(0); QCOMPARE(0, obj1.samples()); - obj1.setSamples(INT_MIN); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setSamples: Cannot have negative number of samples per pixel -2147483648"); + obj1.setSamples(TEST_INT_MIN); QCOMPARE(0, obj1.samples()); // Makes no sense with a negative sample size - obj1.setSamples(INT_MAX); - QCOMPARE(INT_MAX, obj1.samples()); + obj1.setSamples(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setSamples: Cannot have negative number of samples per pixel -1"); + obj1.setSamples(-1); + QCOMPARE(3, obj1.samples()); + obj1.setSamples(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.samples()); + + // int QGLFormat::swapInterval() + // void QGLFormat::setSwapInterval(int) + QCOMPARE(-1, obj1.swapInterval()); + obj1.setSwapInterval(0); + QCOMPARE(0, obj1.swapInterval()); + obj1.setSwapInterval(TEST_INT_MIN); + QCOMPARE(TEST_INT_MIN, obj1.swapInterval()); + obj1.setSwapInterval(-1); + QCOMPARE(-1, obj1.swapInterval()); + obj1.setSwapInterval(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.swapInterval()); // bool QGLFormat::doubleBuffer() // void QGLFormat::setDoubleBuffer(bool) + QCOMPARE(true, obj1.doubleBuffer()); + QVERIFY(obj1.testOption(QGL::DoubleBuffer)); + QVERIFY(!obj1.testOption(QGL::SingleBuffer)); obj1.setDoubleBuffer(false); QCOMPARE(false, obj1.doubleBuffer()); + QVERIFY(!obj1.testOption(QGL::DoubleBuffer)); + QVERIFY(obj1.testOption(QGL::SingleBuffer)); obj1.setDoubleBuffer(true); QCOMPARE(true, obj1.doubleBuffer()); + QVERIFY(obj1.testOption(QGL::DoubleBuffer)); + QVERIFY(!obj1.testOption(QGL::SingleBuffer)); // bool QGLFormat::depth() // void QGLFormat::setDepth(bool) + QCOMPARE(true, obj1.depth()); + QVERIFY(obj1.testOption(QGL::DepthBuffer)); + QVERIFY(!obj1.testOption(QGL::NoDepthBuffer)); obj1.setDepth(false); QCOMPARE(false, obj1.depth()); + QVERIFY(!obj1.testOption(QGL::DepthBuffer)); + QVERIFY(obj1.testOption(QGL::NoDepthBuffer)); obj1.setDepth(true); QCOMPARE(true, obj1.depth()); + QVERIFY(obj1.testOption(QGL::DepthBuffer)); + QVERIFY(!obj1.testOption(QGL::NoDepthBuffer)); // bool QGLFormat::rgba() // void QGLFormat::setRgba(bool) + QCOMPARE(true, obj1.rgba()); + QVERIFY(obj1.testOption(QGL::Rgba)); + QVERIFY(!obj1.testOption(QGL::ColorIndex)); obj1.setRgba(false); QCOMPARE(false, obj1.rgba()); + QVERIFY(!obj1.testOption(QGL::Rgba)); + QVERIFY(obj1.testOption(QGL::ColorIndex)); obj1.setRgba(true); QCOMPARE(true, obj1.rgba()); + QVERIFY(obj1.testOption(QGL::Rgba)); + QVERIFY(!obj1.testOption(QGL::ColorIndex)); // bool QGLFormat::alpha() // void QGLFormat::setAlpha(bool) + QVERIFY(obj1.testOption(QGL::AlphaChannel)); + QVERIFY(!obj1.testOption(QGL::NoAlphaChannel)); obj1.setAlpha(false); QCOMPARE(false, obj1.alpha()); + QVERIFY(!obj1.testOption(QGL::AlphaChannel)); + QVERIFY(obj1.testOption(QGL::NoAlphaChannel)); obj1.setAlpha(true); QCOMPARE(true, obj1.alpha()); + QVERIFY(obj1.testOption(QGL::AlphaChannel)); + QVERIFY(!obj1.testOption(QGL::NoAlphaChannel)); // bool QGLFormat::accum() // void QGLFormat::setAccum(bool) + QCOMPARE(false, obj1.accum()); + QVERIFY(!obj1.testOption(QGL::AccumBuffer)); + QVERIFY(obj1.testOption(QGL::NoAccumBuffer)); obj1.setAccum(false); QCOMPARE(false, obj1.accum()); + QVERIFY(!obj1.testOption(QGL::AccumBuffer)); + QVERIFY(obj1.testOption(QGL::NoAccumBuffer)); obj1.setAccum(true); QCOMPARE(true, obj1.accum()); + QVERIFY(obj1.testOption(QGL::AccumBuffer)); + QVERIFY(!obj1.testOption(QGL::NoAccumBuffer)); // bool QGLFormat::stencil() // void QGLFormat::setStencil(bool) + QCOMPARE(true, obj1.stencil()); + QVERIFY(obj1.testOption(QGL::StencilBuffer)); + QVERIFY(!obj1.testOption(QGL::NoStencilBuffer)); obj1.setStencil(false); QCOMPARE(false, obj1.stencil()); + QVERIFY(!obj1.testOption(QGL::StencilBuffer)); + QVERIFY(obj1.testOption(QGL::NoStencilBuffer)); obj1.setStencil(true); QCOMPARE(true, obj1.stencil()); + QVERIFY(obj1.testOption(QGL::StencilBuffer)); + QVERIFY(!obj1.testOption(QGL::NoStencilBuffer)); // bool QGLFormat::stereo() // void QGLFormat::setStereo(bool) + QCOMPARE(false, obj1.stereo()); + QVERIFY(!obj1.testOption(QGL::StereoBuffers)); + QVERIFY(obj1.testOption(QGL::NoStereoBuffers)); obj1.setStereo(false); QCOMPARE(false, obj1.stereo()); + QVERIFY(!obj1.testOption(QGL::StereoBuffers)); + QVERIFY(obj1.testOption(QGL::NoStereoBuffers)); obj1.setStereo(true); QCOMPARE(true, obj1.stereo()); + QVERIFY(obj1.testOption(QGL::StereoBuffers)); + QVERIFY(!obj1.testOption(QGL::NoStereoBuffers)); // bool QGLFormat::directRendering() // void QGLFormat::setDirectRendering(bool) + QCOMPARE(true, obj1.directRendering()); + QVERIFY(obj1.testOption(QGL::DirectRendering)); + QVERIFY(!obj1.testOption(QGL::IndirectRendering)); obj1.setDirectRendering(false); QCOMPARE(false, obj1.directRendering()); + QVERIFY(!obj1.testOption(QGL::DirectRendering)); + QVERIFY(obj1.testOption(QGL::IndirectRendering)); obj1.setDirectRendering(true); QCOMPARE(true, obj1.directRendering()); + QVERIFY(obj1.testOption(QGL::DirectRendering)); + QVERIFY(!obj1.testOption(QGL::IndirectRendering)); + + // bool QGLFormat::overlay() + // void QGLFormat::setOverlay(bool) + QCOMPARE(false, obj1.hasOverlay()); + QVERIFY(!obj1.testOption(QGL::HasOverlay)); + QVERIFY(obj1.testOption(QGL::NoOverlay)); + obj1.setOverlay(false); + QCOMPARE(false, obj1.hasOverlay()); + QVERIFY(!obj1.testOption(QGL::HasOverlay)); + QVERIFY(obj1.testOption(QGL::NoOverlay)); + obj1.setOverlay(true); + QCOMPARE(true, obj1.hasOverlay()); + QVERIFY(obj1.testOption(QGL::HasOverlay)); + QVERIFY(!obj1.testOption(QGL::NoOverlay)); // int QGLFormat::plane() // void QGLFormat::setPlane(int) + QCOMPARE(0, obj1.plane()); obj1.setPlane(0); QCOMPARE(0, obj1.plane()); - obj1.setPlane(INT_MIN); - QCOMPARE(INT_MIN, obj1.plane()); - obj1.setPlane(INT_MAX); - QCOMPARE(INT_MAX, obj1.plane()); + obj1.setPlane(TEST_INT_MIN); + QCOMPARE(TEST_INT_MIN, obj1.plane()); + obj1.setPlane(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.plane()); MyGLContext obj2(obj1); // bool QGLContext::windowCreated() @@ -452,5 +625,124 @@ void tst_QGL::partialGLWidgetUpdates() QCOMPARE(widget.paintEventRegion, QRegion(widget.rect())); } +class ColormapExtended : public QGLColormap +{ +public: + ColormapExtended() {} + + Qt::HANDLE handle() { return QGLColormap::handle(); } + void setHandle(Qt::HANDLE handle) { QGLColormap::setHandle(handle); } +}; + +void tst_QGL::colormap() +{ + // Check the properties of the default empty colormap. + QGLColormap cmap1; + QVERIFY(cmap1.isEmpty()); + QCOMPARE(cmap1.size(), 0); + QVERIFY(cmap1.entryRgb(0) == 0); + QVERIFY(cmap1.entryRgb(-1) == 0); + QVERIFY(cmap1.entryRgb(100) == 0); + QVERIFY(!cmap1.entryColor(0).isValid()); + QVERIFY(!cmap1.entryColor(-1).isValid()); + QVERIFY(!cmap1.entryColor(100).isValid()); + QCOMPARE(cmap1.find(qRgb(255, 0, 0)), -1); + QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), -1); + + // Set an entry and re-test. + cmap1.setEntry(56, qRgb(255, 0, 0)); + // The colormap is still considered "empty" even though it + // has entries in it now. The isEmpty() method is used to + // detect when the colormap is in use by a GL widget, + // not to detect when it is empty! + QVERIFY(cmap1.isEmpty()); + QCOMPARE(cmap1.size(), 256); + QVERIFY(cmap1.entryRgb(0) == 0); + QVERIFY(cmap1.entryColor(0) == QColor(0, 0, 0, 255)); + QVERIFY(cmap1.entryRgb(56) == qRgb(255, 0, 0)); + QVERIFY(cmap1.entryColor(56) == QColor(255, 0, 0, 255)); + QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56); + QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56); + + // Set some more entries. + static QRgb const colors[] = { + qRgb(255, 0, 0), + qRgb(0, 255, 0), + qRgb(255, 255, 255), + qRgb(0, 0, 255), + qRgb(0, 0, 0) + }; + cmap1.setEntry(57, QColor(0, 255, 0)); + cmap1.setEntries(3, colors + 2, 58); + cmap1.setEntries(5, colors, 251); + int idx; + for (idx = 0; idx < 5; ++idx) { + QVERIFY(cmap1.entryRgb(56 + idx) == colors[idx]); + QVERIFY(cmap1.entryColor(56 + idx) == QColor(colors[idx])); + QVERIFY(cmap1.entryRgb(251 + idx) == colors[idx]); + QVERIFY(cmap1.entryColor(251 + idx) == QColor(colors[idx])); + } + QCOMPARE(cmap1.size(), 256); + + // Perform color lookups. + QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56); + QCOMPARE(cmap1.find(qRgb(0, 0, 0)), 60); // Actually finds 0, 0, 0, 255. + QCOMPARE(cmap1.find(qRgba(0, 0, 0, 0)), 0); + QCOMPARE(cmap1.find(qRgb(0, 255, 0)), 57); + QCOMPARE(cmap1.find(qRgb(255, 255, 255)), 58); + QCOMPARE(cmap1.find(qRgb(0, 0, 255)), 59); + QCOMPARE(cmap1.find(qRgb(140, 0, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 140, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 0, 140)), -1); + QCOMPARE(cmap1.find(qRgb(64, 0, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 64, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 0, 64)), -1); + QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 0)), 60); + QCOMPARE(cmap1.findNearest(qRgba(0, 0, 0, 0)), 0); + QCOMPARE(cmap1.findNearest(qRgb(0, 255, 0)), 57); + QCOMPARE(cmap1.findNearest(qRgb(255, 255, 255)), 58); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 255)), 59); + QCOMPARE(cmap1.findNearest(qRgb(140, 0, 0)), 56); + QCOMPARE(cmap1.findNearest(qRgb(0, 140, 0)), 57); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 140)), 59); + QCOMPARE(cmap1.findNearest(qRgb(64, 0, 0)), 0); + QCOMPARE(cmap1.findNearest(qRgb(0, 64, 0)), 0); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 64)), 0); + + // Make some copies of the colormap and check that they are the same. + QGLColormap cmap2(cmap1); + QGLColormap cmap3; + cmap3 = cmap1; + QVERIFY(cmap2.isEmpty()); + QVERIFY(cmap3.isEmpty()); + QCOMPARE(cmap2.size(), 256); + QCOMPARE(cmap3.size(), 256); + for (idx = 0; idx < 256; ++idx) { + QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx)); + QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx)); + } + + // Modify an entry in one of the copies and recheck the original. + cmap2.setEntry(45, qRgb(255, 0, 0)); + for (idx = 0; idx < 256; ++idx) { + if (idx != 45) + QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx)); + else + QCOMPARE(cmap2.entryRgb(45), qRgb(255, 0, 0)); + QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx)); + } + + // Check that setting the handle will cause isEmpty() to work right. + ColormapExtended cmap4; + cmap4.setEntry(56, qRgb(255, 0, 0)); + QVERIFY(cmap4.isEmpty()); + QCOMPARE(cmap4.size(), 256); + cmap4.setHandle(Qt::HANDLE(42)); + QVERIFY(cmap4.handle() == Qt::HANDLE(42)); + QVERIFY(!cmap4.isEmpty()); + QCOMPARE(cmap4.size(), 256); +} + QTEST_MAIN(tst_QGL) #include "tst_qgl.moc" diff --git a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp index 7f6f322..789bb27 100644 --- a/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/auto/qgraphicsitem/tst_qgraphicsitem.cpp @@ -169,6 +169,7 @@ private slots: void setParentItem(); void children(); void flags(); + void inputMethodHints(); void toolTip(); void visible(); void explicitlyVisible(); @@ -760,6 +761,34 @@ void tst_QGraphicsItem::flags() QApplication::sendEvent(&scene, &event5); QCOMPARE(item->pos(), QPointF(10, 10)); } + { + QGraphicsItem* clippingParent = new QGraphicsRectItem; + clippingParent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); + + QGraphicsItem* nonClippingParent = new QGraphicsRectItem; + nonClippingParent->setFlag(QGraphicsItem::ItemClipsChildrenToShape, false); + + QGraphicsItem* child = new QGraphicsRectItem(nonClippingParent); + QVERIFY(!child->isClipped()); + + child->setParentItem(clippingParent); + QVERIFY(child->isClipped()); + + child->setParentItem(nonClippingParent); + QVERIFY(!child->isClipped()); + } +} + +class ImhTester : public QGraphicsItem +{ + QRectF boundingRect() const { return QRectF(); } + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) {} +}; + +void tst_QGraphicsItem::inputMethodHints() +{ + ImhTester item; + QCOMPARE(item.inputMethodHints(), Qt::ImhNone); } void tst_QGraphicsItem::toolTip() diff --git a/tests/auto/qguard/qguard.pro b/tests/auto/qguard/qguard.pro new file mode 100644 index 0000000..f249dde --- /dev/null +++ b/tests/auto/qguard/qguard.pro @@ -0,0 +1,2 @@ +load(qttest_p4) +SOURCES += tst_qguard.cpp diff --git a/tests/auto/qguard/tst_qguard.cpp b/tests/auto/qguard/tst_qguard.cpp new file mode 100644 index 0000000..96d1b60 --- /dev/null +++ b/tests/auto/qguard/tst_qguard.cpp @@ -0,0 +1,350 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// NOTE: This is identical to the QPointer autotest + +#include <QtTest/QtTest> + +#include <QApplication> +#include <QDebug> +#include <QWidget> +#include <private/qguard_p.h> + +class tst_QGuard : public QObject +{ + Q_OBJECT +public: + tst_QGuard(); + ~tst_QGuard(); + + inline tst_QGuard *me() const + { return const_cast<tst_QGuard *>(this); } + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); +private slots: + void constructors(); + void destructor(); + void assignment_operators(); + void equality_operators(); + void isNull(); + void dereference_operators(); + void disconnect(); + void castDuringDestruction(); + void data() const; + void dataSignature() const; +}; + +tst_QGuard::tst_QGuard() +{ } + +tst_QGuard::~tst_QGuard() +{ } + +void tst_QGuard::initTestCase() +{ } + +void tst_QGuard::cleanupTestCase() +{ } + +void tst_QGuard::init() +{ } + +void tst_QGuard::cleanup() +{ } + +void tst_QGuard::constructors() +{ + QGuard<QObject> p1; + QGuard<QObject> p2(this); + QGuard<QObject> p3(p2); + QCOMPARE(p1, QGuard<QObject>(0)); + QCOMPARE(p2, QGuard<QObject>(this)); + QCOMPARE(p3, QGuard<QObject>(this)); +} + +void tst_QGuard::destructor() +{ + QObject *object = new QObject; + QGuard<QObject> p = object; + QCOMPARE(p, QGuard<QObject>(object)); + delete object; + QCOMPARE(p, QGuard<QObject>(0)); +} + +void tst_QGuard::assignment_operators() +{ + QGuard<QObject> p1; + QGuard<QObject> p2; + + p1 = this; + p2 = p1; + + QCOMPARE(p1, QGuard<QObject>(this)); + QCOMPARE(p2, QGuard<QObject>(this)); + QCOMPARE(p1, QGuard<QObject>(p2)); + + p1 = 0; + p2 = p1; + QCOMPARE(p1, QGuard<QObject>(0)); + QCOMPARE(p2, QGuard<QObject>(0)); + QCOMPARE(p1, QGuard<QObject>(p2)); + + QObject *object = new QObject; + + p1 = object; + p2 = p1; + QCOMPARE(p1, QGuard<QObject>(object)); + QCOMPARE(p2, QGuard<QObject>(object)); + QCOMPARE(p1, QGuard<QObject>(p2)); + + delete object; + QCOMPARE(p1, QGuard<QObject>(0)); + QCOMPARE(p2, QGuard<QObject>(0)); + QCOMPARE(p1, QGuard<QObject>(p2)); +} + +void tst_QGuard::equality_operators() +{ + QGuard<QObject> p1; + QGuard<QObject> p2; + + QVERIFY(p1 == p2); + + QObject *object = 0; + QWidget *widget = 0; + + p1 = object; + QVERIFY(p1 == p2); + QVERIFY(p1 == object); + p2 = object; + QVERIFY(p2 == p1); + QVERIFY(p2 == object); + + p1 = this; + QVERIFY(p1 != p2); + p2 = p1; + QVERIFY(p1 == p2); + + // compare to zero + p1 = 0; + QVERIFY(p1 == 0); + QVERIFY(0 == p1); + QVERIFY(p2 != 0); + QVERIFY(0 != p2); + QVERIFY(p1 == object); + QVERIFY(object == p1); + QVERIFY(p2 != object); + QVERIFY(object != p2); + QVERIFY(p1 == widget); + QVERIFY(widget == p1); + QVERIFY(p2 != widget); + QVERIFY(widget != p2); +} + +void tst_QGuard::isNull() +{ + QGuard<QObject> p1; + QVERIFY(p1.isNull()); + p1 = this; + QVERIFY(!p1.isNull()); + p1 = 0; + QVERIFY(p1.isNull()); +} + +void tst_QGuard::dereference_operators() +{ + QGuard<tst_QGuard> p1 = this; + + QObject *object = p1->me(); + QVERIFY(object == this); + + QObject &ref = *p1; + QVERIFY(&ref == this); + + object = static_cast<QObject *>(p1); + QVERIFY(object == this); +} + +void tst_QGuard::disconnect() +{ + QGuard<QObject> p1 = new QObject; + QVERIFY(!p1.isNull()); + p1->disconnect(); + QVERIFY(!p1.isNull()); + delete static_cast<QObject *>(p1); + QVERIFY(p1.isNull()); +} + +class ChildObject : public QObject +{ + QGuard<QObject> guardedPointer; + +public: + ChildObject(QObject *parent) + : QObject(parent), guardedPointer(parent) + { } + ~ChildObject(); +}; + +ChildObject::~ChildObject() +{ + QCOMPARE(static_cast<QObject *>(guardedPointer), static_cast<QObject *>(0)); + QCOMPARE(qobject_cast<QObject *>(guardedPointer), static_cast<QObject *>(0)); +} + +class ChildWidget : public QWidget +{ + QGuard<QWidget> guardedPointer; + +public: + ChildWidget(QWidget *parent) + : QWidget(parent), guardedPointer(parent) + { } + ~ChildWidget(); +}; + +ChildWidget::~ChildWidget() +{ + QCOMPARE(static_cast<QWidget *>(guardedPointer), static_cast<QWidget *>(0)); + QCOMPARE(qobject_cast<QWidget *>(guardedPointer), static_cast<QWidget *>(0)); +} + +class DerivedChild; + +class DerivedParent : public QObject +{ + Q_OBJECT + + DerivedChild *derivedChild; + +public: + DerivedParent(); + ~DerivedParent(); +}; + +class DerivedChild : public QObject +{ + Q_OBJECT + + DerivedParent *parentPointer; + QGuard<DerivedParent> guardedParentPointer; + +public: + DerivedChild(DerivedParent *parent) + : QObject(parent), parentPointer(parent), guardedParentPointer(parent) + { } + ~DerivedChild(); +}; + +DerivedParent::DerivedParent() + : QObject() +{ + derivedChild = new DerivedChild(this); +} + +DerivedParent::~DerivedParent() +{ + delete derivedChild; +} + +DerivedChild::~DerivedChild() +{ + QCOMPARE(static_cast<DerivedParent *>(guardedParentPointer), parentPointer); + QCOMPARE(qobject_cast<DerivedParent *>(guardedParentPointer), parentPointer); +} + +void tst_QGuard::castDuringDestruction() +{ + { + QObject *parentObject = new QObject(); + (void) new ChildObject(parentObject); + delete parentObject; + } + + { + QWidget *parentWidget = new QWidget(); + (void) new ChildWidget(parentWidget); + delete parentWidget; + } + + { + delete new DerivedParent(); + } +} + +void tst_QGuard::data() const +{ + /* Check value of a default constructed object. */ + { + QGuard<QObject> p; + QCOMPARE(p.data(), static_cast<QObject *>(0)); + } + + /* Check value of a default constructed object. */ + { + QObject *const object = new QObject(); + QGuard<QObject> p(object); + QCOMPARE(p.data(), object); + } +} + +void tst_QGuard::dataSignature() const +{ + /* data() should be const. */ + { + const QGuard<QObject> p; + p.data(); + } + + /* The return type should be T. */ + { + const QGuard<QWidget> p; + /* If the types differs, the QCOMPARE will fail to instansiate. */ + QCOMPARE(p.data(), static_cast<QWidget *>(0)); + } +} + +QTEST_MAIN(tst_QGuard) +#include "tst_qguard.moc" diff --git a/tests/auto/qhelpenginecore/data/collection.qhc b/tests/auto/qhelpenginecore/data/collection.qhc Binary files differindex 6fb8abb..bd2f37c 100644 --- a/tests/auto/qhelpenginecore/data/collection.qhc +++ b/tests/auto/qhelpenginecore/data/collection.qhc diff --git a/tests/auto/qhelpenginecore/data/collection1.qhc b/tests/auto/qhelpenginecore/data/collection1.qhc Binary files differindex 45acf33..de310ea 100644 --- a/tests/auto/qhelpenginecore/data/collection1.qhc +++ b/tests/auto/qhelpenginecore/data/collection1.qhc diff --git a/tests/auto/qhelpenginecore/data/linguist-3.3.8.qch b/tests/auto/qhelpenginecore/data/linguist-3.3.8.qch Binary files differindex 0b53cd8..ed9a89c 100644 --- a/tests/auto/qhelpenginecore/data/linguist-3.3.8.qch +++ b/tests/auto/qhelpenginecore/data/linguist-3.3.8.qch diff --git a/tests/auto/qhelpenginecore/data/qmake-3.3.8.qch b/tests/auto/qhelpenginecore/data/qmake-3.3.8.qch Binary files differindex a3ca18a..0e95c14 100644 --- a/tests/auto/qhelpenginecore/data/qmake-3.3.8.qch +++ b/tests/auto/qhelpenginecore/data/qmake-3.3.8.qch diff --git a/tests/auto/qhelpenginecore/data/qmake-4.3.0.qch b/tests/auto/qhelpenginecore/data/qmake-4.3.0.qch Binary files differindex 8f76134..337d7a1 100644 --- a/tests/auto/qhelpenginecore/data/qmake-4.3.0.qch +++ b/tests/auto/qhelpenginecore/data/qmake-4.3.0.qch diff --git a/tests/auto/qhelpenginecore/data/test.qch b/tests/auto/qhelpenginecore/data/test.qch Binary files differindex 8a8f7a1..06b789a 100644 --- a/tests/auto/qhelpenginecore/data/test.qch +++ b/tests/auto/qhelpenginecore/data/test.qch diff --git a/tests/auto/qhelpenginecore/tst_qhelpenginecore.cpp b/tests/auto/qhelpenginecore/tst_qhelpenginecore.cpp index d62c8e5..f9aa653 100644 --- a/tests/auto/qhelpenginecore/tst_qhelpenginecore.cpp +++ b/tests/auto/qhelpenginecore/tst_qhelpenginecore.cpp @@ -130,7 +130,7 @@ void tst_QHelpEngineCore::setCollectionFile() QStringList docs = help.registeredDocumentations(); QCOMPARE(docs.count(), 1); - QCOMPARE(docs.first(), QLatin1String("trolltech.com.1_0_0.test")); + QCOMPARE(docs.first(), QLatin1String("trolltech.com.1-0-0.test")); } void tst_QHelpEngineCore::copyCollectionFile() @@ -177,7 +177,7 @@ void tst_QHelpEngineCore::copyCollectionFile() i = 0; while (m_query->next()) { if (i == 0) { - QCOMPARE(m_query->value(0).toString(), QString("trolltech.com.3_3_8.qmake")); + QCOMPARE(m_query->value(0).toString(), QString("trolltech.com.3-3-8.qmake")); QCOMPARE(m_query->value(1).toString(), QString("data/qmake-3.3.8.qch")); } ++i; @@ -193,9 +193,9 @@ void tst_QHelpEngineCore::copyCollectionFile() void tst_QHelpEngineCore::namespaceName() { QCOMPARE(QHelpEngineCore::namespaceName(m_path + "/data/qmake-3.3.8.qch"), - QString("trolltech.com.3_3_8.qmake")); + QString("trolltech.com.3-3-8.qmake")); QCOMPARE(QHelpEngineCore::namespaceName(m_path + "/data/linguist-3.3.8.qch"), - QString("trolltech.com.3_3_8.linguist")); + QString("trolltech.com.3-3-8.linguist")); } void tst_QHelpEngineCore::registeredDocumentations() @@ -205,8 +205,8 @@ void tst_QHelpEngineCore::registeredDocumentations() QStringList docs = help.registeredDocumentations(); QCOMPARE(docs.count(), 3); QStringList lst; - lst << "trolltech.com.3_3_8.qmake" << "trolltech.com.4_3_0.qmake" - << "trolltech.com.1_0_0.test"; + lst << "trolltech.com.3-3-8.qmake" << "trolltech.com.4-3-0.qmake" + << "trolltech.com.1-0-0.test"; foreach (QString s, docs) lst.removeAll(s); QCOMPARE(lst.isEmpty(), true); @@ -236,7 +236,7 @@ void tst_QHelpEngineCore::registerDocumentation() } QSqlQuery query(db); query.exec("SELECT FilePath FROM NamespaceTable WHERE " - "Name=\'trolltech.com.3_3_8.linguist\'"); + "Name=\'trolltech.com.3-3-8.linguist\'"); if (query.next()) QCOMPARE(query.value(0).toString(), QString("linguist-3.3.8.qch")); @@ -251,7 +251,7 @@ void tst_QHelpEngineCore::unregisterDocumentation() QHelpEngineCore c(m_colFile); QCOMPARE(c.setupData(), true); QCOMPARE(c.registeredDocumentations().count(), 3); - c.unregisterDocumentation("trolltech.com.3_3_8.qmake"); + c.unregisterDocumentation("trolltech.com.3-3-8.qmake"); QCOMPARE(c.registeredDocumentations().count(), 2); QCOMPARE(c.unregisterDocumentation("noexisting"), false); } @@ -260,9 +260,9 @@ void tst_QHelpEngineCore::documentationFileName() { QHelpEngineCore c(m_colFile); QCOMPARE(c.setupData(), true); - QCOMPARE(c.documentationFileName(QLatin1String("trolltech.com.3_3_8.qmake")), + QCOMPARE(c.documentationFileName(QLatin1String("trolltech.com.3-3-8.qmake")), QString(m_path + "/data/qmake-3.3.8.qch")); - QCOMPARE(c.documentationFileName(QLatin1String("trolltech.com.1_0_0.test")), + QCOMPARE(c.documentationFileName(QLatin1String("trolltech.com.1-0-0.test")), QString(m_path + "/data/test.qch")); QCOMPARE(c.documentationFileName(QLatin1String("trolltech.com.empty")), QString()); @@ -337,7 +337,7 @@ void tst_QHelpEngineCore::filterAttributeSets() { QHelpEngineCore help(m_colFile, 0); QCOMPARE(help.setupData(), true); - QList<QStringList> lst = help.filterAttributeSets("trolltech.com.1_0_0.test"); + QList<QStringList> lst = help.filterAttributeSets("trolltech.com.1-0-0.test"); QCOMPARE(lst.count(), 2); QCOMPARE(lst.first().count(), 2); QCOMPARE((bool)lst.first().contains("filter1"), true); @@ -348,22 +348,22 @@ void tst_QHelpEngineCore::files() { QHelpEngineCore help(m_colFile, 0); QCOMPARE(help.setupData(), true); - QList<QUrl> lst = help.files("trolltech.com.4_3_0.qmake", + QList<QUrl> lst = help.files("trolltech.com.4-3-0.qmake", QStringList()); QCOMPARE(lst.count(), 16); - lst = help.files("trolltech.com.4_3_0.qmake", + lst = help.files("trolltech.com.4-3-0.qmake", QStringList(), "png"); QCOMPARE(lst.count(), 2); - lst = help.files("trolltech.com.4_3_0.qmake", + lst = help.files("trolltech.com.4-3-0.qmake", QStringList() << "qt", "html"); QCOMPARE(lst.count(), 13); - lst = help.files("trolltech.com.4_3_0.qmake", + lst = help.files("trolltech.com.4-3-0.qmake", QStringList() << "qt" << "qmake", "html"); QCOMPARE(lst.count(), 13); - lst = help.files("trolltech.com.4_3_0.qmake", + lst = help.files("trolltech.com.4-3-0.qmake", QStringList() << "qt" << "qmake" << "bla", "html"); QCOMPARE(lst.count(), 0); - lst = help.files("trolltech.com.4_3_0.qmake", + lst = help.files("trolltech.com.4-3-0.qmake", QStringList() << "qt" << "qmake", "foo"); QCOMPARE(lst.count(), 0); @@ -378,7 +378,7 @@ void tst_QHelpEngineCore::fileData() QCOMPARE(help.setupData(), true); QByteArray ba = help.fileData(QUrl("NotExisting")); QCOMPARE(ba.size(), 0); - ba = help.fileData(QUrl("qthelp://trolltech.com.1_0_0.test/testFolder/test.html")); + ba = help.fileData(QUrl("qthelp://trolltech.com.1-0-0.test/testFolder/test.html")); QTextStream s(ba, QIODevice::ReadOnly|QIODevice::Text); QFile f(m_path + "/data/test.html"); if (!f.open(QIODevice::ReadOnly|QIODevice::Text)) @@ -396,7 +396,7 @@ void tst_QHelpEngineCore::linksForIdentifier() QCOMPARE(map.contains("Test Manual"), true); QCOMPARE(map.count(), 1); QCOMPARE(map.value("Test Manual"), - QUrl("qthelp://trolltech.com.1_0_0.test/testFolder/test.html#foo")); + QUrl("qthelp://trolltech.com.1-0-0.test/testFolder/test.html#foo")); help.setCurrentFilter("Custom Filter 2"); map = help.linksForIdentifier("People::newton"); @@ -405,7 +405,7 @@ void tst_QHelpEngineCore::linksForIdentifier() QCOMPARE(map.contains("Fancy"), true); QCOMPARE(map.count(), 1); QCOMPARE(map.value("Fancy"), - QUrl("qthelp://trolltech.com.1_0_0.test/testFolder/fancy.html#foobar")); + QUrl("qthelp://trolltech.com.1-0-0.test/testFolder/fancy.html#foobar")); } void tst_QHelpEngineCore::customValue() diff --git a/tests/auto/qhelpindexmodel/data/collection.qhc b/tests/auto/qhelpindexmodel/data/collection.qhc Binary files differindex 6fb8abb..bd2f37c 100644 --- a/tests/auto/qhelpindexmodel/data/collection.qhc +++ b/tests/auto/qhelpindexmodel/data/collection.qhc diff --git a/tests/auto/qhelpindexmodel/data/collection1.qhc b/tests/auto/qhelpindexmodel/data/collection1.qhc Binary files differindex 45acf33..de310ea 100644 --- a/tests/auto/qhelpindexmodel/data/collection1.qhc +++ b/tests/auto/qhelpindexmodel/data/collection1.qhc diff --git a/tests/auto/qhelpindexmodel/data/linguist-3.3.8.qch b/tests/auto/qhelpindexmodel/data/linguist-3.3.8.qch Binary files differindex 0b53cd8..ed9a89c 100644 --- a/tests/auto/qhelpindexmodel/data/linguist-3.3.8.qch +++ b/tests/auto/qhelpindexmodel/data/linguist-3.3.8.qch diff --git a/tests/auto/qhelpindexmodel/data/qmake-3.3.8.qch b/tests/auto/qhelpindexmodel/data/qmake-3.3.8.qch Binary files differindex a3ca18a..0e95c14 100644 --- a/tests/auto/qhelpindexmodel/data/qmake-3.3.8.qch +++ b/tests/auto/qhelpindexmodel/data/qmake-3.3.8.qch diff --git a/tests/auto/qhelpindexmodel/data/qmake-4.3.0.qch b/tests/auto/qhelpindexmodel/data/qmake-4.3.0.qch Binary files differindex 8f76134..337d7a1 100644 --- a/tests/auto/qhelpindexmodel/data/qmake-4.3.0.qch +++ b/tests/auto/qhelpindexmodel/data/qmake-4.3.0.qch diff --git a/tests/auto/qhelpindexmodel/data/test.qch b/tests/auto/qhelpindexmodel/data/test.qch Binary files differindex 1d6c1a8..4ea9847 100644 --- a/tests/auto/qhelpindexmodel/data/test.qch +++ b/tests/auto/qhelpindexmodel/data/test.qch diff --git a/tests/auto/qhelpindexmodel/tst_qhelpindexmodel.cpp b/tests/auto/qhelpindexmodel/tst_qhelpindexmodel.cpp index 9482def..3adb894 100644 --- a/tests/auto/qhelpindexmodel/tst_qhelpindexmodel.cpp +++ b/tests/auto/qhelpindexmodel/tst_qhelpindexmodel.cpp @@ -185,11 +185,11 @@ void tst_QHelpIndexModel::linksForIndex() QCOMPARE(map.count(), 2); QCOMPARE(map.contains("Test Manual"), true); QCOMPARE(map.value("Test Manual"), - QUrl("qthelp://trolltech.com.1_0_0.test/testFolder/test.html#foo")); + QUrl("qthelp://trolltech.com.1-0-0.test/testFolder/test.html#foo")); QCOMPARE(map.contains("Fancy"), true); QCOMPARE(map.value("Fancy"), - QUrl("qthelp://trolltech.com.1_0_0.test/testFolder/fancy.html#foo")); + QUrl("qthelp://trolltech.com.1-0-0.test/testFolder/fancy.html#foo")); map = m->linksForKeyword("foobar"); QCOMPARE(map.count(), 1); @@ -208,7 +208,7 @@ void tst_QHelpIndexModel::linksForIndex() QCOMPARE(map.count(), 1); QCOMPARE(map.contains("Test Manual"), true); QCOMPARE(map.value("Test Manual"), - QUrl("qthelp://trolltech.com.1_0_0.test/testFolder/test.html#foo")); + QUrl("qthelp://trolltech.com.1-0-0.test/testFolder/test.html#foo")); } QTEST_MAIN(tst_QHelpIndexModel) diff --git a/tests/auto/qitemdelegate/tst_qitemdelegate.cpp b/tests/auto/qitemdelegate/tst_qitemdelegate.cpp index 65dd86f..6714de3 100644 --- a/tests/auto/qitemdelegate/tst_qitemdelegate.cpp +++ b/tests/auto/qitemdelegate/tst_qitemdelegate.cpp @@ -61,6 +61,7 @@ #include <QAbstractItemDelegate> #include <QTextEdit> #include <QPlainTextEdit> +#include <QDialog> Q_DECLARE_METATYPE(QAbstractItemDelegate::EndEditHint) @@ -230,6 +231,8 @@ private slots: void editorEvent(); void enterKey_data(); void enterKey(); + + void task257859_finalizeEdit(); }; @@ -1124,6 +1127,36 @@ void tst_QItemDelegate::enterKey() QCOMPARE(editor && editor->hasFocus(), expectedFocus); } +void tst_QItemDelegate::task257859_finalizeEdit() +{ + QStandardItemModel model; + model.appendRow(new QStandardItem()); + + QListView view; + view.setModel(&model); + view.show(); + QApplication::setActiveWindow(&view); + view.setFocus(); + QTest::qWait(30); + + QModelIndex index = model.index(0, 0); + view.edit(index); + QTest::qWait(30); + + QList<QWidget*> lineEditors = qFindChildren<QWidget *>(view.viewport()); + QCOMPARE(lineEditors.count(), 1); + + QPointer<QWidget> editor = lineEditors.at(0); + QCOMPARE(editor->hasFocus(), true); + + QDialog dialog; + QTimer::singleShot(100, &dialog, SLOT(close())); + dialog.exec(); + + QTest::qWait(10); + + QVERIFY(!editor); +} // ### _not_ covered: diff --git a/tests/auto/qitemselectionmodel/tst_qitemselectionmodel.cpp b/tests/auto/qitemselectionmodel/tst_qitemselectionmodel.cpp index ae64e51..0541b46 100644 --- a/tests/auto/qitemselectionmodel/tst_qitemselectionmodel.cpp +++ b/tests/auto/qitemselectionmodel/tst_qitemselectionmodel.cpp @@ -1547,7 +1547,7 @@ void tst_QItemSelectionModel::resetModel() model.reset(); QVERIFY(view.selectionModel()->selection().isEmpty()); - QVERIFY(view.selectionModel()->hasSelection()); + QVERIFY(view.selectionModel()->hasSelection() == false); view.selectionModel()->select(QItemSelection(model.index(0, 0), model.index(5, 5)), QItemSelectionModel::Select); diff --git a/tests/auto/qlocale/tst_qlocale.cpp b/tests/auto/qlocale/tst_qlocale.cpp index 9ef7f1d..9a5c549 100644 --- a/tests/auto/qlocale/tst_qlocale.cpp +++ b/tests/auto/qlocale/tst_qlocale.cpp @@ -327,9 +327,8 @@ void tst_QLocale::emptyCtor() { \ /* Test constructor without arguments. Needs separate process */ \ /* because of caching of the system locale. */ \ - QString oldEnv = QString::fromLocal8Bit(qgetenv("LANG")); \ - qputenv("LANG", QString(req_lc).toLocal8Bit()); \ QProcess process; \ + process.setEnvironment(QStringList(env) << QString("LANG=%1").arg(req_lc)); \ process.start("syslocaleapp/syslocaleapp"); \ process.waitForReadyRead(); \ QString ret = QString(process.readAll()); \ @@ -337,18 +336,23 @@ void tst_QLocale::emptyCtor() QVERIFY2(!ret.isEmpty(), "Cannot launch external process"); \ QVERIFY2(QString(exp_str) == ret, QString("Expected: " + QString(exp_str) + ", got: " \ + ret + ". Requested: " + QString(req_lc)).toLatin1().constData()); \ - qputenv("LANG", oldEnv.toLocal8Bit()); \ + } + + // Get an environment free of any locale-related variables + QStringList env; + foreach (QString const& entry, QProcess::systemEnvironment()) { + if (entry.startsWith("LANG=") || entry.startsWith("LC_")) + continue; + env << entry; } // Get default locale. - QString old = QString::fromLocal8Bit(qgetenv("LANG")); - qputenv("LANG", ""); QProcess p; + p.setEnvironment(env); p.start("syslocaleapp/syslocaleapp"); p.waitForReadyRead(); QString defaultLoc = QString(p.readAll()); p.waitForFinished(); - qputenv("LANG", old.toLocal8Bit()); TEST_CTOR("C", "C") TEST_CTOR("bla", "C") diff --git a/tests/auto/qmake/testdata/simple_app_build/README b/tests/auto/qmake/testdata/simple_app_build/README new file mode 100644 index 0000000..81dc596 --- /dev/null +++ b/tests/auto/qmake/testdata/simple_app_build/README @@ -0,0 +1 @@ +Placeholder file to ensure this directory exists diff --git a/tests/auto/qmenu/tst_qmenu.cpp b/tests/auto/qmenu/tst_qmenu.cpp index 1d19ffa..6ba6466 100644 --- a/tests/auto/qmenu/tst_qmenu.cpp +++ b/tests/auto/qmenu/tst_qmenu.cpp @@ -95,6 +95,8 @@ private slots: void task250673_activeMultiColumnSubMenuPosition(); void task256918_setFont(); void menuSizeHint(); + void task258920_mouseBorder(); + void setFixedWidth(); protected slots: void onActivated(QAction*); void onHighlighted(QAction*); @@ -763,5 +765,51 @@ void tst_QMenu::menuSizeHint() QCOMPARE(resSize, menu.sizeHint()); } +class Menu258920 : public QMenu +{ + Q_OBJECT +public slots: + void paintEvent(QPaintEvent *e) + { + QMenu::paintEvent(e); + painted = true; + } + +public: + bool painted; +}; + +void tst_QMenu::task258920_mouseBorder() +{ + Menu258920 menu; + QAction *action = menu.addAction("test"); + + menu.popup(QPoint()); + QTest::qWait(100); + QRect actionRect = menu.actionGeometry(action); + QTest::mouseMove(&menu, actionRect.center()); + QTest::qWait(30); + QTest::mouseMove(&menu, actionRect.center() + QPoint(10, 0)); + QTest::qWait(30); + QCOMPARE(action, menu.activeAction()); + menu.painted = false; + QTest::mouseMove(&menu, QPoint(actionRect.center().x(), actionRect.bottom() + 1)); + QTest::qWait(30); + QCOMPARE(static_cast<QAction*>(0), menu.activeAction()); + QVERIFY(menu.painted); +} + +void tst_QMenu::setFixedWidth() +{ + QMenu menu; + menu.addAction("action"); + menu.setFixedWidth(300); + //the sizehint should reflect the minimumwidth because the action will try to + //get as much space as possible + QCOMPARE(menu.sizeHint().width(), menu.minimumWidth()); +} + + + QTEST_MAIN(tst_QMenu) #include "tst_qmenu.moc" diff --git a/tests/auto/qmetaobject/tst_qmetaobject.cpp b/tests/auto/qmetaobject/tst_qmetaobject.cpp index dea0ffb..ac2858c 100644 --- a/tests/auto/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/qmetaobject/tst_qmetaobject.cpp @@ -108,6 +108,8 @@ class tst_QMetaObject : public QObject Q_PROPERTY(int value6 READ value6 NOTIFY value6Changed) Q_PROPERTY(MyStruct value7 READ value7 WRITE setVal7 NOTIFY value7Changed) Q_PROPERTY(int value8 READ value8 NOTIFY value8Changed) + Q_PROPERTY(int value9 READ value9 CONSTANT) + Q_PROPERTY(int value10 READ value10 FINAL) public: enum EnumType { EnumType1 }; @@ -137,6 +139,10 @@ public: int value8() const { return 1; } + int value9() const { return 1; } + + int value10() const { return 1; } + QList<QVariant> value4; QVariantList value5; @@ -159,6 +165,8 @@ private slots: void customPropertyType(); void checkScope(); void propertyNotify(); + void propertyConstant(); + void propertyFinal(); void stdSet(); void classInfo(); @@ -785,6 +793,32 @@ void tst_QMetaObject::propertyNotify() QCOMPARE(signal.signature(), (const char *)0); } +void tst_QMetaObject::propertyConstant() +{ + const QMetaObject *mo = metaObject(); + + QMetaProperty prop = mo->property(mo->indexOfProperty("value8")); + QVERIFY(prop.isValid()); + QVERIFY(!prop.isConstant()); + + prop = mo->property(mo->indexOfProperty("value9")); + QVERIFY(prop.isValid()); + QVERIFY(prop.isConstant()); +} + +void tst_QMetaObject::propertyFinal() +{ + const QMetaObject *mo = metaObject(); + + QMetaProperty prop = mo->property(mo->indexOfProperty("value10")); + QVERIFY(prop.isValid()); + QVERIFY(prop.isFinal()); + + prop = mo->property(mo->indexOfProperty("value9")); + QVERIFY(prop.isValid()); + QVERIFY(!prop.isFinal()); +} + class ClassInfoTestObjectA : public QObject { Q_OBJECT diff --git a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp index 03b1474..cfd3dd0 100644 --- a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp @@ -223,8 +223,10 @@ private Q_SLOTS: void rateControl(); void downloadPerformance(); void uploadPerformance(); - void httpUploadPerformance(); void performanceControlRate(); + void httpUploadPerformance(); + void httpDownloadPerformance_data(); + void httpDownloadPerformance(); void downloadProgress_data(); void downloadProgress(); @@ -248,8 +250,6 @@ private Q_SLOTS: void authorizationError(); void httpConnectionCount(); - void httpDownloadPerformance_data(); - void httpDownloadPerformance(); #ifndef QT_NO_OPENSSL void ignoreSslErrorsList_data(); @@ -513,10 +513,10 @@ public: QTcpSocket *active = new QTcpSocket(this); active->connectToHost("127.0.0.1", server.serverPort()); - if (!active->waitForConnected(10)) + if (!active->waitForConnected(100)) return false; - if (!server.waitForNewConnection(10)) + if (!server.waitForNewConnection(100)) return false; QTcpSocket *passive = server.nextPendingConnection(); passive->setParent(this); @@ -836,6 +836,92 @@ protected: } }; +class HttpDownloadPerformanceClient : QObject { + Q_OBJECT; + QIODevice *device; + public: + HttpDownloadPerformanceClient (QIODevice *dev) : device(dev){ + connect(dev, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + } + + public slots: + void readyReadSlot() { + device->readAll(); + } + +}; + +class HttpDownloadPerformanceServer : QObject { + Q_OBJECT; + qint64 dataSize; + qint64 dataSent; + QTcpServer server; + QTcpSocket *client; + bool serverSendsContentLength; + bool chunkedEncoding; + +public: + HttpDownloadPerformanceServer (qint64 ds, bool sscl, bool ce) : dataSize(ds), dataSent(0), + client(0), serverSendsContentLength(sscl), chunkedEncoding(ce) { + server.listen(); + connect(&server, SIGNAL(newConnection()), this, SLOT(newConnectionSlot())); + } + + int serverPort() { + return server.serverPort(); + } + +public slots: + + void newConnectionSlot() { + client = server.nextPendingConnection(); + client->setParent(this); + connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot(qint64))); + } + + void readyReadSlot() { + client->readAll(); + client->write("HTTP/1.0 200 OK\n"); + if (serverSendsContentLength) + client->write(QString("Content-Length: " + QString::number(dataSize) + "\n").toAscii()); + if (chunkedEncoding) + client->write(QString("Transfer-Encoding: chunked\n").toAscii()); + client->write("Connection: close\n\n"); + } + + void bytesWrittenSlot(qint64 amount) { + Q_UNUSED(amount); + if (dataSent == dataSize && client) { + // close eventually + + // chunked encoding: we have to send a last "empty" chunk + if (chunkedEncoding) + client->write(QString("0\r\n\r\n").toAscii()); + + client->disconnectFromHost(); + server.close(); + client = 0; + return; + } + + // send data + if (client && client->bytesToWrite() < 100*1024 && dataSent < dataSize) { + qint64 amount = qMin(qint64(16*1024), dataSize - dataSent); + QByteArray data(amount, '@'); + + if (chunkedEncoding) { + client->write(QString(QString("%1").arg(amount,0,16).toUpper() + "\r\n").toAscii()); + client->write(data.constData(), amount); + client->write(QString("\r\n").toAscii()); + } else { + client->write(data.constData(), amount); + } + + dataSent += amount; + } + } +}; tst_QNetworkReply::tst_QNetworkReply() @@ -2642,8 +2728,6 @@ void tst_QNetworkReply::ioPutToFileFromProcess() QCOMPARE(file.size(), qint64(data.size())); QByteArray contents = file.readAll(); QCOMPARE(contents, data); - - delete reply; #endif } @@ -3183,7 +3267,7 @@ void tst_QNetworkReply::uploadPerformance() void tst_QNetworkReply::httpUploadPerformance() { - enum {UploadSize = 1000*1024*1024}; // 1000 MB + enum {UploadSize = 128*1024*1024}; // 128 MB ThreadedDataReaderHttpServer reader; FixedSizeDataGenerator generator(UploadSize); @@ -3205,7 +3289,7 @@ void tst_QNetworkReply::httpUploadPerformance() << ((UploadSize/1024.0)/(elapsed/1000.0)) << " kB/sec"; reader.exit(); - reader.wait(3000); + reader.wait(); } @@ -3233,6 +3317,41 @@ void tst_QNetworkReply::performanceControlRate() << elapsedTime << "ms"; } +void tst_QNetworkReply::httpDownloadPerformance_data() +{ + QTest::addColumn<bool>("serverSendsContentLength"); + QTest::addColumn<bool>("chunkedEncoding"); + + QTest::newRow("Server sends no Content-Length") << false << false; + QTest::newRow("Server sends Content-Length") << true << false; + QTest::newRow("Server uses chunked encoding") << false << true; + +} + +void tst_QNetworkReply::httpDownloadPerformance() +{ + QFETCH(bool, serverSendsContentLength); + QFETCH(bool, chunkedEncoding); + + enum {UploadSize = 128*1024*1024}; // 128 MB + HttpDownloadPerformanceServer server(UploadSize, serverSendsContentLength, chunkedEncoding); + + QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1")); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection); + HttpDownloadPerformanceClient client(reply); + + QTime time; + time.start(); + QTestEventLoop::instance().enterLoop(40); + QVERIFY(!QTestEventLoop::instance().timeout()); + + qint64 elapsed = time.elapsed(); + qDebug() << "tst_QNetworkReply::httpDownloadPerformance" << elapsed << "msec, " + << ((UploadSize/1024.0)/(elapsed/1000.0)) << " kB/sec"; +} + void tst_QNetworkReply::downloadProgress_data() { QTest::addColumn<int>("loopCount"); @@ -3720,130 +3839,6 @@ void tst_QNetworkReply::httpConnectionCount() QCOMPARE(pendingConnectionCount, 6); } -class HttpDownloadPerformanceClient : QObject { - Q_OBJECT; - QIODevice *device; - public: - HttpDownloadPerformanceClient (QIODevice *dev) : device(dev){ - connect(dev, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); - } - - public slots: - void readyReadSlot() { - device->readAll(); - } - -}; - -class HttpDownloadPerformanceServer : QObject { - Q_OBJECT; - qint64 dataSize; - qint64 dataSent; - QTcpServer server; - QTcpSocket *client; - bool serverSendsContentLength; - bool chunkedEncoding; - -public: - HttpDownloadPerformanceServer (qint64 ds, bool sscl, bool ce) : dataSize(ds), dataSent(0), - client(0), serverSendsContentLength(sscl), chunkedEncoding(ce) { - server.listen(); - connect(&server, SIGNAL(newConnection()), this, SLOT(newConnectionSlot())); - } - - int serverPort() { - return server.serverPort(); - } - -public slots: - - void newConnectionSlot() { - client = server.nextPendingConnection(); - client->setParent(this); - connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); - connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot(qint64))); - } - - void readyReadSlot() { - client->readAll(); - client->write("HTTP/1.0 200 OK\n"); - if (serverSendsContentLength) - client->write(QString("Content-Length: " + QString::number(dataSize) + "\n").toAscii()); - if (chunkedEncoding) - client->write(QString("Transfer-Encoding: chunked\n").toAscii()); - client->write("Connection: close\n\n"); - } - - void bytesWrittenSlot(qint64 amount) { - Q_UNUSED(amount); - if (dataSent == dataSize && client) { - // close eventually - - // chunked encoding: we have to send a last "empty" chunk - if (chunkedEncoding) - client->write(QString("0\r\n\r\n").toAscii()); - - client->disconnectFromHost(); - server.close(); - client = 0; - return; - } - - // send data - if (client && client->bytesToWrite() < 100*1024 && dataSent < dataSize) { - qint64 amount = qMin(qint64(16*1024), dataSize - dataSent); - QByteArray data(amount, '@'); - - if (chunkedEncoding) { - client->write(QString(QString("%1").arg(amount,0,16).toUpper() + "\r\n").toAscii()); - client->write(data.constData(), amount); - client->write(QString("\r\n").toAscii()); - } else { - client->write(data.constData(), amount); - } - - dataSent += amount; - } - } -}; - -void tst_QNetworkReply::httpDownloadPerformance_data() -{ - QTest::addColumn<bool>("serverSendsContentLength"); - QTest::addColumn<bool>("chunkedEncoding"); - - QTest::newRow("Server sends no Content-Length") << false << false; - QTest::newRow("Server sends Content-Length") << true << false; - QTest::newRow("Server uses chunked encoding") << false << true; - -} - -void tst_QNetworkReply::httpDownloadPerformance() -{ - QFETCH(bool, serverSendsContentLength); - QFETCH(bool, chunkedEncoding); - - enum {UploadSize = 1000*1024*1024}; // 1000 MB - HttpDownloadPerformanceServer server(UploadSize, serverSendsContentLength, chunkedEncoding); - - QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1")); - QNetworkReply* reply = manager.get(request); - - connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection); - HttpDownloadPerformanceClient client(reply); - - QTime time; - time.start(); - QTestEventLoop::instance().enterLoop(40); - QVERIFY(!QTestEventLoop::instance().timeout()); - - qint64 elapsed = time.elapsed(); - qDebug() << "tst_QNetworkReply::httpDownloadPerformance" << elapsed << "msec, " - << ((UploadSize/1024.0)/(elapsed/1000.0)) << " kB/sec"; - - delete reply; -} - #ifndef QT_NO_OPENSSL void tst_QNetworkReply::ignoreSslErrorsList_data() { @@ -3872,7 +3867,7 @@ void tst_QNetworkReply::ignoreSslErrorsList() { QFETCH(QString, url); QNetworkRequest request(url); - QNetworkReply *reply = manager.get(request); + QNetworkReplyPtr reply = manager.get(request); QFETCH(QList<QSslError>, expectedSslErrors); reply->ignoreSslErrors(expectedSslErrors); @@ -3901,7 +3896,7 @@ void tst_QNetworkReply::ignoreSslErrorsListWithSlot() { QFETCH(QString, url); QNetworkRequest request(url); - QNetworkReply *reply = manager.get(request); + QNetworkReplyPtr reply = manager.get(request); QFETCH(QList<QSslError>, expectedSslErrors); // store the errors to ignore them later in the slot connected below diff --git a/tests/auto/qobject/tst_qobject.cpp b/tests/auto/qobject/tst_qobject.cpp index 4f25af6..3df83d7 100644 --- a/tests/auto/qobject/tst_qobject.cpp +++ b/tests/auto/qobject/tst_qobject.cpp @@ -118,6 +118,7 @@ private slots: void connectToSender(); void qobjectConstCast(); void uniqConnection(); + void interfaceIid(); protected: }; @@ -2887,5 +2888,15 @@ void tst_QObject::uniqConnection() delete r2; } +void tst_QObject::interfaceIid() +{ + QCOMPARE(QByteArray(qobject_interface_iid<Foo::Bleh *>()), + QByteArray(Bleh_iid)); + QCOMPARE(QByteArray(qobject_interface_iid<Foo::Bar *>()), + QByteArray("com.qtest.foobar")); + QCOMPARE(QByteArray(qobject_interface_iid<FooObject *>()), + QByteArray()); +} + QTEST_MAIN(tst_QObject) #include "tst_qobject.moc" diff --git a/tests/auto/qparallelanimationgroup/tst_qparallelanimationgroup.cpp b/tests/auto/qparallelanimationgroup/tst_qparallelanimationgroup.cpp index d7d6b88..16c58b0 100644 --- a/tests/auto/qparallelanimationgroup/tst_qparallelanimationgroup.cpp +++ b/tests/auto/qparallelanimationgroup/tst_qparallelanimationgroup.cpp @@ -74,6 +74,7 @@ private slots: void loopCount_data(); void loopCount(); void autoAdd(); + void pauseResume(); }; tst_QParallelAnimationGroup::tst_QParallelAnimationGroup() @@ -828,5 +829,37 @@ void tst_QParallelAnimationGroup::autoAdd() QCOMPARE(group.duration(), 0); } +void tst_QParallelAnimationGroup::pauseResume() +{ + QParallelAnimationGroup group; + TestAnimation2 *anim = new TestAnimation2(250, &group); // 0, duration = 250; + QSignalSpy spy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QCOMPARE(group.duration(), 250); + group.start(); + QTest::qWait(100); + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QCOMPARE(spy.count(), 1); + spy.clear(); + const int currentTime = group.currentTime(); + QCOMPARE(anim->currentTime(), currentTime); + + group.pause(); + QCOMPARE(group.state(), QAnimationGroup::Paused); + QCOMPARE(group.currentTime(), currentTime); + QCOMPARE(anim->state(), QAnimationGroup::Paused); + QCOMPARE(anim->currentTime(), currentTime); + QCOMPARE(spy.count(), 1); + spy.clear(); + + group.resume(); + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(group.currentTime(), currentTime); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QCOMPARE(anim->currentTime(), currentTime); + QCOMPARE(spy.count(), 1); +} + + QTEST_MAIN(tst_QParallelAnimationGroup) #include "tst_qparallelanimationgroup.moc" diff --git a/tests/auto/qplaintextedit/tst_qplaintextedit.cpp b/tests/auto/qplaintextedit/tst_qplaintextedit.cpp index 40ad539..e8350e3 100644 --- a/tests/auto/qplaintextedit/tst_qplaintextedit.cpp +++ b/tests/auto/qplaintextedit/tst_qplaintextedit.cpp @@ -856,7 +856,12 @@ void tst_QPlainTextEdit::lineWrapModes() ed->setLineWrapMode(QPlainTextEdit::NoWrap); QCOMPARE(lineCount(), 1); ed->setLineWrapMode(QPlainTextEdit::WidgetWidth); - ed->resize(1, 1000); + + // QPlainTextEdit does lazy line layout on resize, only for the visible blocks. + // We thus need to make it wide enough to show something visible. + int minimumWidth = 2 * ed->document()->documentMargin(); + minimumWidth += ed->fontMetrics().width(QLatin1Char('a')); + ed->resize(minimumWidth, 1000); QCOMPARE(lineCount(), 26); ed->setParent(0); delete window; diff --git a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp index 87db0ad..c9a553a 100644 --- a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp +++ b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp @@ -72,6 +72,7 @@ private slots: void scopeChain(); void pushAndPopScope(); void getSetActivationObject(); + void inheritActivationAndThisObject(); void toString(); void calledAsConstructor(); void argumentsObjectInNative(); @@ -731,6 +732,35 @@ void tst_QScriptContext::getSetActivationObject() } } +static QScriptValue myEval(QScriptContext *ctx, QScriptEngine *eng) +{ + QString code = ctx->argument(0).toString(); + ctx->setActivationObject(ctx->parentContext()->activationObject()); + ctx->setThisObject(ctx->parentContext()->thisObject()); + return eng->evaluate(code); +} + +void tst_QScriptContext::inheritActivationAndThisObject() +{ + QScriptEngine eng; + eng.globalObject().setProperty("myEval", eng.newFunction(myEval)); + { + QScriptValue ret = eng.evaluate("var a = 123; myEval('a')"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + { + QScriptValue ret = eng.evaluate("(function() { return myEval('this'); }).call(Number)"); + QVERIFY(ret.isFunction()); + QVERIFY(ret.equals(eng.globalObject().property("Number"))); + } + { + QScriptValue ret = eng.evaluate("(function(a) { return myEval('a'); })(123)"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } +} + static QScriptValue parentContextToString(QScriptContext *ctx, QScriptEngine *) { return ctx->parentContext()->toString(); diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp index 06be924..25e84dc 100644 --- a/tests/auto/qscriptengine/tst_qscriptengine.cpp +++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp @@ -914,6 +914,35 @@ void tst_QScriptEngine::getSetGlobalObject() QVERIFY(eng.globalObject().strictlyEquals(obj)); QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj)); QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj)); + + QVERIFY(!obj.property("foo").isValid()); + eng.evaluate("var foo = 123"); + { + QScriptValue ret = obj.property("foo"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + + QVERIFY(!obj.property("bar").isValid()); + eng.evaluate("bar = 456"); + { + QScriptValue ret = obj.property("bar"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 456); + } + + QVERIFY(!obj.property("baz").isValid()); + eng.evaluate("this['baz'] = 789"); + { + QScriptValue ret = obj.property("baz"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 789); + } + + { + QScriptValue ret = eng.evaluate("(function() { return this; })()"); + QVERIFY(ret.strictlyEquals(obj)); + } } void tst_QScriptEngine::globalObjectProperties() diff --git a/tests/auto/qsequentialanimationgroup/tst_qsequentialanimationgroup.cpp b/tests/auto/qsequentialanimationgroup/tst_qsequentialanimationgroup.cpp index 97ff04b..744ba52 100644 --- a/tests/auto/qsequentialanimationgroup/tst_qsequentialanimationgroup.cpp +++ b/tests/auto/qsequentialanimationgroup/tst_qsequentialanimationgroup.cpp @@ -88,6 +88,7 @@ private slots: void currentAnimationWithZeroDuration(); void insertAnimation(); void clearAnimations(); + void pauseResume(); }; tst_QSequentialAnimationGroup::tst_QSequentialAnimationGroup() @@ -1644,5 +1645,40 @@ void tst_QSequentialAnimationGroup::clearAnimations() QCOMPARE(group.state(), QAbstractAnimation::Running); } +void tst_QSequentialAnimationGroup::pauseResume() +{ + QObject dummy; + dummy.setProperty("foo", 0); + QParallelAnimationGroup group; + QPropertyAnimation *anim = new QPropertyAnimation(&dummy, "foo", &group); + anim->setDuration(250); + anim->setEndValue(250); + QSignalSpy spy(anim, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State))); + QCOMPARE(group.duration(), 250); + group.start(); + QTest::qWait(100); + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QCOMPARE(spy.count(), 1); + spy.clear(); + const int currentTime = group.currentTime(); + QCOMPARE(anim->currentTime(), currentTime); + + group.pause(); + QCOMPARE(group.state(), QAnimationGroup::Paused); + QCOMPARE(group.currentTime(), currentTime); + QCOMPARE(anim->state(), QAnimationGroup::Paused); + QCOMPARE(anim->currentTime(), currentTime); + QCOMPARE(spy.count(), 1); + spy.clear(); + + group.resume(); + QCOMPARE(group.state(), QAnimationGroup::Running); + QCOMPARE(group.currentTime(), currentTime); + QCOMPARE(anim->state(), QAnimationGroup::Running); + QCOMPARE(anim->currentTime(), currentTime); + QCOMPARE(spy.count(), 1); +} + QTEST_MAIN(tst_QSequentialAnimationGroup) #include "tst_qsequentialanimationgroup.moc" diff --git a/tests/auto/qsharedpointer/qsharedpointer.pro b/tests/auto/qsharedpointer/qsharedpointer.pro index 90fde06..1759323 100644 --- a/tests/auto/qsharedpointer/qsharedpointer.pro +++ b/tests/auto/qsharedpointer/qsharedpointer.pro @@ -1,9 +1,14 @@ load(qttest_p4) + SOURCES += tst_qsharedpointer.cpp \ forwarddeclaration.cpp \ - forwarddeclared.cpp + forwarddeclared.cpp \ + wrapper.cpp + +HEADERS += forwarddeclared.h \ + wrapper.h + QT = core DEFINES += SRCDIR=\\\"$$PWD/\\\" -requires(contains(QT_CONFIG,private_tests)) + include(externaltests.pri) -HEADERS += forwarddeclared.h diff --git a/tests/auto/qsharedpointer/tst_qsharedpointer.cpp b/tests/auto/qsharedpointer/tst_qsharedpointer.cpp index ae5155e..5214edb 100644 --- a/tests/auto/qsharedpointer/tst_qsharedpointer.cpp +++ b/tests/auto/qsharedpointer/tst_qsharedpointer.cpp @@ -41,8 +41,19 @@ #define QT_SHAREDPOINTER_TRACK_POINTERS #include "qsharedpointer.h" -#include "externaltests.h" #include <QtTest/QtTest> +#include <QtCore/QThread> +#include <QtCore/QVector> + +#include "externaltests.h" +#include "wrapper.h" + +#include <stdlib.h> +#include <time.h> + +namespace QtSharedPointer { + Q_CORE_EXPORT void internalSafetyCheckCleanCheck(); +} class tst_QSharedPointer: public QObject { @@ -56,6 +67,9 @@ private slots: void memoryManagement(); void downCast(); void upCast(); + void qobjectWeakManagement(); + void noSharedPointerFromWeakQObject(); + void weakQObjectFromSharedPointer(); void objectCast(); void differentPointers(); void virtualBaseDifferentPointers(); @@ -68,10 +82,35 @@ private slots: void constCorrectness(); void customDeleter(); void creating(); + void creatingQObject(); + void mixTrackingPointerCode(); + void threadStressTest_data(); + void threadStressTest(); void validConstructs(); void invalidConstructs_data(); void invalidConstructs(); + +public slots: + void cleanup() { check(); } + +public: + inline void check() + { +#ifdef QT_BUILD_INTERNAL + QtSharedPointer::internalSafetyCheckCleanCheck(); +#endif + } +}; + +template <typename Base> +class RefCountHack: public Base +{ +public: + using Base::d; }; +template<typename Base> static inline +QtSharedPointer::ExternalRefCountData *refCountData(const Base &b) +{ return static_cast<const RefCountHack<Base> *>(&b)->d; } class Data { @@ -156,8 +195,8 @@ void tst_QSharedPointer::basics() QVERIFY(! (ptr == otherData)); QVERIFY(! (otherData == ptr)); } - QVERIFY(!ptr.d || ptr.d->weakref == 1); - QVERIFY(!ptr.d || ptr.d->strongref == 1); + QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref == 1); + QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref == 1); { // create another object: @@ -169,8 +208,8 @@ void tst_QSharedPointer::basics() // otherData is deleted here } - QVERIFY(!ptr.d || ptr.d->weakref == 1); - QVERIFY(!ptr.d || ptr.d->strongref == 1); + QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref == 1); + QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref == 1); { // create a copy: @@ -186,8 +225,8 @@ void tst_QSharedPointer::basics() QCOMPARE(copy.data(), aData); QVERIFY(copy == aData); } - QVERIFY(!ptr.d || ptr.d->weakref == 1); - QVERIFY(!ptr.d || ptr.d->strongref == 1); + QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref == 1); + QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref == 1); { // create a weak reference: @@ -218,8 +257,8 @@ void tst_QSharedPointer::basics() QVERIFY(strong == ptr); QCOMPARE(strong.data(), aData); } - QVERIFY(!ptr.d || ptr.d->weakref == 1); - QVERIFY(!ptr.d || ptr.d->strongref == 1); + QVERIFY(!refCountData(ptr) || refCountData(ptr)->weakref == 1); + QVERIFY(!refCountData(ptr) || refCountData(ptr)->strongref == 1); // aData is deleted here } @@ -435,15 +474,15 @@ void tst_QSharedPointer::upCast() QVERIFY(baseptr == derivedptr); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QWeakPointer<DerivedData> derivedptr = qWeakPointerCast<DerivedData>(baseptr); QVERIFY(baseptr == derivedptr); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QWeakPointer<Data> weakptr = baseptr; @@ -451,16 +490,16 @@ void tst_QSharedPointer::upCast() QVERIFY(baseptr == derivedptr); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QSharedPointer<DerivedData> derivedptr = baseptr.staticCast<DerivedData>(); QVERIFY(baseptr == derivedptr); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); } class OtherObject: public QObject @@ -468,6 +507,122 @@ class OtherObject: public QObject Q_OBJECT }; +void tst_QSharedPointer::qobjectWeakManagement() +{ + { + QObject *obj = new QObject; + QWeakPointer<QObject> weak(obj); + QVERIFY(!weak.isNull()); + QVERIFY(weak.data() == obj); + + // now delete + delete obj; + QVERIFY(weak.isNull()); + } + check(); + + { + // same, bit with operator= + QObject *obj = new QObject; + QWeakPointer<QObject> weak; + weak = obj; + QVERIFY(!weak.isNull()); + QVERIFY(weak.data() == obj); + + // now delete + delete obj; + QVERIFY(weak.isNull()); + } + check(); + + { + // delete triggered by parent + QObject *obj, *parent; + parent = new QObject; + obj = new QObject(parent); + QWeakPointer<QObject> weak(obj); + + // now delete the parent + delete parent; + QVERIFY(weak.isNull()); + } + check(); + + { + // same as above, but set the parent after QWeakPointer is created + QObject *obj, *parent; + obj = new QObject; + QWeakPointer<QObject> weak(obj); + + parent = new QObject; + obj->setParent(parent); + + // now delete the parent + delete parent; + QVERIFY(weak.isNull()); + } + check(); + + { + // with two QWeakPointers + QObject *obj = new QObject; + QWeakPointer<QObject> weak(obj); + + { + QWeakPointer<QObject> weak2(obj); + QVERIFY(!weak2.isNull()); + QVERIFY(weak == weak2); + } + QVERIFY(!weak.isNull()); + + delete obj; + QVERIFY(weak.isNull()); + } + check(); + + { + // same, but delete the pointer while two QWeakPointers exist + QObject *obj = new QObject; + QWeakPointer<QObject> weak(obj); + + { + QWeakPointer<QObject> weak2(obj); + QVERIFY(!weak2.isNull()); + + delete obj; + QVERIFY(weak.isNull()); + QVERIFY(weak2.isNull()); + } + QVERIFY(weak.isNull()); + } + check(); +} + +void tst_QSharedPointer::noSharedPointerFromWeakQObject() +{ + // you're not allowed to create a QSharedPointer from an unmanaged QObject + QObject obj; + QWeakPointer<QObject> weak(&obj); + + QSharedPointer<QObject> strong = weak.toStrongRef(); + QVERIFY(strong.isNull()); + + // is something went wrong, we'll probably crash here +} + +void tst_QSharedPointer::weakQObjectFromSharedPointer() +{ + // this is the inverse of the above: you're allowed to create a QWeakPointer + // from a managed QObject + QSharedPointer<QObject> shared(new QObject); + QWeakPointer<QObject> weak = shared.data(); + QVERIFY(!weak.isNull()); + + // delete: + shared.clear(); + QVERIFY(weak.isNull()); +} + void tst_QSharedPointer::objectCast() { { @@ -496,6 +651,7 @@ void tst_QSharedPointer::objectCast() QVERIFY(ptr == data); #endif } + check(); { const OtherObject *data = new OtherObject; @@ -523,6 +679,7 @@ void tst_QSharedPointer::objectCast() QVERIFY(ptr == data); #endif } + check(); { OtherObject *data = new OtherObject; @@ -564,6 +721,7 @@ void tst_QSharedPointer::objectCast() QVERIFY(otherptr.isNull()); #endif } + check(); } void tst_QSharedPointer::differentPointers() @@ -583,6 +741,7 @@ void tst_QSharedPointer::differentPointers() QVERIFY(baseptr == aData); QVERIFY(baseptr == aBase); } + check(); { DiffPtrDerivedData *aData = new DiffPtrDerivedData; @@ -597,6 +756,7 @@ void tst_QSharedPointer::differentPointers() QVERIFY(ptr == aBase); QVERIFY(baseptr == aData); } + check(); { DiffPtrDerivedData *aData = new DiffPtrDerivedData; @@ -613,6 +773,7 @@ void tst_QSharedPointer::differentPointers() QVERIFY(baseptr == aData); QVERIFY(baseptr == aBase); } + check(); } void tst_QSharedPointer::virtualBaseDifferentPointers() @@ -632,6 +793,7 @@ void tst_QSharedPointer::virtualBaseDifferentPointers() QVERIFY(baseptr == aData); QVERIFY(baseptr == aBase); } + check(); { VirtualDerived *aData = new VirtualDerived; @@ -648,6 +810,7 @@ void tst_QSharedPointer::virtualBaseDifferentPointers() QVERIFY(baseptr == aData); QVERIFY(baseptr == aBase); } + check(); } #ifndef QTEST_NO_RTTI @@ -662,8 +825,8 @@ void tst_QSharedPointer::dynamicCast() QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QWeakPointer<Data> weakptr = baseptr; @@ -672,8 +835,8 @@ void tst_QSharedPointer::dynamicCast() QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QSharedPointer<DerivedData> derivedptr = baseptr.dynamicCast<DerivedData>(); @@ -681,8 +844,8 @@ void tst_QSharedPointer::dynamicCast() QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); } void tst_QSharedPointer::dynamicCastDifferentPointers() @@ -697,8 +860,8 @@ void tst_QSharedPointer::dynamicCastDifferentPointers() QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QWeakPointer<Data> weakptr = baseptr; @@ -707,8 +870,8 @@ void tst_QSharedPointer::dynamicCastDifferentPointers() QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QSharedPointer<DiffPtrDerivedData> derivedptr = baseptr.dynamicCast<DiffPtrDerivedData>(); @@ -716,8 +879,8 @@ void tst_QSharedPointer::dynamicCastDifferentPointers() QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); { Stuffing *nakedptr = dynamic_cast<Stuffing *>(baseptr.data()); @@ -742,8 +905,8 @@ void tst_QSharedPointer::dynamicCastVirtualBase() QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QWeakPointer<Data> weakptr = baseptr; @@ -752,8 +915,8 @@ void tst_QSharedPointer::dynamicCastVirtualBase() QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QSharedPointer<VirtualDerived> derivedptr = baseptr.dynamicCast<VirtualDerived>(); @@ -761,8 +924,8 @@ void tst_QSharedPointer::dynamicCastVirtualBase() QCOMPARE(derivedptr.data(), aData); QCOMPARE(static_cast<Data *>(derivedptr.data()), baseptr.data()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); } void tst_QSharedPointer::dynamicCastFailure() @@ -774,15 +937,15 @@ void tst_QSharedPointer::dynamicCastFailure() QSharedPointer<DerivedData> derivedptr = qSharedPointerDynamicCast<DerivedData>(baseptr); QVERIFY(derivedptr.isNull()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); { QSharedPointer<DerivedData> derivedptr = baseptr.dynamicCast<DerivedData>(); QVERIFY(derivedptr.isNull()); } - QCOMPARE(int(baseptr.d->weakref), 1); - QCOMPARE(int(baseptr.d->strongref), 1); + QCOMPARE(int(refCountData(baseptr)->weakref), 1); + QCOMPARE(int(refCountData(baseptr)->strongref), 1); } #endif @@ -814,6 +977,7 @@ void tst_QSharedPointer::constCorrectness() ptr = cvptr.constCast<Data>(); #endif } + check(); { Data *aData = new Data; @@ -827,6 +991,7 @@ void tst_QSharedPointer::constCorrectness() QCOMPARE(cptr.data(), aData); QCOMPARE(cptr.operator->(), aData); } + check(); } static int customDeleterFnCallCount; @@ -836,9 +1001,14 @@ void customDeleterFn(Data *ptr) delete ptr; } +static int refcount; + template <typename T> struct CustomDeleter { + CustomDeleter() { ++refcount; } + CustomDeleter(const CustomDeleter &) { ++refcount; } + ~CustomDeleter() { --refcount; } inline void operator()(T *ptr) { delete ptr; @@ -855,11 +1025,13 @@ void tst_QSharedPointer::customDeleter() QSharedPointer<Data> ptr2(new Data, &Data::alsoDelete); QSharedPointer<Data> ptr3(new Data, &Data::virtualDelete); } + check(); { QSharedPointer<DerivedData> ptr(new DerivedData, &Data::doDelete); QSharedPointer<DerivedData> ptr2(new DerivedData, &Data::alsoDelete); QSharedPointer<DerivedData> ptr3(new DerivedData, &Data::virtualDelete); } + check(); customDeleterFnCallCount = 0; { @@ -868,6 +1040,7 @@ void tst_QSharedPointer::customDeleter() QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); + check(); customDeleterFnCallCount = 0; { @@ -877,6 +1050,7 @@ void tst_QSharedPointer::customDeleter() QCOMPARE(customDeleterFnCallCount, 1); } QCOMPARE(customDeleterFnCallCount, 1); + check(); customDeleterFnCallCount = 0; { @@ -886,6 +1060,7 @@ void tst_QSharedPointer::customDeleter() QCOMPARE(customDeleterFnCallCount, 1); } QCOMPARE(customDeleterFnCallCount, 1); + check(); customDeleterFnCallCount = 0; { @@ -894,6 +1069,7 @@ void tst_QSharedPointer::customDeleter() QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); + check(); customDeleterFnCallCount = 0; { @@ -902,6 +1078,7 @@ void tst_QSharedPointer::customDeleter() QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); + check(); customDeleterFnCallCount = 0; { @@ -914,6 +1091,7 @@ void tst_QSharedPointer::customDeleter() QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); + check(); customDeleterFnCallCount = 0; { @@ -926,7 +1104,9 @@ void tst_QSharedPointer::customDeleter() QCOMPARE(customDeleterFnCallCount, 0); } QCOMPARE(customDeleterFnCallCount, 1); + check(); + refcount = 0; CustomDeleter<Data> dataDeleter; dataDeleter.callCount = 0; { @@ -935,6 +1115,8 @@ void tst_QSharedPointer::customDeleter() QCOMPARE(dataDeleter.callCount, 0); } QCOMPARE(dataDeleter.callCount, 1); + QCOMPARE(refcount, 1); + check(); dataDeleter.callCount = 0; { @@ -944,6 +1126,8 @@ void tst_QSharedPointer::customDeleter() QCOMPARE(dataDeleter.callCount, 0); } QCOMPARE(dataDeleter.callCount, 1); + QCOMPARE(refcount, 1); + check(); dataDeleter.callCount = 0; { @@ -956,6 +1140,8 @@ void tst_QSharedPointer::customDeleter() QCOMPARE(dataDeleter.callCount, 0); } QCOMPARE(dataDeleter.callCount, 1); + QCOMPARE(refcount, 1); + check(); dataDeleter.callCount = 0; { @@ -964,6 +1150,8 @@ void tst_QSharedPointer::customDeleter() QCOMPARE(dataDeleter.callCount, 0); } QCOMPARE(dataDeleter.callCount, 1); + QCOMPARE(refcount, 1); + check(); CustomDeleter<DerivedData> derivedDataDeleter; derivedDataDeleter.callCount = 0; @@ -976,6 +1164,8 @@ void tst_QSharedPointer::customDeleter() } QCOMPARE(dataDeleter.callCount, 0); QCOMPARE(derivedDataDeleter.callCount, 1); + QCOMPARE(refcount, 2); + check(); derivedDataDeleter.callCount = 0; dataDeleter.callCount = 0; @@ -992,6 +1182,8 @@ void tst_QSharedPointer::customDeleter() } QCOMPARE(dataDeleter.callCount, 1); QCOMPARE(derivedDataDeleter.callCount, 0); + QCOMPARE(refcount, 2); + check(); derivedDataDeleter.callCount = 0; dataDeleter.callCount = 0; @@ -1008,6 +1200,14 @@ void tst_QSharedPointer::customDeleter() } QCOMPARE(dataDeleter.callCount, 0); QCOMPARE(derivedDataDeleter.callCount, 1); + QCOMPARE(refcount, 2); + check(); +} + +void customQObjectDeleterFn(QObject *obj) +{ + ++customDeleterFnCallCount; + delete obj; } void tst_QSharedPointer::creating() @@ -1025,12 +1225,13 @@ void tst_QSharedPointer::creating() ptr.clear(); QCOMPARE(Data::destructorCounter, 1); } + check(); Data::generationCounter = Data::destructorCounter = 0; { QSharedPointer<Data> ptr = QSharedPointer<Data>::create(); QWeakPointer<Data> weakptr = ptr; - QtSharedPointer::ExternalRefCountData *d = ptr.d; + QtSharedPointer::ExternalRefCountData *d = refCountData(ptr); ptr.clear(); QVERIFY(ptr.isNull()); @@ -1040,6 +1241,7 @@ void tst_QSharedPointer::creating() QVERIFY(d->weakref == 1); QVERIFY(d->strongref == 0); } + check(); Data::generationCounter = Data::destructorCounter = 0; DerivedData::derivedDestructorCounter = 0; @@ -1052,6 +1254,7 @@ void tst_QSharedPointer::creating() QCOMPARE(Data::destructorCounter, 1); QCOMPARE(DerivedData::derivedDestructorCounter, 1); } + check(); { QSharedPointer<Data> ptr = QSharedPointer<DiffPtrDerivedData>::create(); @@ -1060,6 +1263,7 @@ void tst_QSharedPointer::creating() QCOMPARE(ptr.staticCast<DiffPtrDerivedData>()->buffer[3]+0, 16-3); QCOMPARE(ptr.staticCast<DiffPtrDerivedData>()->buffer[0]+0, 16); } + check(); { QSharedPointer<VirtualDerived> ptr = QSharedPointer<VirtualDerived>::create(); @@ -1069,7 +1273,11 @@ void tst_QSharedPointer::creating() QSharedPointer<Data> baseptr = ptr; QCOMPARE(baseptr->classLevel(), 4); } + check(); +} +void tst_QSharedPointer::creatingQObject() +{ { QSharedPointer<QObject> ptr = QSharedPointer<QObject>::create(); QCOMPARE(ptr->metaObject(), &QObject::staticMetaObject); @@ -1079,11 +1287,148 @@ void tst_QSharedPointer::creating() QVERIFY(qptr.isNull()); } + check(); { QSharedPointer<QObject> ptr = QSharedPointer<OtherObject>::create(); QCOMPARE(ptr->metaObject(), &OtherObject::staticMetaObject); } + check(); +} + +void tst_QSharedPointer::mixTrackingPointerCode() +{ + { + // pointer created with tracking + // deleted in code without tracking + QSharedPointer<int> ptr = QSharedPointer<int>(new int(42)); + Wrapper w(ptr); + ptr.clear(); + } + check(); + + { + // pointer created without tracking + // deleted in code with tracking + Wrapper w = Wrapper::create(); + w.ptr.clear(); + } +} + +class ThreadData +{ + QAtomicInt * volatile ptr; +public: + ThreadData(QAtomicInt *p) : ptr(p) { } + ~ThreadData() { ++ptr; } + void ref() + { + // if we're called after the destructor, we'll crash + ptr->ref(); + } +}; + +class StrongThread: public QThread +{ +protected: + void run() + { + usleep(rand() % 2000); + ptr->ref(); + ptr.clear(); + } +public: + QSharedPointer<ThreadData> ptr; +}; + +class WeakThread: public QThread +{ +protected: + void run() + { + usleep(rand() % 2000); + QSharedPointer<ThreadData> ptr = weak; + if (ptr) + ptr->ref(); + ptr.clear(); + } +public: + QWeakPointer<ThreadData> weak; +}; + +void tst_QSharedPointer::threadStressTest_data() +{ + QTest::addColumn<int>("strongThreadCount"); + QTest::addColumn<int>("weakThreadCount"); + + QTest::newRow("0+0") << 0 << 0; + QTest::newRow("1+0") << 1 << 0; + QTest::newRow("2+0") << 2 << 0; + QTest::newRow("10+0") << 10 << 0; + + QTest::newRow("0+1") << 0 << 1; + QTest::newRow("1+1") << 1 << 1; + + QTest::newRow("2+10") << 2 << 10; + QTest::newRow("5+10") << 5 << 10; + QTest::newRow("5+30") << 5 << 30; + + QTest::newRow("100+100") << 100 << 100; +} + +void tst_QSharedPointer::threadStressTest() +{ + QFETCH(int, strongThreadCount); + QFETCH(int, weakThreadCount); + + int guard1[128]; + QAtomicInt counter; + int guard2[128]; + + memset(guard1, 0, sizeof guard1); + memset(guard2, 0, sizeof guard2); + + for (int r = 0; r < 5; ++r) { + QVector<QThread*> allThreads(6 * qMax(strongThreadCount, weakThreadCount) + 3, 0); + QSharedPointer<ThreadData> base = QSharedPointer<ThreadData>(new ThreadData(&counter)); + counter = 0; + + // set the pointers + for (int i = 0; i < strongThreadCount; ++i) { + StrongThread *t = new StrongThread; + t->ptr = base; + allThreads[2 * i] = t; + } + for (int i = 0; i < weakThreadCount; ++i) { + WeakThread *t = new WeakThread; + t->weak = base; + allThreads[6 * i + 3] = t; + } + + base.clear(); + + srand(time(NULL)); + // start threads + for (int i = 0; i < allThreads.count(); ++i) + if (allThreads[i]) allThreads[i]->start(); + + // wait for them to finish + for (int i = 0; i < allThreads.count(); ++i) + if (allThreads[i]) allThreads[i]->wait(); + qDeleteAll(allThreads); + + // ensure the guards aren't touched + for (uint i = 0; i < sizeof guard1 / sizeof guard1[0]; ++i) + QVERIFY(!guard1[i]); + for (uint i = 0; i < sizeof guard2 / sizeof guard2[0]; ++i) + QVERIFY(!guard2[i]); + + // verify that the count is the right range + int minValue = strongThreadCount; + int maxValue = strongThreadCount + weakThreadCount; + QVERIFY(counter >= minValue); + QVERIFY(counter <= maxValue); + } } void tst_QSharedPointer::validConstructs() @@ -1195,6 +1540,7 @@ void tst_QSharedPointer::invalidConstructs_data() "QSharedPointer<Data> b;\n" "if (a + b) return;"; +#if QT_VERSION >= 0x040600 // two objects with the same pointer QTest::newRow("same-pointer") << &QTest::QExternalTest::tryRunFail @@ -1208,6 +1554,7 @@ void tst_QSharedPointer::invalidConstructs_data() << "Data *aData = new Data;\n" "QSharedPointer<Data> ptr1 = QSharedPointer<Data>(aData);" "ptr1 = QSharedPointer<Data>(aData);"; +#endif // any type of cast for unrelated types: // (we have no reinterpret_cast) @@ -1229,6 +1576,17 @@ void tst_QSharedPointer::invalidConstructs_data() << &QTest::QExternalTest::tryCompileFail << "QSharedPointer<Data> ptr1;\n" "QSharedPointer<int> ptr2 = qSharedPointerObjectCast<int>(ptr1);"; + + QTest::newRow("weak-pointer-from-regular-pointer") + << &QTest::QExternalTest::tryCompileFail + << "Data *ptr = 0;\n" + "QWeakPointer<Data> weakptr(ptr);\n"; + + QTest::newRow("shared-pointer-from-unmanaged-qobject") + << &QTest::QExternalTest::tryRunFail + << "QObject *ptr = new QObject;\n" + "QWeakPointer<QObject> weak = ptr;\n" // this makes the object unmanaged + "QSharedPointer<QObject> shared(ptr);\n"; } void tst_QSharedPointer::invalidConstructs() diff --git a/tests/auto/qsharedpointer/wrapper.cpp b/tests/auto/qsharedpointer/wrapper.cpp new file mode 100644 index 0000000..7640e68 --- /dev/null +++ b/tests/auto/qsharedpointer/wrapper.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef QT_SHAREDPOINTER_TRACK_POINTERS +# undef QT_SHAREDPOINTER_TRACK_POINTERS +#endif +#include <QtCore/qsharedpointer.h> +#include "wrapper.h" + +Wrapper::Wrapper(const QSharedPointer<int> &value) + : ptr(value) +{ +} + +Wrapper::~Wrapper() +{ +} + +Wrapper Wrapper::create() +{ + return Wrapper(QSharedPointer<int>(new int(-47))); +} diff --git a/tests/auto/qsharedpointer/wrapper.h b/tests/auto/qsharedpointer/wrapper.h new file mode 100644 index 0000000..a888063 --- /dev/null +++ b/tests/auto/qsharedpointer/wrapper.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef WRAPPER_H +#define WRAPPER_H + +template <class T> class QSharedPointer; +class Wrapper +{ +public: + QSharedPointer<int> ptr; + Wrapper(const QSharedPointer<int> &); + ~Wrapper(); + + static Wrapper create(); +}; + +#endif // WRAPPER_H diff --git a/tests/auto/qsharedpointer_and_qwidget/qsharedpointer_and_qwidget.pro b/tests/auto/qsharedpointer_and_qwidget/qsharedpointer_and_qwidget.pro new file mode 100644 index 0000000..db647a5 --- /dev/null +++ b/tests/auto/qsharedpointer_and_qwidget/qsharedpointer_and_qwidget.pro @@ -0,0 +1,3 @@ +load(qttest_p4) +SOURCES += tst_qsharedpointer_and_qwidget.cpp +DEFINES += SRCDIR=\\\"$$PWD/\\\" diff --git a/tests/auto/qsharedpointer_and_qwidget/tst_qsharedpointer_and_qwidget.cpp b/tests/auto/qsharedpointer_and_qwidget/tst_qsharedpointer_and_qwidget.cpp new file mode 100644 index 0000000..66727c2 --- /dev/null +++ b/tests/auto/qsharedpointer_and_qwidget/tst_qsharedpointer_and_qwidget.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/QWidget> +#include <QtGui/QPushButton> +#include <QtTest/QtTest> + +namespace QtSharedPointer { + Q_CORE_EXPORT void internalSafetyCheckCleanCheck(); +} + +class tst_QSharedPointer_and_QWidget: public QObject +{ + Q_OBJECT +private slots: + void weak_externalDelete(); + void weak_parentDelete(); + void weak_parentDelete_setParent(); + + void strong_weak(); + + void strong_sharedptrDelete(); + +public slots: + void cleanup() { check(); } + +public: + inline void check() + { +#ifdef QT_BUILD_INTERNAL + QtSharedPointer::internalSafetyCheckCleanCheck(); +#endif + } +}; + +void tst_QSharedPointer_and_QWidget::weak_externalDelete() +{ + QWidget *w = new QWidget; + QWeakPointer<QWidget> ptr = w; + + QVERIFY(!ptr.isNull()); + + delete w; + QVERIFY(ptr.isNull()); +} + +void tst_QSharedPointer_and_QWidget::weak_parentDelete() +{ + QWidget *parent = new QWidget; + QWidget *w = new QWidget(parent); + QWeakPointer<QWidget> ptr = w; + + QVERIFY(!ptr.isNull()); + + delete parent; + QVERIFY(ptr.isNull()); +} + +void tst_QSharedPointer_and_QWidget::weak_parentDelete_setParent() +{ + QWidget *parent = new QWidget; + QWidget *w = new QWidget; + QWeakPointer<QWidget> ptr = w; + w->setParent(parent); + + QVERIFY(!ptr.isNull()); + + delete parent; + QVERIFY(ptr.isNull()); +} + +// -- mixed -- + +void tst_QSharedPointer_and_QWidget::strong_weak() +{ + QSharedPointer<QWidget> ptr(new QWidget); + QWeakPointer<QWidget> weak = ptr.data(); + QWeakPointer<QWidget> weak2 = ptr; + + QVERIFY(!weak.isNull()); + QVERIFY(!weak2.isNull()); + + ptr.clear(); // deletes + + QVERIFY(weak.isNull()); + QVERIFY(weak2.isNull()); +} + + +// ---- strong management ---- + +void tst_QSharedPointer_and_QWidget::strong_sharedptrDelete() +{ + QWidget *parent = new QWidget; + QSharedPointer<QWidget> ptr(new QWidget(parent)); + QWeakPointer<QWidget> weak = ptr; + QPointer<QWidget> check = ptr.data(); + + QVERIFY(!check.isNull()); + QVERIFY(!weak.isNull()); + + ptr.clear(); // deletes + + QVERIFY(check.isNull()); + QVERIFY(weak.isNull()); + + delete parent; // mustn't crash +} + +QTEST_MAIN(tst_QSharedPointer_and_QWidget) + +#include "tst_qsharedpointer_and_qwidget.moc" diff --git a/tests/auto/qsqldatabase/tst_databases.h b/tests/auto/qsqldatabase/tst_databases.h index 8253541..f1e9d28 100644 --- a/tests/auto/qsqldatabase/tst_databases.h +++ b/tests/auto/qsqldatabase/tst_databases.h @@ -219,7 +219,7 @@ public: // addDb( "QMYSQL3", "testdb", "troll", "trond", "horsehead.nokia.troll.no", 3307 ); // addDb( "QMYSQL3", "testdb", "troll", "trond", "horsehead.nokia.troll.no", 3308, "CLIENT_COMPRESS=1;CLIENT_SSL=1" ); // MySQL 4.1.1 // addDb( "QMYSQL3", "testdb", "troll", "trond", "horsehead.nokia.troll.no", 3309, "CLIENT_COMPRESS=1;CLIENT_SSL=1" ); // MySQL 5.0.18 Linux -// addDb( "QMYSQL3", "testdb", "troll", "trond", "iceblink.nokia.troll.no" ); // MySQL 5.0.13 Windows +// addDb( "QMYSQL3", "testdb", "troll", "trond", "silence.nokia.troll.no" ); // MySQL 5.1.36 Windows // addDb( "QMYSQL3", "testdb", "testuser", "Ee4Gabf6_", "mysql4-nokia.trolltech.com.au" ); // MySQL 4.1.22-2.el4 linux // addDb( "QMYSQL3", "testdb", "testuser", "Ee4Gabf6_", "mysql5-nokia.trolltech.com.au" ); // MySQL 5.0.45-7.el5 linux @@ -518,6 +518,16 @@ public: return QString(); } + static QString getPSQLVersion( const QSqlDatabase &db ) + { + QSqlQuery q(db); + q.exec( "select version()" ); + if(q.next()) + return q.value( 0 ).toString(); + else + return QString(); + } + QStringList dbNames; int counter; }; diff --git a/tests/auto/qsqldatabase/tst_qsqldatabase.cpp b/tests/auto/qsqldatabase/tst_qsqldatabase.cpp index e1f1d3c..fe4c86e 100644 --- a/tests/auto/qsqldatabase/tst_qsqldatabase.cpp +++ b/tests/auto/qsqldatabase/tst_qsqldatabase.cpp @@ -1054,6 +1054,7 @@ void tst_QSqlDatabase::recordMySQL() int major = tst_Databases::getMySqlVersion( db ).section( QChar('.'), 0, 0 ).toInt(); int minor = tst_Databases::getMySqlVersion( db ).section( QChar('.'), 1, 1 ).toInt(); int revision = tst_Databases::getMySqlVersion( db ).section( QChar('.'), 2, 2 ).toInt(); + int vernum = (major << 16) + (minor << 8) + revision; #ifdef QT3_SUPPORT /* The below is broken in mysql below 5.0.15 @@ -1061,7 +1062,7 @@ void tst_QSqlDatabase::recordMySQL() specifically: Before MySQL 5.0.15, the pad value is space. Values are right-padded with space on insert, and trailing spaces are removed on select. */ - if ( major >5 || ( major == 5 && minor > 0) || ( major == 5 && minor == 0 && revision >= 15) ) { + if( vernum >= ((5 << 16) + 15) ) { bin10 = FieldDef("binary(10)", QVariant::ByteArray, QByteArray(Q3CString("123abc "))); varbin10 = FieldDef("varbinary(10)", QVariant::ByteArray, QByteArray(Q3CString("123abcv "))); } @@ -1591,6 +1592,11 @@ void tst_QSqlDatabase::bug_249059() QSqlDatabase db = QSqlDatabase::database(dbName); CHECK_DATABASE(db); + QString version=tst_Databases::getPSQLVersion( db ); + double ver=version.section(QChar::fromLatin1('.'),0,1).toDouble(); + if (ver < 7.3) + QSKIP("Test requires PostgreSQL >= 7.3", SkipSingle); + QSqlQuery q(db); QString tableName = qTableName("bug_249059"); QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (dt timestamp, t time)").arg(tableName))); @@ -2025,11 +2031,9 @@ void tst_QSqlDatabase::mysql_multiselect() CHECK_DATABASE(db); QSqlQuery q(db); - QVERIFY_SQL(q, exec("select version()")); - QVERIFY_SQL(q, next()); QString version=tst_Databases::getMySqlVersion( db ); - int ver=version.section(QChar::fromLatin1('.'),0,1).toDouble(); - if (ver >= 4.1) + double ver=version.section(QChar::fromLatin1('.'),0,1).toDouble(); + if (ver < 4.1) QSKIP("Test requires MySQL >= 4.1", SkipSingle); QVERIFY_SQL(q, exec("SELECT * FROM " + qTableName("qtest") + "; SELECT * FROM " + qTableName("qtest"))); diff --git a/tests/auto/qsqlrelationaltablemodel/tst_qsqlrelationaltablemodel.cpp b/tests/auto/qsqlrelationaltablemodel/tst_qsqlrelationaltablemodel.cpp index 1e23d3d..d836486 100644 --- a/tests/auto/qsqlrelationaltablemodel/tst_qsqlrelationaltablemodel.cpp +++ b/tests/auto/qsqlrelationaltablemodel/tst_qsqlrelationaltablemodel.cpp @@ -930,13 +930,15 @@ void tst_QSqlRelationalTableModel::casing() QSqlQuery q(db); QVERIFY_SQL( q, exec("create table " + qTableName("CASETEST1", db.driver()).toUpper() + " (id int not null primary key, name varchar(20), title_key int, another_title_key int)")); + + if( !q.exec("create table " + qTableName("casetest1", db.driver()) + + " (ident int not null primary key, name varchar(20), title_key int)")) + QSKIP("The casing test for this database is irrelevant since this database does not treat different cases as separate entities", SkipAll); + QVERIFY_SQL( q, exec("insert into " + qTableName("CASETEST1", db.driver()).toUpper() + " values(1, 'harry', 1, 2)")); QVERIFY_SQL( q, exec("insert into " + qTableName("CASETEST1", db.driver()).toUpper() + " values(2, 'trond', 2, 1)")); QVERIFY_SQL( q, exec("insert into " + qTableName("CASETEST1", db.driver()).toUpper() + " values(3, 'vohi', 1, 2)")); QVERIFY_SQL( q, exec("insert into " + qTableName("CASETEST1", db.driver()).toUpper() + " values(4, 'boris', 2, 2)")); - - QVERIFY_SQL( q, exec("create table " + qTableName("casetest1", db.driver()) + - " (ident int not null primary key, name varchar(20), title_key int)")); QVERIFY_SQL( q, exec("insert into " + qTableName("casetest1", db.driver()) + " values(1, 'jerry', 1)")); QVERIFY_SQL( q, exec("insert into " + qTableName("casetest1", db.driver()) + " values(2, 'george', 2)")); QVERIFY_SQL( q, exec("insert into " + qTableName("casetest1", db.driver()) + " values(4, 'kramer', 2)")); diff --git a/tests/auto/qstate/tst_qstate.cpp b/tests/auto/qstate/tst_qstate.cpp index 78b9853..43ea7fe 100644 --- a/tests/auto/qstate/tst_qstate.cpp +++ b/tests/auto/qstate/tst_qstate.cpp @@ -250,8 +250,9 @@ class EventTestTransition: public QAbstractTransition { public: EventTestTransition(QEvent::Type type, QState *targetState) - : QAbstractTransition(QList<QAbstractState*>() << targetState), m_type(type) - { + : QAbstractTransition(), m_type(type) + { + setTargetState(targetState); } protected: diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index 7f4d9f5..97115bb 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -192,6 +192,7 @@ private slots: // void overrideDefaultTargetAnimationWithSource(); void nestedStateMachines(); + void goToState(); }; tst_QStateMachine::tst_QStateMachine() @@ -225,7 +226,8 @@ class TestTransition : public QAbstractTransition { public: TestTransition(QAbstractState *target) - : QAbstractTransition(QList<QAbstractState*>() << target) {} + : QAbstractTransition() + { setTargetState(target); } QList<int> triggers; protected: virtual bool eventTest(QEvent *) { @@ -248,7 +250,8 @@ class EventTransition : public QAbstractTransition { public: EventTransition(QEvent::Type type, QAbstractState *target, QState *parent = 0) - : QAbstractTransition(QList<QAbstractState*>() << target, parent), m_type(type) {} + : QAbstractTransition(parent), m_type(type) + { setTargetState(target); } protected: virtual bool eventTest(QEvent *e) { return (e->type() == m_type); @@ -1513,7 +1516,8 @@ class StringTransition : public QAbstractTransition { public: StringTransition(const QString &value, QAbstractState *target) - : QAbstractTransition(QList<QAbstractState*>() << target), m_value(value) {} + : QAbstractTransition(), m_value(value) + { setTargetState(target); } protected: virtual bool eventTest(QEvent *e) @@ -1736,7 +1740,8 @@ public: : QSignalTransition(sourceState) {} TestSignalTransition(QObject *sender, const char *signal, QAbstractState *target) - : QSignalTransition(sender, signal, QList<QAbstractState*>() << target) {} + : QSignalTransition(sender, signal) + { setTargetState(target); } QVariantList argumentsReceived() const { return m_args; } @@ -2021,20 +2026,15 @@ void tst_QStateMachine::signalTransitions() void tst_QStateMachine::eventTransitions() { QPushButton button; - for (int x = 0; x < 2; ++x) { + { QStateMachine machine; QState *s0 = new QState(&machine); QFinalState *s1 = new QFinalState(&machine); QMouseEventTransition *trans; - if (x == 0) { - trans = new QMouseEventTransition(&button, QEvent::MouseButtonPress, Qt::LeftButton); - QCOMPARE(trans->targetState(), (QAbstractState*)0); - trans->setTargetState(s1); - } else { - trans = new QMouseEventTransition(&button, QEvent::MouseButtonPress, - Qt::LeftButton, QList<QAbstractState*>() << s1); - } + trans = new QMouseEventTransition(&button, QEvent::MouseButtonPress, Qt::LeftButton); + QCOMPARE(trans->targetState(), (QAbstractState*)0); + trans->setTargetState(s1); QCOMPARE(trans->eventType(), QEvent::MouseButtonPress); QCOMPARE(trans->button(), Qt::LeftButton); QCOMPARE(trans->targetState(), (QAbstractState*)s1); @@ -2070,7 +2070,7 @@ void tst_QStateMachine::eventTransitions() QTest::mousePress(&button2, Qt::LeftButton); QTRY_COMPARE(finishedSpy.count(), 4); } - for (int x = 0; x < 3; ++x) { + for (int x = 0; x < 2; ++x) { QStateMachine machine; QState *s0 = new QState(&machine); QFinalState *s1 = new QFinalState(&machine); @@ -2086,9 +2086,6 @@ void tst_QStateMachine::eventTransitions() } else if (x == 1) { trans = new QEventTransition(&button, QEvent::MouseButtonPress); trans->setTargetState(s1); - } else { - trans = new QEventTransition(&button, QEvent::MouseButtonPress, - QList<QAbstractState*>() << s1); } QCOMPARE(trans->eventObject(), (QObject*)&button); QCOMPARE(trans->eventType(), QEvent::MouseButtonPress); @@ -3900,5 +3897,45 @@ void tst_QStateMachine::nestedStateMachines() QTRY_COMPARE(finishedSpy.count(), 1); } +void tst_QStateMachine::goToState() +{ + QStateMachine machine; + QState *s1 = new QState(&machine); + QState *s2 = new QState(&machine); + machine.setInitialState(s1); + QSignalSpy startedSpy(&machine, SIGNAL(started())); + machine.start(); + QTRY_COMPARE(startedSpy.count(), 1); + + QStateMachinePrivate::get(&machine)->goToState(s2); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s2)); + + QStateMachinePrivate::get(&machine)->goToState(s2); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s2)); + + QStateMachinePrivate::get(&machine)->goToState(s1); + QStateMachinePrivate::get(&machine)->goToState(s2); + QStateMachinePrivate::get(&machine)->goToState(s1); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s2)); + + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); + + // go to state in group + QState *s2_1 = new QState(s2); + s2->setInitialState(s2_1); + QStateMachinePrivate::get(&machine)->goToState(s2_1); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 2); + QVERIFY(machine.configuration().contains(s2)); + QVERIFY(machine.configuration().contains(s2_1)); +} + QTEST_MAIN(tst_QStateMachine) #include "tst_qstatemachine.moc" diff --git a/tests/auto/selftests/expected_longstring.txt b/tests/auto/selftests/expected_longstring.txt new file mode 100644 index 0000000..4abdb23 --- /dev/null +++ b/tests/auto/selftests/expected_longstring.txt @@ -0,0 +1,16 @@ +********* Start testing of tst_LongString ********* +Config: Using QTest library 4.6.0, Qt 4.6.0 +PASS : tst_LongString::initTestCase() +FAIL! : tst_LongString::failWithLongString() Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. + +Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. + +Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi. + +Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis. + +Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. + Loc: [/home/rmcgover/depot/qt/master/tests/auto/selftests/longstring/tst_longstring.cpp(67)] +PASS : tst_LongString::cleanupTestCase() +Totals: 2 passed, 1 failed, 0 skipped +********* Finished testing of tst_LongString ********* diff --git a/tests/auto/selftests/longstring/longstring.pro b/tests/auto/selftests/longstring/longstring.pro new file mode 100644 index 0000000..b917481 --- /dev/null +++ b/tests/auto/selftests/longstring/longstring.pro @@ -0,0 +1,10 @@ +load(qttest_p4) +SOURCES += tst_longstring.cpp +QT = core + +mac:CONFIG -= app_bundle +CONFIG -= debug_and_release_target + + +TARGET = longstring + diff --git a/tests/auto/selftests/longstring/tst_longstring.cpp b/tests/auto/selftests/longstring/tst_longstring.cpp new file mode 100644 index 0000000..a708fa7 --- /dev/null +++ b/tests/auto/selftests/longstring/tst_longstring.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtCore> +#include <QtTest/QtTest> + +char const lipsum[] = +"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui.\n\n" +"Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia.\n\n" +"Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. Etiam imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi.\n\n" +"Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede sit amet augue. In turpis. Pellentesque posuere. Praesent turpis.\n\n" +"Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum mollis diam. Pellentesque ut neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas." +; + +/*! + \internal + */ +class tst_LongString: public QObject +{ + Q_OBJECT + +private slots: + void failWithLongString() const; +}; + +void tst_LongString::failWithLongString() const +{ + QFAIL(lipsum); +} + +QTEST_MAIN(tst_LongString) + +#include "tst_longstring.moc" diff --git a/tests/auto/selftests/selftests.pro b/tests/auto/selftests/selftests.pro index 45de658..d854b5e 100644 --- a/tests/auto/selftests/selftests.pro +++ b/tests/auto/selftests/selftests.pro @@ -5,7 +5,7 @@ SUBDIRS = subtest test warnings maxwarnings cmptest globaldata skipglobal skip \ skipinit skipinitdata datetime singleskip assert waitwithoutgui differentexec \ exceptionthrow qexecstringlist datatable commandlinedata\ benchlibwalltime benchlibcallgrind benchlibeventcounter benchlibtickcounter \ - benchliboptions xunit badxml + benchliboptions xunit badxml longstring INSTALLS = diff --git a/tests/auto/selftests/selftests.qrc b/tests/auto/selftests/selftests.qrc index 3d78bf5..4b75405 100644 --- a/tests/auto/selftests/selftests.qrc +++ b/tests/auto/selftests/selftests.qrc @@ -36,5 +36,6 @@ <file>expected_benchliboptions.txt</file> <file>expected_benchlibtickcounter.txt</file> <file>expected_xunit.txt</file> + <file>expected_longstring.txt</file> </qresource> </RCC> diff --git a/tests/auto/selftests/tst_selftests.cpp b/tests/auto/selftests/tst_selftests.cpp index 8eb7fe1..76a005b 100644 --- a/tests/auto/selftests/tst_selftests.cpp +++ b/tests/auto/selftests/tst_selftests.cpp @@ -184,13 +184,15 @@ void tst_Selftests::runSubTest_data() QTest::newRow("benchliboptions") << "benchliboptions" << QStringList("-eventcounter"); QTest::newRow("benchlibwalltime") << "benchlibwalltime" << QStringList(); - //### QWS tests are currently run on a virtual machine, where ticks are not - //### monotonously increasing -#if defined(HAVE_TICK_COUNTER) && !defined(Q_WS_QWS) + //### This test is affected by the speed of the CPU and whether the tick counter is + //### monotonically increasing. It won't work on some machines so leave it off by default. + //### Feel free to uncomment for your own testing. +#if 0 QTest::newRow("benchlibtickcounter") << "benchlibtickcounter" << QStringList("-tickcounter"); #endif QTest::newRow("xunit") << "xunit" << QStringList("-xunitxml"); + QTest::newRow("longstring") << "longstring" << QStringList(); } diff --git a/tests/auto/tests.xml b/tests/auto/tests.xml index a5386b2..ccfc380 100644 --- a/tests/auto/tests.xml +++ b/tests/auto/tests.xml @@ -271,6 +271,10 @@ <Test name="qsocks5socketengine" location="tests/auto/qsocks5socketengine/tst_qsocks5socketengine" /> <Test name="qsortfilterproxymodel" location="tests/auto/qsortfilterproxymodel/tst_qsortfilterproxymodel" /> <Test name="qsound" location="tests/auto/qsound/tst_qsound" /> + <Test name="qaudiodeviceid" location="tests/auto/qaudiodeviceid/tst_qaudiodeviceid" /> + <Test name="qaudioformat" location="tests/auto/qaudioformat/tst_qaudioformat" /> + <Test name="qaudiooutput" location="tests/auto/qaudiooutput/tst_qaudiooutput" /> + <Test name="qaudioinput" location="tests/auto/qaudioinput/tst_qaudioinput" /> <Test name="qsourcelocation" location="tests/auto/qsourcelocation/tst_qsourcelocation" /> <Test name="qspinbox" location="tests/auto/qspinbox/tst_qspinbox" /> <Test name="qsplitter" location="tests/auto/qsplitter/tst_qsplitter" /> @@ -676,6 +680,10 @@ <Test id="qsocks5socketengine" /> <Test id="qsortfilterproxymodel" /> <Test id="qsound" /> + <Test id="qaudiodeviceid" /> + <Test id="qaudioformat" /> + <Test id="qaudiooutput" /> + <Test id="qaudioinput" /> <Test id="qsourcelocation" /> <Test id="qspinbox" /> <Test id="qsplitter" /> diff --git a/tests/auto/uic3/uic3.pro b/tests/auto/uic3/uic3.pro index 520f95a..72a1304 100644 --- a/tests/auto/uic3/uic3.pro +++ b/tests/auto/uic3/uic3.pro @@ -6,3 +6,6 @@ TARGET = tst_uic3 # This test is not run on wince (I think) DEFINES += SRCDIR=\\\"$$PWD\\\" + +requires(contains(QT_CONFIG,qt3support)) + diff --git a/tests/auto/uiloader/baseline/css_task259226_spinboxes.ui b/tests/auto/uiloader/baseline/css_task259226_spinboxes.ui new file mode 100644 index 0000000..eb9ade3 --- /dev/null +++ b/tests/auto/uiloader/baseline/css_task259226_spinboxes.ui @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Form</class> + <widget class="QWidget" name="Form"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>320</width> + <height>116</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <property name="styleSheet"> + <string notr="true">#spinbox1::up-button{ + subcontrol-origin: border; + subcontrol-position: top left; + } +#spinbox1::down-button { + subcontrol-origin: border; + subcontrol-position: bottom left; + } + + +#spinbox2::up-button{ + subcontrol-origin: border; + subcontrol-position: center left; + left: 0px; + height: 100%; + top: 0px; + } +#spinbox2::down-button { + subcontrol-origin: border; + subcontrol-position: center right; + right: 0px; + height: 100%; + top: 0px; + } + + + +#spinbox3::up-button{ + subcontrol-origin: border; + subcontrol-position: top left; + } + + +#spinbox4 { padding: 20px 1px; } +#spinbox4::up-button{ + subcontrol-origin: border; + subcontrol-position: top center; + width:100%; + height: 20px; + } +#spinbox4::down-button { + subcontrol-origin: border; + subcontrol-position: bottom center; + width:100%; + height: 20px; + } + +</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QSpinBox" name="spinbox1"/> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="spinbox2"/> + </item> + <item row="1" column="0"> + <widget class="QSpinBox" name="spinbox3"/> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="spinbox4"/> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tests/benchmarks/qgraphicsitem/qgraphicsitem.pro b/tests/benchmarks/qgraphicsitem/qgraphicsitem.pro index c8fc07b..726bb96 100644 --- a/tests/benchmarks/qgraphicsitem/qgraphicsitem.pro +++ b/tests/benchmarks/qgraphicsitem/qgraphicsitem.pro @@ -3,4 +3,3 @@ TEMPLATE = app TARGET = tst_qgraphicsitem SOURCES += tst_qgraphicsitem.cpp - diff --git a/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp b/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp index d072d08..ee27ebe 100644 --- a/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp +++ b/tests/benchmarks/qgraphicsitem/tst_qgraphicsitem.cpp @@ -25,6 +25,9 @@ public slots: void cleanup(); private slots: + void setParentItem(); + void setParentItem_deep(); + void deleteItemWithManyChildren(); void setPos_data(); void setPos(); void setTransform_data(); @@ -34,7 +37,6 @@ private slots: void shear(); void translate(); void setRotation(); - void setRotationXYZ(); }; tst_QGraphicsItem::tst_QGraphicsItem() @@ -53,6 +55,40 @@ void tst_QGraphicsItem::cleanup() { } +void tst_QGraphicsItem::setParentItem() +{ + QBENCHMARK { + QGraphicsRectItem rect; + QGraphicsRectItem *childRect = new QGraphicsRectItem; + childRect->setParentItem(&rect); + } +} + +void tst_QGraphicsItem::setParentItem_deep() +{ + QBENCHMARK { + QGraphicsRectItem rect; + QGraphicsRectItem *lastRect = ▭ + for (int i = 0; i < 10; ++i) { + QGraphicsRectItem *childRect = new QGraphicsRectItem; + childRect->setParentItem(lastRect); + lastRect = childRect; + } + QGraphicsItem *first = rect.children().first(); + first->setParentItem(0); + } +} + +void tst_QGraphicsItem::deleteItemWithManyChildren() +{ + QBENCHMARK { + QGraphicsRectItem *rect = new QGraphicsRectItem; + for (int i = 0; i < 1000; ++i) + new QGraphicsRectItem(rect); + delete rect; + } +} + void tst_QGraphicsItem::setPos_data() { QTest::addColumn<QPointF>("pos"); @@ -150,18 +186,7 @@ void tst_QGraphicsItem::setRotation() QGraphicsItem *item = scene.addRect(QRectF(0, 0, 100, 100)); QBENCHMARK { - item->setXRotation(45); - item->transform(); // prevent lazy optimizing - } -} - -void tst_QGraphicsItem::setRotationXYZ() -{ - QGraphicsScene scene; - QGraphicsItem *item = scene.addRect(QRectF(0, 0, 100, 100)); - - QBENCHMARK { - item->setRotation(45, 45, 45); + item->setRotation(45); item->transform(); // prevent lazy optimizing } } diff --git a/tests/manual/windowflags/controllerwindow.cpp b/tests/manual/windowflags/controllerwindow.cpp new file mode 100644 index 0000000..055ff40 --- /dev/null +++ b/tests/manual/windowflags/controllerwindow.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +#include "controllerwindow.h" + +//! [0] +ControllerWindow::ControllerWindow() +{ + parentWindow = new QMainWindow; + parentWindow->setWindowTitle(tr("Preview parent window")); + QLabel *label = new QLabel(tr("Parent window")); + parentWindow->setCentralWidget(label); + + previewWindow = new PreviewWindow; + previewDialog = new PreviewDialog; + + createTypeGroupBox(); + createHintsGroupBox(); + + quitButton = new QPushButton(tr("&Quit")); + connect(quitButton, SIGNAL(clicked()), qApp, SLOT(quit())); + + QHBoxLayout *bottomLayout = new QHBoxLayout; + bottomLayout->addStretch(); + bottomLayout->addWidget(quitButton); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(widgetTypeGroupBox); + mainLayout->addWidget(additionalOptionsGroupBox); + mainLayout->addWidget(typeGroupBox); + mainLayout->addWidget(hintsGroupBox); + mainLayout->addLayout(bottomLayout); + setLayout(mainLayout); + + setWindowTitle(tr("Window Flags")); + updatePreview(); +} + +void ControllerWindow::updatePreview() +{ + Qt::WindowFlags flags = 0; + + if (windowRadioButton->isChecked()) { + flags = Qt::Window; + } else if (dialogRadioButton->isChecked()) { + flags = Qt::Dialog; + } else if (sheetRadioButton->isChecked()) { + flags = Qt::Sheet; + } else if (drawerRadioButton->isChecked()) { + flags = Qt::Drawer; + } else if (popupRadioButton->isChecked()) { + flags = Qt::Popup; + } else if (toolRadioButton->isChecked()) { + flags = Qt::Tool; + } else if (toolTipRadioButton->isChecked()) { + flags = Qt::ToolTip; + } else if (splashScreenRadioButton->isChecked()) { + flags = Qt::SplashScreen; + } + + if (msWindowsFixedSizeDialogCheckBox->isChecked()) + flags |= Qt::MSWindowsFixedSizeDialogHint; + if (x11BypassWindowManagerCheckBox->isChecked()) + flags |= Qt::X11BypassWindowManagerHint; + if (framelessWindowCheckBox->isChecked()) + flags |= Qt::FramelessWindowHint; + if (windowTitleCheckBox->isChecked()) + flags |= Qt::WindowTitleHint; + if (windowSystemMenuCheckBox->isChecked()) + flags |= Qt::WindowSystemMenuHint; + if (windowMinimizeButtonCheckBox->isChecked()) + flags |= Qt::WindowMinimizeButtonHint; + if (windowMaximizeButtonCheckBox->isChecked()) + flags |= Qt::WindowMaximizeButtonHint; + if (windowCloseButtonCheckBox->isChecked()) + flags |= Qt::WindowCloseButtonHint; + if (windowContextHelpButtonCheckBox->isChecked()) + flags |= Qt::WindowContextHelpButtonHint; + if (windowShadeButtonCheckBox->isChecked()) + flags |= Qt::WindowShadeButtonHint; + if (windowStaysOnTopCheckBox->isChecked()) + flags |= Qt::WindowStaysOnTopHint; + if (windowStaysOnBottomCheckBox->isChecked()) + flags |= Qt::WindowStaysOnBottomHint; + if (customizeWindowHintCheckBox->isChecked()) + flags |= Qt::CustomizeWindowHint; + + previewWindow->hide(); + previewDialog->hide(); + QWidget *widget = 0; + if (previewWidgetButton->isChecked()) + widget = previewWindow; + else + widget = previewDialog; + + if (modalWindowCheckBox->isChecked()) { + parentWindow->show(); + widget->setWindowModality(Qt::WindowModal); + widget->setParent(parentWindow); + } else { + widget->setWindowModality(Qt::NonModal); + widget->setParent(0); + parentWindow->hide(); + } + + widget->setWindowFlags(flags); + + if (fixedSizeWindowCheckBox->isChecked()) { + widget->setFixedSize(300, 300); + } else { + widget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + } + + QPoint pos = widget->pos(); + if (pos.x() < 0) + pos.setX(0); + if (pos.y() < 0) + pos.setY(0); + widget->move(pos); + widget->show(); +} + +void ControllerWindow::createTypeGroupBox() +{ + widgetTypeGroupBox = new QGroupBox(tr("Widget Type")); + previewWidgetButton = createRadioButton(tr("QWidget")); + previewWidgetButton->setChecked(true); + previewDialogButton = createRadioButton(tr("QDialog")); + QHBoxLayout *l = new QHBoxLayout; + l->addWidget(previewWidgetButton); + l->addWidget(previewDialogButton); + widgetTypeGroupBox->setLayout(l); + + additionalOptionsGroupBox = new QGroupBox(tr("Additional options")); + l = new QHBoxLayout; + modalWindowCheckBox = createCheckBox(tr("Modal window")); + fixedSizeWindowCheckBox = createCheckBox(tr("Fixed size window")); + l->addWidget(modalWindowCheckBox); + l->addWidget(fixedSizeWindowCheckBox); + additionalOptionsGroupBox->setLayout(l); + + typeGroupBox = new QGroupBox(tr("Type")); + + windowRadioButton = createRadioButton(tr("Window")); + dialogRadioButton = createRadioButton(tr("Dialog")); + sheetRadioButton = createRadioButton(tr("Sheet")); + drawerRadioButton = createRadioButton(tr("Drawer")); + popupRadioButton = createRadioButton(tr("Popup")); + toolRadioButton = createRadioButton(tr("Tool")); + toolTipRadioButton = createRadioButton(tr("Tooltip")); + splashScreenRadioButton = createRadioButton(tr("Splash screen")); + windowRadioButton->setChecked(true); + + QGridLayout *layout = new QGridLayout; + layout->addWidget(windowRadioButton, 0, 0); + layout->addWidget(dialogRadioButton, 1, 0); + layout->addWidget(sheetRadioButton, 2, 0); + layout->addWidget(drawerRadioButton, 3, 0); + layout->addWidget(popupRadioButton, 0, 1); + layout->addWidget(toolRadioButton, 1, 1); + layout->addWidget(toolTipRadioButton, 2, 1); + layout->addWidget(splashScreenRadioButton, 3, 1); + typeGroupBox->setLayout(layout); +} +//! [5] + +//! [6] +void ControllerWindow::createHintsGroupBox() +{ + hintsGroupBox = new QGroupBox(tr("Hints")); + + msWindowsFixedSizeDialogCheckBox = + createCheckBox(tr("MS Windows fixed size dialog")); + x11BypassWindowManagerCheckBox = + createCheckBox(tr("X11 bypass window manager")); + framelessWindowCheckBox = createCheckBox(tr("Frameless window")); + windowTitleCheckBox = createCheckBox(tr("Window title")); + windowSystemMenuCheckBox = createCheckBox(tr("Window system menu")); + windowMinimizeButtonCheckBox = createCheckBox(tr("Window minimize button")); + windowMaximizeButtonCheckBox = createCheckBox(tr("Window maximize button")); + windowCloseButtonCheckBox = createCheckBox(tr("Window close button")); + windowContextHelpButtonCheckBox = + createCheckBox(tr("Window context help button")); + windowShadeButtonCheckBox = createCheckBox(tr("Window shade button")); + windowStaysOnTopCheckBox = createCheckBox(tr("Window stays on top")); + windowStaysOnBottomCheckBox = createCheckBox(tr("Window stays on bottom")); + customizeWindowHintCheckBox= createCheckBox(tr("Customize window")); + + QGridLayout *layout = new QGridLayout; + layout->addWidget(msWindowsFixedSizeDialogCheckBox, 0, 0); + layout->addWidget(x11BypassWindowManagerCheckBox, 1, 0); + layout->addWidget(framelessWindowCheckBox, 2, 0); + layout->addWidget(windowTitleCheckBox, 3, 0); + layout->addWidget(windowSystemMenuCheckBox, 4, 0); + layout->addWidget(windowMinimizeButtonCheckBox, 0, 1); + layout->addWidget(windowMaximizeButtonCheckBox, 1, 1); + layout->addWidget(windowCloseButtonCheckBox, 2, 1); + layout->addWidget(windowContextHelpButtonCheckBox, 3, 1); + layout->addWidget(windowShadeButtonCheckBox, 4, 1); + layout->addWidget(windowStaysOnTopCheckBox, 5, 1); + layout->addWidget(windowStaysOnBottomCheckBox, 6, 1); + layout->addWidget(customizeWindowHintCheckBox, 5, 0); + hintsGroupBox->setLayout(layout); +} +//! [6] + +//! [7] +QCheckBox *ControllerWindow::createCheckBox(const QString &text) +{ + QCheckBox *checkBox = new QCheckBox(text); + connect(checkBox, SIGNAL(clicked()), this, SLOT(updatePreview())); + return checkBox; +} +//! [7] + +//! [8] +QRadioButton *ControllerWindow::createRadioButton(const QString &text) +{ + QRadioButton *button = new QRadioButton(text); + connect(button, SIGNAL(clicked()), this, SLOT(updatePreview())); + return button; +} +//! [8] diff --git a/src/gui/painting/qregion_wince.cpp b/tests/manual/windowflags/controllerwindow.h index 9c33123..3d315be 100644 --- a/src/gui/painting/qregion_wince.cpp +++ b/tests/manual/windowflags/controllerwindow.h @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the QtGui module of the Qt Toolkit. +** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -39,81 +39,77 @@ ** ****************************************************************************/ -#include "qatomic.h" -#include "qbitmap.h" -#include "qbuffer.h" -#include "qimage.h" -#include "qpolygon.h" -#include "qregion.h" -#include "qt_windows.h" -#include "qpainterpath.h" -#include "qguifunctions_wince.h" +#ifndef CONTROLLERWINDOW_H +#define CONTROLLERWINDOW_H -QT_BEGIN_NAMESPACE +#include <QWidget> -QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0 }; +#include "previewwindow.h" -HRGN qt_tryCreateRegion(QRegion::RegionType type, int left, int top, int right, int bottom) -{ - const int tries = 10; - for (int i = 0; i < tries; ++i) { - HRGN region; - switch (type) { - case QRegion::Rectangle: - region = CreateRectRgn(left, top, right, bottom); - break; - case QRegion::Ellipse: -#ifndef Q_OS_WINCE - region = CreateEllipticRgn(left, top, right, bottom); -#endif - break; - } - if (region) { - if (GetRegionData(region, 0, 0)) - return region; - else - DeleteObject(region); - } - } - return 0; -} +QT_BEGIN_NAMESPACE +class QCheckBox; +class QGroupBox; +class QLabel; +class QPushButton; +class QRadioButton; +class QMainWindow; +QT_END_NAMESPACE -void qt_win_dispose_rgn(HRGN r) +//! [0] +class ControllerWindow : public QWidget { - if (r) - DeleteObject(r); -} + Q_OBJECT -static void qt_add_rect(HRGN &winRegion, QRect r) -{ - HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height()); - if (rgn) { - HRGN dest = CreateRectRgn(0,0,0,0); - int result = CombineRgn(dest, winRegion, rgn, RGN_OR); - if (result) { - DeleteObject(winRegion); - winRegion = dest; - } - } -} +public: + ControllerWindow(); -void QRegion::ensureHandle() const -{ - if (d->rgn) - DeleteObject(d->rgn); - d->rgn = CreateRectRgn(0,0,0,0); - if (d->qt_rgn) { - if (d->qt_rgn->numRects == 1) { - QRect r = d->qt_rgn->extents; - qt_add_rect(d->rgn, r); - return; - } - for (int i = 0;i < d->qt_rgn->numRects;i++) { - QRect r = d->qt_rgn->rects.at(i); - qt_add_rect(d->rgn, r); - } - } -} +private slots: + void updatePreview(); +private: + void createTypeGroupBox(); + void createHintsGroupBox(); + QCheckBox *createCheckBox(const QString &text); + QRadioButton *createRadioButton(const QString &text); -QT_END_NAMESPACE + QMainWindow *parentWindow; + PreviewWindow *previewWindow; + PreviewDialog *previewDialog; + + QGroupBox *widgetTypeGroupBox; + QGroupBox *additionalOptionsGroupBox; + QGroupBox *typeGroupBox; + QGroupBox *hintsGroupBox; + QPushButton *quitButton; + + QRadioButton *previewWidgetButton; + QRadioButton *previewDialogButton; + QCheckBox *modalWindowCheckBox; + QCheckBox *fixedSizeWindowCheckBox; + + QRadioButton *windowRadioButton; + QRadioButton *dialogRadioButton; + QRadioButton *sheetRadioButton; + QRadioButton *drawerRadioButton; + QRadioButton *popupRadioButton; + QRadioButton *toolRadioButton; + QRadioButton *toolTipRadioButton; + QRadioButton *splashScreenRadioButton; + + QCheckBox *msWindowsFixedSizeDialogCheckBox; + QCheckBox *x11BypassWindowManagerCheckBox; + QCheckBox *framelessWindowCheckBox; + QCheckBox *windowTitleCheckBox; + QCheckBox *windowSystemMenuCheckBox; + QCheckBox *windowMinimizeButtonCheckBox; + QCheckBox *windowMaximizeButtonCheckBox; + QCheckBox *windowCloseButtonCheckBox; + QCheckBox *windowContextHelpButtonCheckBox; + QCheckBox *windowShadeButtonCheckBox; + QCheckBox *windowStaysOnTopCheckBox; + QCheckBox *windowStaysOnBottomCheckBox; + QCheckBox *customizeWindowHintCheckBox; +}; +//! [0] + +#endif diff --git a/tests/manual/windowflags/main.cpp b/tests/manual/windowflags/main.cpp new file mode 100644 index 0000000..011ca3f --- /dev/null +++ b/tests/manual/windowflags/main.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> + +#include "controllerwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + ControllerWindow controller; + controller.show(); + return app.exec(); +} diff --git a/tests/manual/windowflags/previewwindow.cpp b/tests/manual/windowflags/previewwindow.cpp new file mode 100644 index 0000000..796bdb9 --- /dev/null +++ b/tests/manual/windowflags/previewwindow.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +#include "previewwindow.h" + +static QString windowFlagsToString(Qt::WindowFlags flags) +{ + QString text; + + Qt::WindowFlags type = (flags & Qt::WindowType_Mask); + if (type == Qt::Window) { + text = "Qt::Window"; + } else if (type == Qt::Dialog) { + text = "Qt::Dialog"; + } else if (type == Qt::Sheet) { + text = "Qt::Sheet"; + } else if (type == Qt::Drawer) { + text = "Qt::Drawer"; + } else if (type == Qt::Popup) { + text = "Qt::Popup"; + } else if (type == Qt::Tool) { + text = "Qt::Tool"; + } else if (type == Qt::ToolTip) { + text = "Qt::ToolTip"; + } else if (type == Qt::SplashScreen) { + text = "Qt::SplashScreen"; + } + + if (flags & Qt::MSWindowsFixedSizeDialogHint) + text += "\n| Qt::MSWindowsFixedSizeDialogHint"; + if (flags & Qt::X11BypassWindowManagerHint) + text += "\n| Qt::X11BypassWindowManagerHint"; + if (flags & Qt::FramelessWindowHint) + text += "\n| Qt::FramelessWindowHint"; + if (flags & Qt::WindowTitleHint) + text += "\n| Qt::WindowTitleHint"; + if (flags & Qt::WindowSystemMenuHint) + text += "\n| Qt::WindowSystemMenuHint"; + if (flags & Qt::WindowMinimizeButtonHint) + text += "\n| Qt::WindowMinimizeButtonHint"; + if (flags & Qt::WindowMaximizeButtonHint) + text += "\n| Qt::WindowMaximizeButtonHint"; + if (flags & Qt::WindowCloseButtonHint) + text += "\n| Qt::WindowCloseButtonHint"; + if (flags & Qt::WindowContextHelpButtonHint) + text += "\n| Qt::WindowContextHelpButtonHint"; + if (flags & Qt::WindowShadeButtonHint) + text += "\n| Qt::WindowShadeButtonHint"; + if (flags & Qt::WindowStaysOnTopHint) + text += "\n| Qt::WindowStaysOnTopHint"; + if (flags & Qt::CustomizeWindowHint) + text += "\n| Qt::CustomizeWindowHint"; + return text; +} + +PreviewWindow::PreviewWindow(QWidget *parent) + : QWidget(parent) +{ + textEdit = new QTextEdit; + textEdit->setReadOnly(true); + textEdit->setLineWrapMode(QTextEdit::NoWrap); + + closeButton = new QPushButton(tr("&Close")); + connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(textEdit); + layout->addWidget(closeButton); + setLayout(layout); + + setWindowTitle(tr("Preview <QWidget>")); +} + +void PreviewWindow::setWindowFlags(Qt::WindowFlags flags) +{ + QWidget::setWindowFlags(flags); + + QString text = windowFlagsToString(flags); + textEdit->setPlainText(text); +} + +PreviewDialog::PreviewDialog(QWidget *parent) + : QDialog(parent) +{ + textEdit = new QTextEdit; + textEdit->setReadOnly(true); + textEdit->setLineWrapMode(QTextEdit::NoWrap); + + closeButton = new QPushButton(tr("&Close")); + connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); + + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(textEdit); + layout->addWidget(closeButton); + setLayout(layout); + + setWindowTitle(tr("Preview <QDialog>")); +} + +void PreviewDialog::setWindowFlags(Qt::WindowFlags flags) +{ + QWidget::setWindowFlags(flags); + + QString text = windowFlagsToString(flags); + textEdit->setPlainText(text); +} diff --git a/tests/manual/windowflags/previewwindow.h b/tests/manual/windowflags/previewwindow.h new file mode 100644 index 0000000..90ed8ba --- /dev/null +++ b/tests/manual/windowflags/previewwindow.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PREVIEWWINDOW_H +#define PREVIEWWINDOW_H + +#include <QWidget> +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QPushButton; +class QTextEdit; +QT_END_NAMESPACE + +class PreviewWindow : public QWidget +{ + Q_OBJECT + +public: + PreviewWindow(QWidget *parent = 0); + + void setWindowFlags(Qt::WindowFlags flags); + +private: + QTextEdit *textEdit; + QPushButton *closeButton; +}; + +class PreviewDialog : public QDialog +{ + Q_OBJECT + +public: + PreviewDialog(QWidget *parent = 0); + + void setWindowFlags(Qt::WindowFlags flags); + +private: + QTextEdit *textEdit; + QPushButton *closeButton; +}; + +#endif diff --git a/tests/manual/windowflags/windowflags.pro b/tests/manual/windowflags/windowflags.pro new file mode 100644 index 0000000..1b45d0d --- /dev/null +++ b/tests/manual/windowflags/windowflags.pro @@ -0,0 +1,5 @@ +HEADERS = controllerwindow.h \ + previewwindow.h +SOURCES = controllerwindow.cpp \ + previewwindow.cpp \ + main.cpp diff --git a/tools/assistant/lib/qhelpprojectdata.cpp b/tools/assistant/lib/qhelpprojectdata.cpp index 55b4ea7..9412f1c 100644 --- a/tools/assistant/lib/qhelpprojectdata.cpp +++ b/tools/assistant/lib/qhelpprojectdata.cpp @@ -45,6 +45,7 @@ #include <QtCore/QFileInfo> #include <QtCore/QStack> #include <QtCore/QMap> +#include <QtCore/QRegExp> #include <QtCore/QVariant> #include <QtXml/QXmlStreamReader> @@ -75,6 +76,8 @@ private: void readFiles(); void raiseUnknownTokenError(); void addMatchingFiles(const QString &pattern); + + QMap<QString, QStringList> dirEntriesCache; }; void QHelpProjectDataPrivate::raiseUnknownTokenError() @@ -265,14 +268,40 @@ void QHelpProjectDataPrivate::readFiles() // meaningful warning later. void QHelpProjectDataPrivate::addMatchingFiles(const QString &pattern) { + // The pattern matching is expensive, so we skip it if no + // wildcard symbols occur in the string. + if (!pattern.contains('?') && !pattern.contains('*') + && !pattern.contains('[') && !pattern.contains(']')) { + filterSectionList.last().addFile(pattern); + return; + } + QFileInfo fileInfo(rootPath + '/' + pattern); - const QStringList &matches = - fileInfo.dir().entryList(QStringList(fileInfo.fileName())); - for (QStringList::ConstIterator it = matches.constBegin(); - it != matches.constEnd(); - ++it) - filterSectionList.last().addFile(QFileInfo(pattern).dir().path() + '/' + *it); - if (matches.empty()) + const QDir &dir = fileInfo.dir(); + const QString &path = dir.canonicalPath(); + + // QDir::entryList() is expensive, so we cache the results. + QMap<QString, QStringList>::ConstIterator it = dirEntriesCache.find(path); + const QStringList &entries = it != dirEntriesCache.constEnd() ? + it.value() : dir.entryList(QDir::Files); + if (it == dirEntriesCache.constEnd()) + dirEntriesCache.insert(path, entries); + + bool matchFound = false; +#ifdef Q_OS_WIN + Qt::CaseSensitivity cs = Qt::CaseInsensitive; +#else + Qt::CaseSensitivity cs = Qt::CaseSensitive; +#endif + QRegExp regExp(fileInfo.fileName(), cs, QRegExp::Wildcard); + foreach (const QString &file, entries) { + if (regExp.exactMatch(file)) { + matchFound = true; + filterSectionList.last(). + addFile(QFileInfo(pattern).dir().path() + '/' + file); + } + } + if (!matchFound) filterSectionList.last().addFile(pattern); } diff --git a/tools/assistant/lib/qhelpsearchquerywidget.cpp b/tools/assistant/lib/qhelpsearchquerywidget.cpp index 00444b1..110df4f 100644 --- a/tools/assistant/lib/qhelpsearchquerywidget.cpp +++ b/tools/assistant/lib/qhelpsearchquerywidget.cpp @@ -43,9 +43,12 @@ #include <QtCore/QDebug> +#include <QtCore/QAbstractListModel> #include <QtCore/QObject> #include <QtCore/QStringList> +#include <QtCore/QtGlobal> +#include <QtGui/QCompleter> #include <QtGui/QLabel> #include <QtGui/QLayout> #include <QtGui/QLineEdit> @@ -60,8 +63,46 @@ class QHelpSearchQueryWidgetPrivate : public QObject Q_OBJECT private: + struct QueryHistory { + explicit QueryHistory() : curQuery(-1) {} + QList<QList<QHelpSearchQuery> > queries; + int curQuery; + }; + + class CompleterModel : public QAbstractListModel + { + public: + explicit CompleterModel(QObject *parent) + : QAbstractListModel(parent) {} + + int rowCount(const QModelIndex &parent = QModelIndex()) const + { + return parent.isValid() ? 0 : termList.size(); + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const + { + if (!index.isValid() || index.row() >= termList.count()|| + (role != Qt::EditRole && role != Qt::DisplayRole)) + return QVariant(); + return termList.at(index.row()); + } + + void addTerm(const QString &term) + { + if (!termList.contains(term)) { + termList.append(term); + reset(); + } + } + + private: + QStringList termList; + }; + QHelpSearchQueryWidgetPrivate() - : QObject() + : QObject(), simpleSearch(true), + searchCompleter(new CompleterModel(this), this) { searchButton = 0; advancedSearchWidget = 0; @@ -136,11 +177,102 @@ private: return wordList; } + void saveQuery(const QList<QHelpSearchQuery> &query, QueryHistory &queryHist) + { + // We only add the query to the list if it is different from the last one. + bool insert = false; + if (queryHist.queries.empty()) + insert = true; + else { + const QList<QHelpSearchQuery> &lastQuery = queryHist.queries.last(); + if (lastQuery.size() != query.size()) { + insert = true; + } else { + for (int i = 0; i < query.size(); ++i) { + if (query.at(i).fieldName != lastQuery.at(i).fieldName + || query.at(i).wordList != lastQuery.at(i).wordList) { + insert = true; + break; + } + } + } + } + if (insert) { + queryHist.queries.append(query); + foreach (const QHelpSearchQuery &queryPart, query) { + static_cast<CompleterModel *>(searchCompleter.model())-> + addTerm(queryPart.wordList.join(" ")); + } + } + } + + void nextOrPrevQuery(int maxOrMinIndex, int addend, + QToolButton *thisButton, QToolButton *otherButton) + { + QueryHistory *queryHist; + QList<QLineEdit *> lineEdits; + if (simpleSearch) { + queryHist = &simpleQueries; + lineEdits << defaultQuery; + } else { + queryHist = &complexQueries; + lineEdits << allQuery << atLeastQuery << similarQuery + << withoutQuery << exactQuery; + } + foreach (QLineEdit *lineEdit, lineEdits) + lineEdit->clear(); + + // Otherwise, the respective button would be disabled. + Q_ASSERT(queryHist->curQuery != maxOrMinIndex); + + queryHist->curQuery += addend; + const QList<QHelpSearchQuery> &query = + queryHist->queries.at(queryHist->curQuery); + foreach (const QHelpSearchQuery &queryPart, query) { + QLineEdit *lineEdit; + switch (queryPart.fieldName) { + case QHelpSearchQuery::DEFAULT: + lineEdit = defaultQuery; + break; + case QHelpSearchQuery::ALL: + lineEdit = allQuery; + break; + case QHelpSearchQuery::ATLEAST: + lineEdit = atLeastQuery; + break; + case QHelpSearchQuery::FUZZY: + lineEdit = similarQuery; + break; + case QHelpSearchQuery::WITHOUT: + lineEdit = withoutQuery; + break; + case QHelpSearchQuery::PHRASE: + lineEdit = exactQuery; + break; + default: + Q_ASSERT(0); + } + lineEdit->setText(queryPart.wordList.join(" ")); + } + + if (queryHist->curQuery == maxOrMinIndex) + thisButton->setEnabled(false); + otherButton->setEnabled(true); + } + + void enableOrDisableToolButtons() + { + const QueryHistory &queryHist = + simpleSearch ? simpleQueries : complexQueries; + prevQueryButton->setEnabled(queryHist.curQuery > 0); + nextQueryButton->setEnabled(queryHist.curQuery < + queryHist.queries.size() - 1); + } + private slots: void showHideAdvancedSearch() { - bool hidden = advancedSearchWidget->isHidden(); - if (hidden) { + if (simpleSearch) { advancedSearchWidget->show(); showHideAdvancedSearchButton->setText((QLatin1String("-"))); } else { @@ -148,12 +280,86 @@ private slots: showHideAdvancedSearchButton->setText((QLatin1String("+"))); } - defaultQuery->setEnabled(!hidden); + simpleSearch = !simpleSearch; + defaultQuery->setEnabled(simpleSearch); + enableOrDisableToolButtons(); + } + + void searchRequested() + { + QList<QHelpSearchQuery> queryList; +#if !defined(QT_CLUCENE_SUPPORT) + queryList.append(QHelSearchQuery(QHelpSearchQuery::DEFAULT, + QStringList(defaultQuery->text()))); + +#else + if (defaultQuery->isEnabled()) { + queryList.append(QHelpSearchQuery(QHelpSearchQuery::DEFAULT, + buildTermList(escapeString(defaultQuery->text())))); + } else { + const QRegExp exp(QLatin1String("\\s+")); + QStringList lst = similarQuery->text().split(exp, QString::SkipEmptyParts); + if (!lst.isEmpty()) { + QStringList fuzzy; + foreach (const QString term, lst) + fuzzy += buildTermList(escapeString(term)); + queryList.append(QHelpSearchQuery(QHelpSearchQuery::FUZZY, fuzzy)); + } + + lst = withoutQuery->text().split(exp, QString::SkipEmptyParts); + if (!lst.isEmpty()) { + QStringList without; + foreach (const QString term, lst) + without.append(escapeString(term)); + queryList.append(QHelpSearchQuery(QHelpSearchQuery::WITHOUT, without)); + } + + if (!exactQuery->text().isEmpty()) { + QString phrase = exactQuery->text().remove(QLatin1Char('\"')); + phrase = escapeString(phrase.simplified()); + queryList.append(QHelpSearchQuery(QHelpSearchQuery::PHRASE, QStringList(phrase))); + } + + lst = allQuery->text().split(exp, QString::SkipEmptyParts); + if (!lst.isEmpty()) { + QStringList all; + foreach (const QString term, lst) + all.append(escapeString(term)); + queryList.append(QHelpSearchQuery(QHelpSearchQuery::ALL, all)); + } + + lst = atLeastQuery->text().split(exp, QString::SkipEmptyParts); + if (!lst.isEmpty()) { + QStringList atLeast; + foreach (const QString term, lst) + atLeast += buildTermList(escapeString(term)); + queryList.append(QHelpSearchQuery(QHelpSearchQuery::ATLEAST, atLeast)); + } + } +#endif + QueryHistory &queryHist = simpleSearch ? simpleQueries : complexQueries; + saveQuery(queryList, queryHist); + queryHist.curQuery = queryHist.queries.size() - 1; + if (queryHist.curQuery > 0) + prevQueryButton->setEnabled(true); + nextQueryButton->setEnabled(false); + } + + void nextQuery() + { + nextOrPrevQuery((simpleSearch ? simpleQueries : complexQueries).queries.size() - 1, + 1, nextQueryButton, prevQueryButton); + } + + void prevQuery() + { + nextOrPrevQuery(0, -1, prevQueryButton, nextQueryButton); } private: friend class QHelpSearchQueryWidget; + bool simpleSearch; QPushButton *searchButton; QWidget* advancedSearchWidget; QToolButton *showHideAdvancedSearchButton; @@ -163,6 +369,11 @@ private: QLineEdit *withoutQuery; QLineEdit *allQuery; QLineEdit *atLeastQuery; + QToolButton *nextQueryButton; + QToolButton *prevQueryButton; + QueryHistory simpleQueries; + QueryHistory complexQueries; + QCompleter searchCompleter; }; #include "qhelpsearchquerywidget.moc" @@ -199,13 +410,26 @@ QHelpSearchQueryWidget::QHelpSearchQueryWidget(QWidget *parent) QHBoxLayout* hBoxLayout = new QHBoxLayout(); QLabel *label = new QLabel(tr("Search for:"), this); d->defaultQuery = new QLineEdit(this); + d->defaultQuery->setCompleter(&d->searchCompleter); + d->prevQueryButton = new QToolButton(this); + d->prevQueryButton->setArrowType(Qt::LeftArrow); + d->prevQueryButton->setToolTip(tr("Previous search")); + d->prevQueryButton->setEnabled(false); + d->nextQueryButton = new QToolButton(this); + d->nextQueryButton->setArrowType(Qt::RightArrow); + d->nextQueryButton->setToolTip(tr("Next search")); + d->nextQueryButton->setEnabled(false); d->searchButton = new QPushButton(tr("Search"), this); hBoxLayout->addWidget(label); hBoxLayout->addWidget(d->defaultQuery); + hBoxLayout->addWidget(d->prevQueryButton); + hBoxLayout->addWidget(d->nextQueryButton); hBoxLayout->addWidget(d->searchButton); vLayout->addLayout(hBoxLayout); + connect(d->prevQueryButton, SIGNAL(clicked()), d, SLOT(prevQuery())); + connect(d->nextQueryButton, SIGNAL(clicked()), d, SLOT(nextQuery())); connect(d->searchButton, SIGNAL(clicked()), this, SIGNAL(search())); connect(d->defaultQuery, SIGNAL(returnPressed()), this, SIGNAL(search())); @@ -236,26 +460,31 @@ QHelpSearchQueryWidget::QHelpSearchQueryWidget(QWidget *parent) label = new QLabel(tr("words <B>similar</B> to:"), this); gLayout->addWidget(label, 0, 0); d->similarQuery = new QLineEdit(this); + d->similarQuery->setCompleter(&d->searchCompleter); gLayout->addWidget(d->similarQuery, 0, 1); label = new QLabel(tr("<B>without</B> the words:"), this); gLayout->addWidget(label, 1, 0); d->withoutQuery = new QLineEdit(this); + d->withoutQuery->setCompleter(&d->searchCompleter); gLayout->addWidget(d->withoutQuery, 1, 1); label = new QLabel(tr("with <B>exact phrase</B>:"), this); gLayout->addWidget(label, 2, 0); d->exactQuery = new QLineEdit(this); + d->exactQuery->setCompleter(&d->searchCompleter); gLayout->addWidget(d->exactQuery, 2, 1); label = new QLabel(tr("with <B>all</B> of the words:"), this); gLayout->addWidget(label, 3, 0); d->allQuery = new QLineEdit(this); + d->allQuery->setCompleter(&d->searchCompleter); gLayout->addWidget(d->allQuery, 3, 1); label = new QLabel(tr("with <B>at least one</B> of the words:"), this); gLayout->addWidget(label, 4, 0); d->atLeastQuery = new QLineEdit(this); + d->atLeastQuery->setCompleter(&d->searchCompleter); gLayout->addWidget(d->atLeastQuery, 4, 1); vLayout->addWidget(d->advancedSearchWidget); @@ -269,6 +498,7 @@ QHelpSearchQueryWidget::QHelpSearchQueryWidget(QWidget *parent) connect(d->showHideAdvancedSearchButton, SIGNAL(clicked()), d, SLOT(showHideAdvancedSearch())); #endif + connect(this, SIGNAL(search()), d, SLOT(searchRequested())); } /*! @@ -285,59 +515,10 @@ QHelpSearchQueryWidget::~QHelpSearchQueryWidget() */ QList<QHelpSearchQuery> QHelpSearchQueryWidget::query() const { -#if !defined(QT_CLUCENE_SUPPORT) - QList<QHelpSearchQuery> queryList; - queryList.append(QHelpSearchQuery(QHelpSearchQuery::DEFAULT, - QStringList(d->defaultQuery->text()))); - - return queryList; -#else - QList<QHelpSearchQuery> queryList; - if (d->defaultQuery->isEnabled()) { - queryList.append(QHelpSearchQuery(QHelpSearchQuery::DEFAULT, - d->buildTermList(d->escapeString(d->defaultQuery->text())))); - } else { - const QRegExp exp(QLatin1String("\\s+")); - QStringList lst = d->similarQuery->text().split(exp, QString::SkipEmptyParts); - if (!lst.isEmpty()) { - QStringList fuzzy; - foreach (const QString term, lst) - fuzzy += d->buildTermList(d->escapeString(term)); - queryList.append(QHelpSearchQuery(QHelpSearchQuery::FUZZY, fuzzy)); - } - - lst = d->withoutQuery->text().split(exp, QString::SkipEmptyParts); - if (!lst.isEmpty()) { - QStringList without; - foreach (const QString term, lst) - without.append(d->escapeString(term)); - queryList.append(QHelpSearchQuery(QHelpSearchQuery::WITHOUT, without)); - } - - if (!d->exactQuery->text().isEmpty()) { - QString phrase = d->exactQuery->text().remove(QLatin1Char('\"')); - phrase = d->escapeString(phrase.simplified()); - queryList.append(QHelpSearchQuery(QHelpSearchQuery::PHRASE, QStringList(phrase))); - } - - lst = d->allQuery->text().split(exp, QString::SkipEmptyParts); - if (!lst.isEmpty()) { - QStringList all; - foreach (const QString term, lst) - all.append(d->escapeString(term)); - queryList.append(QHelpSearchQuery(QHelpSearchQuery::ALL, all)); - } - - lst = d->atLeastQuery->text().split(exp, QString::SkipEmptyParts); - if (!lst.isEmpty()) { - QStringList atLeast; - foreach (const QString term, lst) - atLeast += d->buildTermList(d->escapeString(term)); - queryList.append(QHelpSearchQuery(QHelpSearchQuery::ATLEAST, atLeast)); - } - } - return queryList; -#endif + const QHelpSearchQueryWidgetPrivate::QueryHistory &queryHist = + d->simpleSearch ? d->simpleQueries : d->complexQueries; + return queryHist.queries.isEmpty() ? + QList<QHelpSearchQuery>() : queryHist.queries.last(); } /*! \reimp diff --git a/tools/assistant/tools/assistant/assistant.qch b/tools/assistant/tools/assistant/assistant.qch Binary files differindex 99687ed..3e66bd9 100644 --- a/tools/assistant/tools/assistant/assistant.qch +++ b/tools/assistant/tools/assistant/assistant.qch diff --git a/tools/assistant/tools/assistant/centralwidget.cpp b/tools/assistant/tools/assistant/centralwidget.cpp index a384544..23562db 100644 --- a/tools/assistant/tools/assistant/centralwidget.cpp +++ b/tools/assistant/tools/assistant/centralwidget.cpp @@ -43,6 +43,7 @@ #include "helpviewer.h" #include "searchwidget.h" #include "mainwindow.h" +#include "preferencesdialog.h" #include <QtCore/QDir> #include <QtCore/QEvent> @@ -423,6 +424,27 @@ void CentralWidget::setSource(const QUrl &url) tabWidget->setTabText(lastTabPage, quoteTabTitle(viewer->documentTitle())); } +void CentralWidget::setupWidget() +{ + int option = helpEngine->customValue(QLatin1String("StartOption"), + ShowLastPages).toInt(); + + if (option != ShowLastPages) { + QString homePage; + if (option == ShowHomePage) { + homePage = helpEngine->customValue(QLatin1String("defaultHomepage"), + QLatin1String("help")).toString(); + homePage = helpEngine->customValue(QLatin1String("homepage"), + homePage).toString(); + } + if (option == ShowBlankPage) + homePage = QLatin1String("about:blank"); + setSource(homePage); + } else { + setLastShownPages(); + } +} + void CentralWidget::setLastShownPages() { const QLatin1String key("LastShownPages"); @@ -739,8 +761,10 @@ void CentralWidget::setTabTitle(const QUrl &url) QTabBar *tabBar = qFindChild<QTabBar*>(tabWidget); for (int tab = 0; tab < tabBar->count(); ++tab) { HelpViewer *viewer = qobject_cast<HelpViewer*>(tabWidget->widget(tab)); - if (viewer) - tabWidget->setTabText(tab, viewer->documentTitle().trimmed()); + if (viewer) { + tabWidget->setTabText(tab, + quoteTabTitle(viewer->documentTitle().trimmed())); + } } #else HelpViewer *viewer = currentHelpViewer(); diff --git a/tools/assistant/tools/assistant/centralwidget.h b/tools/assistant/tools/assistant/centralwidget.h index ca9caaf..f7ce1d5 100644 --- a/tools/assistant/tools/assistant/centralwidget.h +++ b/tools/assistant/tools/assistant/centralwidget.h @@ -111,7 +111,7 @@ public: CentralWidget(QHelpEngine *engine, MainWindow *parent); ~CentralWidget(); - void setLastShownPages(); + void setupWidget(); bool hasSelection() const; QUrl currentSource() const; QString currentTitle() const; @@ -188,6 +188,7 @@ private: void initPrinter(); QString quoteTabTitle(const QString &title) const; void highlightSearchTerms(); + void setLastShownPages(); private: int lastTabPage; diff --git a/tools/assistant/tools/assistant/doc/assistant.qhp b/tools/assistant/tools/assistant/doc/assistant.qhp index 7a26101..7ea4cdd 100644 --- a/tools/assistant/tools/assistant/doc/assistant.qhp +++ b/tools/assistant/tools/assistant/doc/assistant.qhp @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <QtHelpProject version="1.0"> <virtualFolder>assistant</virtualFolder> - <namespace>com.trolltech.com.assistantinternal_1.0.0</namespace> + <namespace>com.trolltech.com.assistantinternal-1.0.0</namespace> <filterSection> <files> <file>assistant.html</file> diff --git a/tools/assistant/tools/assistant/helpviewer.cpp b/tools/assistant/tools/assistant/helpviewer.cpp index 6799db3..5422bf46 100644 --- a/tools/assistant/tools/assistant/helpviewer.cpp +++ b/tools/assistant/tools/assistant/helpviewer.cpp @@ -306,7 +306,7 @@ void HelpViewer::setSource(const QUrl &url) loadFinished = false; if (url.toString() == QLatin1String("help")) { load(QUrl(QLatin1String("qthelp://com.trolltech.com." - "assistantinternal_1.0.0/assistant/assistant.html"))); + "assistantinternal-1.0.0/assistant/assistant.html"))); } else { load(url); } @@ -425,7 +425,7 @@ void HelpViewer::setSource(const QUrl &url) if (help) { QTextBrowser::setSource(QUrl(QLatin1String("qthelp://com.trolltech.com." - "assistantinternal_1.0.0/assistant/assistant.html"))); + "assistantinternal-1.0.0/assistant/assistant.html"))); } else { QTextBrowser::setSource(url); setHtml(tr("<title>Error 404...</title><div align=\"center\"><br><br>" diff --git a/tools/assistant/tools/assistant/main.cpp b/tools/assistant/tools/assistant/main.cpp index 05355d9..4af2570 100644 --- a/tools/assistant/tools/assistant/main.cpp +++ b/tools/assistant/tools/assistant/main.cpp @@ -181,7 +181,21 @@ QString indexFilesFolder(const QString &collectionFile) int main(int argc, char *argv[]) { - QApplication a(argc, argv); + // First do a quick search for arguments that imply command-line mode. + const char * cmdModeArgs[] = { + "-help", "-register", "-unregister", "-remove-search-index" + }; + bool useGui = true; + for (int i = 1; i < argc; ++i) { + for (size_t j = 0; j < sizeof cmdModeArgs/sizeof *cmdModeArgs; ++j) { + if(strcmp(argv[i], cmdModeArgs[j]) == 0) { + useGui = false; + break; + } + } + } + + QApplication a(argc, argv, useGui); a.addLibraryPath(a.applicationDirPath() + QLatin1String("/plugins")); CmdLineParser cmd; @@ -306,7 +320,7 @@ int main(int argc, char *argv[]) } } - QLatin1String intern("com.trolltech.com.assistantinternal_"); + QLatin1String intern("com.trolltech.com.assistantinternal-"); foreach (const QString &doc, userDocs) { if (!callerDocs.contains(doc) && !doc.startsWith(intern)) user.unregisterDocumentation(doc); diff --git a/tools/assistant/tools/assistant/mainwindow.cpp b/tools/assistant/tools/assistant/mainwindow.cpp index 7926020..bc73b80 100644 --- a/tools/assistant/tools/assistant/mainwindow.cpp +++ b/tools/assistant/tools/assistant/mainwindow.cpp @@ -274,7 +274,7 @@ bool MainWindow::initHelpDB() return false; bool assistantInternalDocRegistered = false; - QString intern(QLatin1String("com.trolltech.com.assistantinternal_")); + QString intern(QLatin1String("com.trolltech.com.assistantinternal-")); foreach (const QString &ns, m_helpEngine->registeredDocumentations()) { if (ns.startsWith(intern)) { intern = ns; @@ -407,7 +407,7 @@ void MainWindow::insertLastPages() if (m_cmdLine->url().isValid()) m_centralWidget->setSource(m_cmdLine->url()); else - m_centralWidget->setLastShownPages(); + m_centralWidget->setupWidget(); if (m_cmdLine->search() == CmdLineParser::Activate) showSearch(); diff --git a/tools/assistant/tools/assistant/preferencesdialog.cpp b/tools/assistant/tools/assistant/preferencesdialog.cpp index fa70196..2b58c64 100644 --- a/tools/assistant/tools/assistant/preferencesdialog.cpp +++ b/tools/assistant/tools/assistant/preferencesdialog.cpp @@ -146,10 +146,13 @@ PreferencesDialog::~PreferencesDialog() emit updateBrowserFont(); } - if (!m_ui.homePageLineEdit->text().isEmpty()) { - key = QLatin1String("homepage"); - m_helpEngine->setCustomValue(key, m_ui.homePageLineEdit->text()); - } + QString homePage = m_ui.homePageLineEdit->text(); + if (homePage.isEmpty()) + homePage = QLatin1String("help"); + m_helpEngine->setCustomValue(QLatin1String("homepage"), homePage); + + int option = m_ui.helpStartComboBox->currentIndex(); + m_helpEngine->setCustomValue(QLatin1String("StartOption"), option); } void PreferencesDialog::showDialog() @@ -379,6 +382,8 @@ void PreferencesDialog::applyChanges() CentralWidget* widget = CentralWidget::instance(); for (int i = m_TabsToClose.count(); --i >= 0;) widget->closeTabAt(m_TabsToClose.at(i)); + if (widget->availableHelpViewer()== 0) + widget->setSource(QUrl(QLatin1String("about:blank"))); if (m_unregDocs.count()) { foreach (const QString &doc, m_unregDocs) @@ -483,22 +488,23 @@ void PreferencesDialog::updateOptionsPage() homepage = m_helpEngine->customValue(QLatin1String("defaultHomepage"), QLatin1String("help")).toString(); } - m_ui.homePageLineEdit->setText(homepage); - connect(m_ui.currentPageButton, SIGNAL(pressed()), this, - SLOT(currentHomepageChanged())); - connect(m_ui.restoreDefaultHomePageButton, SIGNAL(pressed()), this, - SLOT(restoreDefaultHomepage())); + + int option = m_helpEngine->customValue(QLatin1String("StartOption"), + ShowLastPages).toInt(); + m_ui.helpStartComboBox->setCurrentIndex(option); + + connect(m_ui.blankPageButton, SIGNAL(clicked()), this, SLOT(setBlankPage())); + connect(m_ui.currentPageButton, SIGNAL(clicked()), this, SLOT(setCurrentPage())); + connect(m_ui.defaultPageButton, SIGNAL(clicked()), this, SLOT(setDefaultPage())); } -void PreferencesDialog::restoreDefaultHomepage() +void PreferencesDialog::setBlankPage() { - QString homepage = m_helpEngine->customValue(QLatin1String("defaultHomepage"), - QLatin1String("help")).toString(); - m_ui.homePageLineEdit->setText(homepage); + m_ui.homePageLineEdit->setText(QLatin1String("about:blank")); } -void PreferencesDialog::currentHomepageChanged() +void PreferencesDialog::setCurrentPage() { QString homepage = CentralWidget::instance()->currentSource().toString(); if (homepage.isEmpty()) @@ -507,4 +513,11 @@ void PreferencesDialog::currentHomepageChanged() m_ui.homePageLineEdit->setText(homepage); } +void PreferencesDialog::setDefaultPage() +{ + QString homepage = m_helpEngine->customValue(QLatin1String("defaultHomepage"), + QLatin1String("help")).toString(); + m_ui.homePageLineEdit->setText(homepage); +} + QT_END_NAMESPACE diff --git a/tools/assistant/tools/assistant/preferencesdialog.h b/tools/assistant/tools/assistant/preferencesdialog.h index 5b8ffe8..4471b5f 100644 --- a/tools/assistant/tools/assistant/preferencesdialog.h +++ b/tools/assistant/tools/assistant/preferencesdialog.h @@ -50,6 +50,12 @@ QT_BEGIN_NAMESPACE class FontPanel; class QHelpEngineCore; +enum { + ShowHomePage = 0, + ShowBlankPage = 1, + ShowLastPages = 2 +}; + class PreferencesDialog : public QDialog { Q_OBJECT @@ -72,8 +78,10 @@ private slots: void appFontSettingChanged(int index); void browserFontSettingToggled(bool on); void browserFontSettingChanged(int index); - void restoreDefaultHomepage(); - void currentHomepageChanged(); + + void setBlankPage(); + void setCurrentPage(); + void setDefaultPage(); signals: void updateBrowserFont(); diff --git a/tools/assistant/tools/assistant/preferencesdialog.ui b/tools/assistant/tools/assistant/preferencesdialog.ui index d848b49..279084d 100644 --- a/tools/assistant/tools/assistant/preferencesdialog.ui +++ b/tools/assistant/tools/assistant/preferencesdialog.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>359</width> + <width>375</width> <height>266</height> </rect> </property> @@ -187,19 +187,90 @@ <attribute name="title"> <string>Options</string> </attribute> - <layout class="QVBoxLayout" name="verticalLayout_3"> + <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <widget class="QGroupBox" name="groupBox"> + <widget class="QGroupBox" name="groupBox_2"> <property name="title"> - <string>Homepage</string> + <string/> </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> - <widget class="QLineEdit" name="homePageLineEdit"/> + <widget class="QLabel" name="label_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>On help start:</string> + </property> + </widget> </item> <item> + <widget class="QComboBox" name="helpStartComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <item> + <property name="text"> + <string>Show my home page</string> + </property> + </item> + <item> + <property name="text"> + <string>Show a blank page</string> + </property> + </item> + <item> + <property name="text"> + <string>Show my tabs from last session</string> + </property> + </item> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>54</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string/> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Homepage</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="homePageLineEdit"/> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> @@ -213,22 +284,25 @@ </spacer> </item> <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QPushButton" name="currentPageButton"> - <property name="text"> - <string>Current Page</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="restoreDefaultHomePageButton"> - <property name="text"> - <string>Restore to default</string> - </property> - </widget> - </item> - </layout> + <widget class="QPushButton" name="currentPageButton"> + <property name="text"> + <string>Current Page</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="blankPageButton"> + <property name="text"> + <string>Blank Page</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="defaultPageButton"> + <property name="text"> + <string>Restore to default</string> + </property> + </widget> </item> </layout> </item> diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index c938919..f32e7dc 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -245,6 +245,7 @@ Configure::Configure( int& argc, char** argv ) dictionary[ "XMLPATTERNS" ] = "auto"; dictionary[ "PHONON" ] = "auto"; dictionary[ "PHONON_BACKEND" ] = "yes"; + dictionary[ "MULTIMEDIA" ] = "yes"; dictionary[ "DIRECTSHOW" ] = "no"; dictionary[ "WEBKIT" ] = "auto"; dictionary[ "PLUGIN_MANIFESTS" ] = "yes"; @@ -843,6 +844,10 @@ void Configure::parseCmdLine() dictionary[ "XMLPATTERNS" ] = "no"; } else if( configCmdLine.at(i) == "-xmlpatterns" ) { dictionary[ "XMLPATTERNS" ] = "yes"; + } else if( configCmdLine.at(i) == "-no-multimedia" ) { + dictionary[ "MULTIMEDIA" ] = "no"; + } else if( configCmdLine.at(i) == "-multimedia" ) { + dictionary[ "MULTIMEDIA" ] = "yes"; } else if( configCmdLine.at(i) == "-no-phonon" ) { dictionary[ "PHONON" ] = "no"; } else if( configCmdLine.at(i) == "-phonon" ) { @@ -1425,6 +1430,7 @@ bool Configure::displayHelp() "[-no-openssl] [-no-dbus] [-dbus] [-dbus-linked] [-platform <spec>]\n" "[-qtnamespace <namespace>] [-qtlibinfix <infix>] [-no-phonon]\n" "[-phonon] [-no-phonon-backend] [-phonon-backend]\n" + "[-no-multimedia] [-multimedia]\n" "[-no-webkit] [-webkit]\n" "[-no-scripttools] [-scripttools]\n" "[-graphicssystem raster|opengl]\n\n", 0, 7); @@ -1599,6 +1605,8 @@ bool Configure::displayHelp() desc("PHONON", "yes", "-phonon", "Compile the Phonon module (Phonon is built if a decent C++ compiler is used.)"); desc("PHONON_BACKEND","no", "-no-phonon-backend","Do not compile the platform-specific Phonon backend-plugin"); desc("PHONON_BACKEND","yes","-phonon-backend", "Compile in the platform-specific Phonon backend-plugin"); + desc("MULTIMEDIA", "no", "-no-multimedia", "Do not compile the multimedia module"); + desc("MULTIMEDIA", "yes","-multimedia", "Compile in multimedia module"); desc("WEBKIT", "no", "-no-webkit", "Do not compile in the WebKit module"); desc("WEBKIT", "yes", "-webkit", "Compile in the WebKit module (WebKit is built if a decent C++ compiler is used.)"); desc("SCRIPTTOOLS", "no", "-no-scripttools", "Do not build the QtScriptTools module."); @@ -1867,6 +1875,8 @@ bool Configure::checkAvailability(const QString &part) if (!findFile("msdmo.lib")) cout << "msdmo.lib not found" << endl; if (!findFile("d3d9.h")) cout << "d3d9.h not found" << endl; } + } else if (part == "MULTIMEDIA") { + available = true; } else if (part == "WEBKIT") { available = (dictionary.value("QMAKESPEC") == "win32-msvc2005") || (dictionary.value("QMAKESPEC") == "win32-msvc2008") || (dictionary.value("QMAKESPEC") == "win32-g++"); } else if (part == "SCRIPTTOOLS") { @@ -2302,6 +2312,9 @@ void Configure::generateOutputVars() qtConfig += "phonon-backend"; } + if (dictionary["MULTIMEDIA"] == "yes") + qtConfig += "multimedia"; + if (dictionary["WEBKIT"] == "yes") qtConfig += "webkit"; @@ -2662,6 +2675,7 @@ void Configure::generateConfigfiles() if(dictionary["IPV6"] == "no") qconfigList += "QT_NO_IPV6"; if(dictionary["WEBKIT"] == "no") qconfigList += "QT_NO_WEBKIT"; if(dictionary["PHONON"] == "no") qconfigList += "QT_NO_PHONON"; + if(dictionary["MULTIMEDIA"] == "no") qconfigList += "QT_NO_MULTIMEDIA"; if(dictionary["XMLPATTERNS"] == "no") qconfigList += "QT_NO_XMLPATTERNS"; if(dictionary["SCRIPTTOOLS"] == "no") qconfigList += "QT_NO_SCRIPTTOOLS"; @@ -2920,6 +2934,7 @@ void Configure::displayConfig() cout << "QtDBus support.............." << dictionary[ "DBUS" ] << endl; cout << "QtXmlPatterns support......." << dictionary[ "XMLPATTERNS" ] << endl; cout << "Phonon support.............." << dictionary[ "PHONON" ] << endl; + cout << "Multimedia support.........." << dictionary[ "MULTIMEDIA" ] << endl; cout << "WebKit support.............." << dictionary[ "WEBKIT" ] << endl; cout << "QtScriptTools support......." << dictionary[ "SCRIPTTOOLS" ] << endl; cout << "Graphics System............." << dictionary[ "GRAPHICS_SYSTEM" ] << endl; diff --git a/tools/linguist/lupdate/cpp.cpp b/tools/linguist/lupdate/cpp.cpp index 58e094b..21b230e 100644 --- a/tools/linguist/lupdate/cpp.cpp +++ b/tools/linguist/lupdate/cpp.cpp @@ -1676,7 +1676,9 @@ void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions) case Tok_trid: if (!results->tor) goto case_default; - if (!sourcetext.isEmpty()) { + if (sourcetext.isEmpty()) { + yyTok = getToken(); + } else { if (!msgid.isEmpty()) qWarning("%s:%d: //= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n", qPrintable(yyFileName), yyLineNo); diff --git a/tools/linguist/shared/po.cpp b/tools/linguist/shared/po.cpp index 796d012..a6795cb 100644 --- a/tools/linguist/shared/po.cpp +++ b/tools/linguist/shared/po.cpp @@ -359,7 +359,7 @@ bool loadPO(Translator &translator, QIODevice &dev, ConversionData &cd) const QChar quote = QLatin1Char('"'); const QChar newline = QLatin1Char('\n'); QTextStream in(&dev); - in.setCodec(cd.m_codecForSource.isEmpty() ? "UTF-8" : cd.m_codecForSource); + in.setCodec(cd.m_codecForSource.isEmpty() ? QByteArray("UTF-8") : cd.m_codecForSource); bool error = false; // format of a .po file entry: @@ -555,7 +555,7 @@ bool savePO(const Translator &translator, QIODevice &dev, ConversionData &cd) { bool ok = true; QTextStream out(&dev); - out.setCodec(cd.m_outputCodec.isEmpty() ? "UTF-8" : cd.m_outputCodec); + out.setCodec(cd.m_outputCodec.isEmpty() ? QByteArray("UTF-8") : cd.m_outputCodec); bool first = true; if (translator.messages().isEmpty() || !translator.messages().first().sourceText().isEmpty()) { diff --git a/tools/linguist/shared/qm.cpp b/tools/linguist/shared/qm.cpp index 638e997..dc681f5 100644 --- a/tools/linguist/shared/qm.cpp +++ b/tools/linguist/shared/qm.cpp @@ -553,7 +553,7 @@ bool loadQM(Translator &translator, QIODevice &dev, ConversionData &cd) //qDebug() << "NUMITEMS: " << numItems; QTextCodec *codec = QTextCodec::codecForName( - cd.m_codecForSource.isEmpty() ? "Latin1" : cd.m_codecForSource); + cd.m_codecForSource.isEmpty() ? QByteArray("Latin1") : cd.m_codecForSource); QTextCodec *utf8Codec = 0; if (codec->name() != "UTF-8") utf8Codec = QTextCodec::codecForName("UTF-8"); diff --git a/tools/qdoc3/atom.cpp b/tools/qdoc3/atom.cpp index a82a783..da32735 100644 --- a/tools/qdoc3/atom.cpp +++ b/tools/qdoc3/atom.cpp @@ -93,6 +93,7 @@ QString Atom::UPPERROMAN_ ("upperroman"); \value AbstractLeft \value AbstractRight + \value AnnotatedList \value AutoLink \value BaseName \value BriefLeft @@ -163,6 +164,7 @@ static const struct { } atms[] = { { "AbstractLeft", Atom::AbstractLeft }, { "AbstractRight", Atom::AbstractRight }, + { "AnnotatedList", Atom::AnnotatedList }, { "AutoLink", Atom::AutoLink }, { "BaseName", Atom::BaseName }, { "BriefLeft", Atom::BriefLeft }, diff --git a/tools/qdoc3/atom.h b/tools/qdoc3/atom.h index 6d5af0a..941ac70 100644 --- a/tools/qdoc3/atom.h +++ b/tools/qdoc3/atom.h @@ -58,6 +58,7 @@ class Atom enum Type { AbstractLeft, AbstractRight, + AnnotatedList, AutoLink, BaseName, BriefLeft, diff --git a/tools/qdoc3/codemarker.cpp b/tools/qdoc3/codemarker.cpp index e95153d..4c018d1 100644 --- a/tools/qdoc3/codemarker.cpp +++ b/tools/qdoc3/codemarker.cpp @@ -168,7 +168,8 @@ const Node *CodeMarker::nodeForString(const QString& string) { if (sizeof(const Node *) == sizeof(uint)) { return reinterpret_cast<const Node *>(string.toUInt()); - } else { + } + else { return reinterpret_cast<const Node *>(string.toULongLong()); } } @@ -177,7 +178,8 @@ QString CodeMarker::stringForNode(const Node *node) { if (sizeof(const Node *) == sizeof(ulong)) { return QString::number(reinterpret_cast<ulong>(node)); - } else { + } + else { return QString::number(reinterpret_cast<qulonglong>(node)); } } @@ -220,7 +222,8 @@ QString CodeMarker::typified(const QString &string) || ch.digitValue() >= 0 || ch == QLatin1Char('_') || ch == QLatin1Char(':')) { pendingWord += ch; - } else { + } + else { if (!pendingWord.isEmpty()) { bool isProbablyType = (pendingWord != QLatin1String("const")); if (isProbablyType) @@ -251,7 +254,7 @@ QString CodeMarker::typified(const QString &string) return result; } -QString CodeMarker::taggedNode(const Node *node) +QString CodeMarker::taggedNode(const Node* node) { QString tag; @@ -276,10 +279,34 @@ QString CodeMarker::taggedNode(const Node *node) break; default: tag = QLatin1String("@unknown"); + break; + } + return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name()) + + QLatin1String("</") + tag + QLatin1Char('>'); +} + +#ifdef QDOC_QML +QString CodeMarker::taggedQmlNode(const Node* node) +{ + QString tag; + switch (node->type()) { + case Node::QmlProperty: + tag = QLatin1String("@property"); + break; + case Node::QmlSignal: + tag = QLatin1String("@signal"); + break; + case Node::QmlMethod: + tag = QLatin1String("@method"); + break; + default: + tag = QLatin1String("@unknown"); + break; } return QLatin1Char('<') + tag + QLatin1Char('>') + protect(node->name()) + QLatin1String("</") + tag + QLatin1Char('>'); } +#endif QString CodeMarker::linkTag(const Node *node, const QString& body) { @@ -308,9 +335,11 @@ QString CodeMarker::sortName(const Node *node) QString sortNo; if (func->metaness() == FunctionNode::Ctor) { sortNo = QLatin1String("C"); - } else if (func->metaness() == FunctionNode::Dtor) { + } + else if (func->metaness() == FunctionNode::Dtor) { sortNo = QLatin1String("D"); - } else { + } + else { if (nodeName.startsWith(QLatin1String("operator")) && nodeName.length() > 8 && !nodeName[8].isLetterOrNumber()) @@ -336,9 +365,14 @@ void CodeMarker::insert(FastSection &fastSection, SynopsisStyle style, Status status) { - bool inheritedMember = (!node->relates() && - (node->parent() != (const InnerNode *)fastSection.innerNode)); bool irrelevant = false; + bool inheritedMember = false; + if (!node->relates()) { + if (node->parent() != (const InnerNode*)fastSection.innerNode) { + if (node->type() != Node::QmlProperty) + inheritedMember = true; + } + } if (node->access() == Node::Private) { irrelevant = true; @@ -462,7 +496,8 @@ QStringList CodeMarker::macRefsForNode(const Node *node) #if 0 if (!classe->templateStuff().isEmpty()) { result += QLatin1String("tmplt/"); - } else + } + else #endif { result += QLatin1String("cl/"); @@ -499,14 +534,18 @@ QStringList CodeMarker::macRefsForNode(const Node *node) result += QLatin1String("macro/"); isMacro = true; #if 0 - } else if (!func->templateStuff().isEmpty()) { + } + else if (!func->templateStuff().isEmpty()) { result += QLatin1String("ftmplt/"); #endif - } else if (func->isStatic()) { + } + else if (func->isStatic()) { result += QLatin1String("clm/"); - } else if (!func->parent()->name().isEmpty()) { + } + else if (!func->parent()->name().isEmpty()) { result += QLatin1String("instm/"); - } else { + } + else { result += QLatin1String("func/"); } @@ -520,7 +559,8 @@ QStringList CodeMarker::macRefsForNode(const Node *node) result += "/" + QLatin1String(QMetaObject::normalizedSignature(func->returnType().toLatin1().constData())) + "/("; const QList<Parameter> ¶ms = func->parameters(); for (int i = 0; i < params.count(); ++i) { - QString type = params.at(i).leftType() + params.at(i).rightType(); + QString type = params.at(i).leftType() + + params.at(i).rightType(); type = QLatin1String(QMetaObject::normalizedSignature(type.toLatin1().constData())); if (i != 0) result += ","; @@ -563,10 +603,21 @@ QString CodeMarker::macName(const Node *node, const QString &name) if (node->name().isEmpty()) { return QLatin1Char('/') + myName; - } else { + } + else { return plainFullName(node) + QLatin1Char('/') + myName; } } +#ifdef QDOC_QML +/*! + Get the list of documentation sections for the children of + the specified QmlClassNode. + */ +QList<Section> CodeMarker::qmlSections(const QmlClassNode* , SynopsisStyle ) +{ + return QList<Section>(); +} +#endif QT_END_NAMESPACE diff --git a/tools/qdoc3/codemarker.h b/tools/qdoc3/codemarker.h index 67b1064..91dc8b0 100644 --- a/tools/qdoc3/codemarker.h +++ b/tools/qdoc3/codemarker.h @@ -121,6 +121,9 @@ class CodeMarker virtual QString markedUpSynopsis(const Node *node, const Node *relative, SynopsisStyle style) = 0; +#ifdef QDOC_QML + virtual QString markedUpQmlItem(const Node* , bool) { return QString(); } +#endif virtual QString markedUpName(const Node *node) = 0; virtual QString markedUpFullName(const Node *node, const Node *relative = 0) = 0; @@ -132,6 +135,10 @@ class CodeMarker virtual QList<Section> sections(const InnerNode *inner, SynopsisStyle style, Status status) = 0; +#ifdef QDOC_QML + virtual QList<Section> qmlSections(const QmlClassNode* qmlClassNode, + SynopsisStyle style); +#endif virtual const Node *resolveTarget(const QString& target, const Tree *tree, const Node *relative) = 0; @@ -151,7 +158,10 @@ class CodeMarker virtual QString sortName(const Node *node); QString protect(const QString &string); QString typified(const QString &string); - QString taggedNode(const Node *node); + QString taggedNode(const Node* node); +#ifdef QDOC_QML + QString taggedQmlNode(const Node* node); +#endif QString linkTag(const Node *node, const QString& body); void insert(FastSection &fastSection, Node *node, diff --git a/tools/qdoc3/command.cpp b/tools/qdoc3/command.cpp index e51e235..bce262b 100644 --- a/tools/qdoc3/command.cpp +++ b/tools/qdoc3/command.cpp @@ -49,44 +49,46 @@ QT_BEGIN_NAMESPACE -void executeCommand( const Location& location, const QString& format, - const QStringList& args ) +void executeCommand(const Location& location, + const QString& format, + const QStringList& args) { QString actualCommand; - for ( int i = 0; i < (int) format.length(); i++ ) { + for (int i = 0; i < (int) format.length(); i++) { int ch = format[i].unicode(); - if ( ch > 0 && ch < 8 ) { + if (ch > 0 && ch < 8) { actualCommand += args[ch - 1]; - } else { + } + else { actualCommand += format[i]; } } QString toolName = actualCommand; - int space = toolName.indexOf( QLatin1Char(' ') ); - if ( space != -1 ) - toolName.truncate( space ); + int space = toolName.indexOf(QLatin1Char(' ')); + if (space != -1) + toolName.truncate(space); QProcess process; process.start(QLatin1String("sh"), - QStringList() << QLatin1String("-c") << actualCommand ); + QStringList() << QLatin1String("-c") << actualCommand); process.waitForFinished(); if (process.exitCode() == 127) - location.fatal( tr("Couldn't launch the '%1' tool") - .arg(toolName), - tr("Make sure the tool is installed and in the" - " path.") ); + location.fatal(tr("Couldn't launch the '%1' tool") + .arg(toolName), + tr("Make sure the tool is installed and in the" + " path.")); QString errors = QString::fromLocal8Bit(process.readAllStandardError()); - while ( errors.endsWith(QLatin1Char('\n')) ) - errors.truncate( errors.length() - 1 ); - if ( !errors.isEmpty() ) - location.fatal( tr("The '%1' tool encountered some problems") - .arg(toolName), - tr("The tool was invoked like this:\n%1\n" - "It emitted these errors:\n%2") - .arg(actualCommand).arg(errors) ); + while (errors.endsWith(QLatin1Char('\n'))) + errors.truncate(errors.length() - 1); + if (!errors.isEmpty()) + location.fatal(tr("The '%1' tool encountered some problems") + .arg(toolName), + tr("The tool was invoked like this:\n%1\n" + "It emitted these errors:\n%2") + .arg(actualCommand).arg(errors)); } QT_END_NAMESPACE diff --git a/tools/qdoc3/cppcodemarker.cpp b/tools/qdoc3/cppcodemarker.cpp index f807609..0f8d1b7 100644 --- a/tools/qdoc3/cppcodemarker.cpp +++ b/tools/qdoc3/cppcodemarker.cpp @@ -284,7 +284,7 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, } else { for (int i = 0; i < documentedItems.size(); ++i) { - if (i < MaxEnumValues - 2 || i == documentedItems.size() - 1) { + if (i < MaxEnumValues-2 || i == documentedItems.size()-1) { if (i != 0) synopsis += ", "; synopsis += documentedItems.at(i); @@ -345,6 +345,43 @@ QString CppCodeMarker::markedUpSynopsis(const Node *node, return synopsis + extra; } +#ifdef QDOC_QML +/*! + */ +QString CppCodeMarker::markedUpQmlItem(const Node* node, bool summary) +{ + QString name = taggedQmlNode(node); + if (summary) { + name = linkTag(node,name); + } + name = "<@name>" + name + "</@name>"; + QString synopsis = name; + if (node->type() == Node::QmlProperty) { + const QmlPropertyNode* pn = static_cast<const QmlPropertyNode*>(node); + synopsis += " : " + typified(pn->dataType()); + } + + QString extra; + if (summary) { + if (node->status() == Node::Preliminary) { + extra += " (preliminary)"; + } + else if (node->status() == Node::Deprecated) { + extra += " (deprecated)"; + } + else if (node->status() == Node::Obsolete) { + extra += " (obsolete)"; + } + } + + if (!extra.isEmpty()) { + extra.prepend("<@extra>"); + extra.append("</@extra>"); + } + return synopsis + extra; +} +#endif + QString CppCodeMarker::markedUpName(const Node *node) { QString name = linkTag(node, taggedNode(node)); @@ -537,7 +574,7 @@ QList<Section> CppCodeMarker::sections(const InnerNode *inner, else if (isStatic) { if ((*c)->type() != Node::Variable || !(*c)->doc().isEmpty()) - insert(staticPublicMembers, *c, style, status); + insert(staticPublicMembers,*c,style,status); } else if ((*c)->type() == Node::Property) { insert(properties, *c, style, status); @@ -561,11 +598,11 @@ QList<Section> CppCodeMarker::sections(const InnerNode *inner, else if (isStatic) { if ((*c)->type() != Node::Variable || !(*c)->doc().isEmpty()) - insert(staticProtectedMembers, *c, style, status); + insert(staticProtectedMembers,*c,style,status); } else if ((*c)->type() == Node::Variable) { if (!(*c)->doc().isEmpty()) - insert(protectedVariables, *c, style, status); + insert(protectedVariables,*c,style,status); } else if ((*c)->type() == Node::Function) { if (!insertReimpFunc(protectedFunctions,*c,status)) @@ -582,14 +619,14 @@ QList<Section> CppCodeMarker::sections(const InnerNode *inner, else if (isStatic) { if ((*c)->type() != Node::Variable || !(*c)->doc().isEmpty()) - insert(staticPrivateMembers, *c, style, status); + insert(staticPrivateMembers,*c,style,status); } else if ((*c)->type() == Node::Function) { if (!insertReimpFunc(privateFunctions,*c,status)) insert(privateFunctions, *c, style, status); } else { - insert(privateTypes, *c, style, status); + insert(privateTypes,*c,style,status); } } ++c; @@ -706,17 +743,23 @@ QList<Section> CppCodeMarker::sections(const InnerNode *inner, "Namespaces", "namespace", "namespaces"); - FastSection classes(inner, "Classes", "class", "classes"); + FastSection classes(inner, + "Classes", + "class", + "classes"); FastSection types(inner, - style == Summary ? "Types" : "Type Documentation", + style == Summary ? + "Types" : "Type Documentation", "type", "types"); FastSection functions(inner, - style == Summary ? "Functions" : "Function Documentation", + style == Summary ? + "Functions" : "Function Documentation", "function", "functions"); FastSection macros(inner, - style == Summary ? "Macros" : "Macro Documentation", + style == Summary ? + "Macros" : "Macro Documentation", "macro", "macros"); @@ -1051,4 +1094,82 @@ QString CppCodeMarker::addMarkUp(const QString& protectedCode, return result; } +#ifdef QDOC_QML +/*! + This function is for documenting QML properties. It returns + the list of documentation sections for the children of the + \a qmlClassNode. + + Currently, it only handles QML property groups. + */ +QList<Section> CppCodeMarker::qmlSections(const QmlClassNode* qmlClassNode, + SynopsisStyle style) +{ + QList<Section> sections; + if (qmlClassNode) { + if (style == Summary) { + FastSection qmlproperties(qmlClassNode, + "QML Properties", + "property", + "properties"); + FastSection qmlsignals(qmlClassNode, + "QML Signals", + "signal", + "signals"); + FastSection qmlmethods(qmlClassNode, + "QML Methods", + "method", + "methods"); + + NodeList::ConstIterator c = qmlClassNode->childNodes().begin(); + while (c != qmlClassNode->childNodes().end()) { + if ((*c)->subType() == Node::QmlPropertyGroup) { + const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(*c); + NodeList::ConstIterator p = qpgn->childNodes().begin(); + while (p != qpgn->childNodes().end()) { + if ((*p)->type() == Node::QmlProperty) { + insert(qmlproperties,*p,style,Okay); + } + ++p; + } + } + else if ((*c)->type() == Node::QmlSignal) { + insert(qmlsignals,*c,style,Okay); + } + else if ((*c)->type() == Node::QmlMethod) { + insert(qmlmethods,*c,style,Okay); + } + ++c; + } + append(sections,qmlproperties); + append(sections,qmlsignals); + append(sections,qmlmethods); + } + else if (style == Detailed) { + FastSection qmlproperties(qmlClassNode,"QML Property Documentation"); + FastSection qmlsignals(qmlClassNode,"QML Signal Documentation"); + FastSection qmlmethods(qmlClassNode,"QML Method Documentation"); + NodeList::ConstIterator c = qmlClassNode->childNodes().begin(); + while (c != qmlClassNode->childNodes().end()) { + if ((*c)->subType() == Node::QmlPropertyGroup) { + insert(qmlproperties,*c,style,Okay); + } + else if ((*c)->type() == Node::QmlSignal) { + insert(qmlsignals,*c,style,Okay); + } + else if ((*c)->type() == Node::QmlMethod) { + insert(qmlmethods,*c,style,Okay); + } + ++c; + } + append(sections,qmlproperties); + append(sections,qmlsignals); + append(sections,qmlmethods); + } + } + + return sections; +} +#endif + QT_END_NAMESPACE diff --git a/tools/qdoc3/cppcodemarker.h b/tools/qdoc3/cppcodemarker.h index 2967dfe..fa3cb78 100644 --- a/tools/qdoc3/cppcodemarker.h +++ b/tools/qdoc3/cppcodemarker.h @@ -67,6 +67,9 @@ class CppCodeMarker : public CodeMarker QString markedUpSynopsis(const Node *node, const Node *relative, SynopsisStyle style); +#ifdef QDOC_QML + QString markedUpQmlItem(const Node *node, bool summary); +#endif QString markedUpName(const Node *node); QString markedUpFullName(const Node *node, const Node *relative); QString markedUpEnumValue(const QString &enumValue, const Node *relative); @@ -76,6 +79,8 @@ class CppCodeMarker : public CodeMarker QList<Section> sections(const InnerNode *innerNode, SynopsisStyle style, Status status); + QList<Section> qmlSections(const QmlClassNode* qmlClassNode, + SynopsisStyle style); const Node *resolveTarget(const QString& target, const Tree *tree, const Node *relative); diff --git a/tools/qdoc3/cppcodeparser.cpp b/tools/qdoc3/cppcodeparser.cpp index dd10c1c..562684b 100644 --- a/tools/qdoc3/cppcodeparser.cpp +++ b/tools/qdoc3/cppcodeparser.cpp @@ -87,6 +87,10 @@ QT_BEGIN_NAMESPACE #ifdef QDOC_QML #define COMMAND_QMLCLASS Doc::alias("qmlclass") #define COMMAND_QMLPROPERTY Doc::alias("qmlproperty") +#define COMMAND_QMLINHERITS Doc::alias("inherits") +#define COMMAND_QMLSIGNAL Doc::alias("qmlsignal") +#define COMMAND_QMLMETHOD Doc::alias("qmlmethod") +#define COMMAND_QMLDEFAULT Doc::alias("default") #endif QStringList CppCodeParser::exampleFiles; @@ -109,7 +113,8 @@ static void extractPageLinkAndDesc(const QString &arg, if (arg.contains(".html") && spaceAt != -1) { *link = arg.left(spaceAt).trimmed(); *desc = arg.mid(spaceAt).trimmed(); - } else { + } + else { *link = arg; *desc = arg; } @@ -202,11 +207,6 @@ void CppCodeParser::initializeParser(const Config &config) nodeTypeMap.insert(COMMAND_PROPERTY, Node::Property); nodeTypeMap.insert(COMMAND_VARIABLE, Node::Variable); -#ifdef QDOC_QML - // nodeTypeMap.insert(COMMAND_QMLCLASS, Node::Class); - nodeTypeMap.insert(COMMAND_QMLPROPERTY, Node::Property); -#endif - exampleFiles = config.getStringList(CONFIG_EXAMPLES); exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS); QStringList exampleFilePatterns = config.getStringList( @@ -477,13 +477,15 @@ QSet<QString> CppCodeParser::topicCommands() << COMMAND_PROPERTY << COMMAND_SERVICE << COMMAND_TYPEDEF -#ifdef QDOC_QML +#ifdef QDOC_QML << COMMAND_VARIABLE << COMMAND_QMLCLASS - << COMMAND_QMLPROPERTY; -#else + << COMMAND_QMLPROPERTY + << COMMAND_QMLSIGNAL + << COMMAND_QMLMETHOD; +#else << COMMAND_VARIABLE; -#endif +#endif } /*! @@ -587,7 +589,7 @@ Node *CppCodeParser::processTopicCommand(const Doc& doc, The command was neither "fn" nor "macro" . */ // ### split(" ") hack is there to support header file syntax - QStringList paths = arg.split(" "); + QStringList paths = arg.split(" "); QStringList path = paths[0].split("::"); Node *node = 0; if (!usedNamespaces.isEmpty()) { @@ -627,49 +629,169 @@ Node *CppCodeParser::processTopicCommand(const Doc& doc, } } + if (command == COMMAND_CLASS) { + if (paths.size() > 1) { + if (!paths[1].endsWith(".h")) { + ClassNode*cnode = static_cast<ClassNode*>(node); + cnode->setQmlElement(paths[1]); + } + } + } return node; } else if (command == COMMAND_EXAMPLE) { - FakeNode *fake = new FakeNode(tre->root(), arg, FakeNode::Example); + FakeNode *fake = new FakeNode(tre->root(), arg, Node::Example); createExampleFileNodes(fake); return fake; } else if (command == COMMAND_EXTERNALPAGE) { - return new FakeNode(tre->root(), arg, FakeNode::ExternalPage); + return new FakeNode(tre->root(), arg, Node::ExternalPage); } else if (command == COMMAND_FILE) { - return new FakeNode(tre->root(), arg, FakeNode::File); + return new FakeNode(tre->root(), arg, Node::File); } else if (command == COMMAND_GROUP) { - return new FakeNode(tre->root(), arg, FakeNode::Group); + return new FakeNode(tre->root(), arg, Node::Group); } else if (command == COMMAND_HEADERFILE) { - return new FakeNode(tre->root(), arg, FakeNode::HeaderFile); + return new FakeNode(tre->root(), arg, Node::HeaderFile); } else if (command == COMMAND_MODULE) { - return new FakeNode(tre->root(), arg, FakeNode::Module); + return new FakeNode(tre->root(), arg, Node::Module); } else if (command == COMMAND_PAGE) { - return new FakeNode(tre->root(), arg, FakeNode::Page); + return new FakeNode(tre->root(), arg, Node::Page); } -#ifdef QDOC_QML +#ifdef QDOC_QML else if (command == COMMAND_QMLCLASS) { const ClassNode* classNode = 0; - QStringList names = arg.split(" "); - //qDebug() << "QMLCLASS" << names; + QStringList names = arg.split(" "); if (names.size() > 1) { Node* n = tre->findNode(names[1].split("::"),Node::Class); - if (n) { + if (n) classNode = static_cast<const ClassNode*>(n); - //qDebug() << "FOUND IT!" << classNode->name(); + } + return new QmlClassNode(tre->root(), names[0], classNode); + } + else if ((command == COMMAND_QMLSIGNAL) || + (command == COMMAND_QMLMETHOD)) { + QString element; + QString name; + QmlClassNode* qmlClass = 0; + if (splitQmlArg(doc,arg,element,name)) { + Node* n = tre->findNode(QStringList(element),Node::Fake); + if (n && n->subType() == Node::QmlClass) { + qmlClass = static_cast<QmlClassNode*>(n); + if (command == COMMAND_QMLSIGNAL) + return new QmlSignalNode(qmlClass,name); + else + return new QmlMethodNode(qmlClass,name); } } - return new QmlNode(tre->root(), names[0], classNode); } -#endif +#endif return 0; } +#ifdef QDOC_QML + +/*! + A QML property argument has the form... + + <type> <element>::<name> + + This function splits the argument into those three + parts, sets \a type, \a element, and \a property, + and returns true. If any of the parts isn't found, + a debug message is output and false is returned. + */ +bool CppCodeParser::splitQmlPropertyArg(const Doc& doc, + const QString& arg, + QString& type, + QString& element, + QString& property) +{ + QStringList blankSplit = arg.split(" "); + if (blankSplit.size() > 1) { + type = blankSplit[0]; + QStringList colonSplit(blankSplit[1].split("::")); + if (colonSplit.size() > 1) { + element = colonSplit[0]; + property = colonSplit[1]; + return true; + } + else + doc.location().warning(tr("Missing QML element name or property name")); + } + else + doc.location().warning(tr("Missing QML property type or property path")); + return false; +} + +/*! + A QML signal or method argument has the form... + + <element>::<name> + + This function splits the argument into those two + parts, sets \a element, and \a name, and returns + true. If either of the parts isn't found, a debug + message is output and false is returned. + */ +bool CppCodeParser::splitQmlArg(const Doc& doc, + const QString& arg, + QString& element, + QString& name) +{ + QStringList colonSplit(arg.split("::")); + if (colonSplit.size() > 1) { + element = colonSplit[0]; + name = colonSplit[1]; + return true; + } + else + doc.location().warning(tr("Missing QML element name or signal/method name")); + return false; +} + +/*! + Process the topic \a command group with arguments \a args. + + Currently, this function is called only for \e{qmlproperty}. + */ +Node *CppCodeParser::processTopicCommandGroup(const Doc& doc, + const QString& command, + const QStringList& args) +{ + QmlPropGroupNode* qmlPropGroup = 0; + if (command == COMMAND_QMLPROPERTY) { + QString type; + QString element; + QString property; + QStringList::ConstIterator arg = args.begin(); + if (splitQmlPropertyArg(doc,(*arg),type,element,property)) { + Node* n = tre->findNode(QStringList(element),Node::Fake); + if (n && n->subType() == Node::QmlClass) { + QmlClassNode* qmlClass = static_cast<QmlClassNode*>(n); + if (qmlClass) + qmlPropGroup = new QmlPropGroupNode(qmlClass,property); + } + } + if (qmlPropGroup) { + new QmlPropertyNode(qmlPropGroup,property,type); + ++arg; + while (arg != args.end()) { + if (splitQmlPropertyArg(doc,(*arg),type,element,property)) { + new QmlPropertyNode(qmlPropGroup,property,type); + } + ++arg; + } + } + } + return qmlPropGroup; +} +#endif + /*! Returns the set of strings representing the common metacommands plus some other metacommands. @@ -684,7 +806,13 @@ QSet<QString> CppCodeParser::otherMetaCommands() << COMMAND_NEXTPAGE << COMMAND_PREVIOUSPAGE << COMMAND_INDEXPAGE +#ifdef QDOC_QML + << COMMAND_STARTPAGE + << COMMAND_QMLINHERITS + << COMMAND_QMLDEFAULT; +#else << COMMAND_STARTPAGE; +#endif } /*! @@ -759,14 +887,19 @@ void CppCodeParser::processOtherMetaCommand(const Doc& doc, else if (command == COMMAND_RELATES) { InnerNode *pseudoParent; if (arg.startsWith("<") || arg.startsWith("\"")) { - pseudoParent = static_cast<InnerNode *>(tre->findNode(QStringList(arg), Node::Fake)); + pseudoParent = + static_cast<InnerNode *>(tre->findNode(QStringList(arg), + Node::Fake)); } else { QStringList newPath = arg.split("::"); - pseudoParent = static_cast<InnerNode *>(tre->findNode(QStringList(newPath), Node::Class)); + pseudoParent = + static_cast<InnerNode*>(tre->findNode(QStringList(newPath), + Node::Class)); if (!pseudoParent) - pseudoParent = static_cast<InnerNode *>(tre->findNode(QStringList(newPath), - Node::Namespace)); + pseudoParent = + static_cast<InnerNode*>(tre->findNode(QStringList(newPath), + Node::Namespace)); } if (!pseudoParent) { doc.location().warning(tr("Cannot find '%1' in '\\%2'") @@ -791,6 +924,15 @@ void CppCodeParser::processOtherMetaCommand(const Doc& doc, else if (command == COMMAND_STARTPAGE) { setLink(node, Node::StartLink, arg); } +#ifdef QDOC_QML + else if (command == COMMAND_QMLINHERITS) { + setLink(node, Node::InheritsLink, arg); + } + else if (command == COMMAND_QMLDEFAULT) { + QmlPropGroupNode* qpgn = static_cast<QmlPropGroupNode*>(node); + qpgn->setDefault(); + } +#endif else { processCommonMetaCommand(doc.location(),command,arg,node,tre); } @@ -870,9 +1012,8 @@ bool CppCodeParser::match(int target) readToken(); return true; } - else { + else return false; - } } /*! @@ -907,11 +1048,14 @@ bool CppCodeParser::matchTemplateAngles(CodeChunk *dataType) do { if (tok == Tok_LeftAngle) { leftAngleDepth++; - } else if (tok == Tok_RightAngle) { + } + else if (tok == Tok_RightAngle) { leftAngleDepth--; - } else if (tok == Tok_LeftParen || tok == Tok_LeftBrace) { + } + else if (tok == Tok_LeftParen || tok == Tok_LeftBrace) { ++parenAndBraceDepth; - } else if (tok == Tok_RightParen || tok == Tok_RightBrace) { + } + else if (tok == Tok_RightParen || tok == Tok_RightBrace) { if (--parenAndBraceDepth < 0) return false; } @@ -974,7 +1118,8 @@ bool CppCodeParser::matchDataType(CodeChunk *dataType, QString *var) dataType->append(previousLexeme()); else return false; - } else if (match(Tok_int) || match(Tok_char) || match(Tok_double)) { + } + else if (match(Tok_int) || match(Tok_char) || match(Tok_double)) { dataType->append(previousLexeme()); } @@ -1074,8 +1219,10 @@ bool CppCodeParser::matchParameter(FunctionNode *func) readToken(); } } - func->addParameter(Parameter(dataType.toString(), "", name, - defaultValue.toString())); // ### + func->addParameter(Parameter(dataType.toString(), + "", + name, + defaultValue.toString())); // ### return true; } @@ -1123,7 +1270,8 @@ bool CppCodeParser::matchFunctionDecl(InnerNode *parent, compat = true; if (tok == Tok_operator && - (returnType.toString().isEmpty() || returnType.toString().endsWith("::"))) { + (returnType.toString().isEmpty() || + returnType.toString().endsWith("::"))) { // 'QString::operator const char *()' parentPath = returnType.toString().split(sep); parentPath.removeAll(QString()); @@ -1159,11 +1307,10 @@ bool CppCodeParser::matchFunctionDecl(InnerNode *parent, name = previousLexeme(); matchTemplateAngles(); - if (match(Tok_Gulbrandsen)) { + if (match(Tok_Gulbrandsen)) parentPath.append(name); - } else { + else break; - } } if (tok == Tok_operator) { @@ -1176,7 +1323,9 @@ bool CppCodeParser::matchFunctionDecl(InnerNode *parent, break; } } - if (parent && (tok == Tok_Semicolon || tok == Tok_LeftBracket || tok == Tok_Colon) + if (parent && (tok == Tok_Semicolon || + tok == Tok_LeftBracket || + tok == Tok_Colon) && access != Node::Private) { if (tok == Tok_LeftBracket) { returnType.appendHotspot(); @@ -1190,7 +1339,8 @@ bool CppCodeParser::matchFunctionDecl(InnerNode *parent, } if (tok != Tok_Semicolon) return false; - } else if (tok == Tok_Colon) { + } + else if (tok == Tok_Colon) { returnType.appendHotspot(); while (tok != Tok_Semicolon && tok != Tok_Eoi) { @@ -1382,7 +1532,7 @@ bool CppCodeParser::matchNamespaceDecl(InnerNode *parent) QString namespaceName = previousLexeme(); NamespaceNode *namespasse = 0; if (parent) - namespasse = static_cast<NamespaceNode *>(parent->findNode(namespaceName, Node::Namespace)); + namespasse = static_cast<NamespaceNode*>(parent->findNode(namespaceName, Node::Namespace)); if (!namespasse) { namespasse = new NamespaceNode(parent, namespaceName); namespasse->setAccess(access); @@ -1448,7 +1598,8 @@ bool CppCodeParser::matchEnumItem(InnerNode *parent, EnumNode *enume) if (strVal.isEmpty()) { if (enume->items().isEmpty()) { strVal = "0"; - } else { + } + else { QString last = enume->items().last().value(); bool ok; int n = last.toInt(&ok); @@ -1458,15 +1609,16 @@ bool CppCodeParser::matchEnumItem(InnerNode *parent, EnumNode *enume) strVal = last.left(2) + QString::number(n + 1, 16); else strVal = "0" + QString::number(n + 1, 8); - } else { - strVal = QString::number(n + 1); } + else + strVal = QString::number(n + 1); } } } enume->addItem(EnumItem(name, strVal)); - } else { + } + else { VariableNode *var = new VariableNode(parent, name); var->setAccess(access); var->setLocation(location()); @@ -1554,7 +1706,8 @@ bool CppCodeParser::matchProperty(InnerNode *parent) if (match(Tok_Ident)) { value = previousLexeme(); - } else if (match(Tok_LeftParen)) { + } + else if (match(Tok_LeftParen)) { int depth = 1; while (tok != Tok_Eoi) { if (tok == Tok_LeftParen) { @@ -1571,6 +1724,9 @@ bool CppCodeParser::matchProperty(InnerNode *parent) value = "?"; } + /* + Task 259071 requires work here. See gui/widgets/qdatetime.h, for example. + */ if (key == "READ") tre->addPropertyFunction(property, value, PropertyNode::Getter); else if (key == "WRITE") @@ -1807,6 +1963,28 @@ bool CppCodeParser::matchDocsAndStuff() /* There is a topic command. Process it. */ +#ifdef QDOC_QML + if (topic == COMMAND_QMLPROPERTY) { + Doc nodeDoc = doc; + Node *node = processTopicCommandGroup(nodeDoc,topic,args); + if (node != 0) { + nodes.append(node); + docs.append(nodeDoc); + } + } + else { + QStringList::ConstIterator a = args.begin(); + while (a != args.end()) { + Doc nodeDoc = doc; + Node *node = processTopicCommand(nodeDoc,topic,*a); + if (node != 0) { + nodes.append(node); + docs.append(nodeDoc); + } + ++a; + } + } +#else QStringList::ConstIterator a = args.begin(); while (a != args.end()) { Doc nodeDoc = doc; @@ -1817,6 +1995,7 @@ bool CppCodeParser::matchDocsAndStuff() } ++a; } +#endif } NodeList::Iterator n = nodes.begin(); @@ -1824,7 +2003,8 @@ bool CppCodeParser::matchDocsAndStuff() while (n != nodes.end()) { processOtherMetaCommands(*d, *n); (*n)->setDoc(*d); - if ((*n)->isInnerNode() && ((InnerNode *)*n)->includes().isEmpty()) { + if ((*n)->isInnerNode() && + ((InnerNode *)*n)->includes().isEmpty()) { InnerNode *m = static_cast<InnerNode *>(*n); while (m->parent() != tre->root()) m = m->parent(); @@ -1912,7 +2092,8 @@ void CppCodeParser::parseQiteratorDotH(const Location &location, mutableSequentialIteratorDefinition = lines[1]; associativeIteratorDefinition = lines[2]; mutableAssociativeIteratorDefinition = lines[3]; - } else { + } + else { location.warning(tr("The qiterator.h hack failed")); } } @@ -1994,7 +2175,7 @@ void CppCodeParser::createExampleFileNodes(FakeNode *fake) foreach (const QString &exampleFile, exampleFiles) (void) new FakeNode(fake, exampleFile.mid(sizeOfBoringPartOfName), - FakeNode::File); + Node::File); } QT_END_NAMESPACE diff --git a/tools/qdoc3/cppcodeparser.h b/tools/qdoc3/cppcodeparser.h index 1f41318..cbb0149 100644 --- a/tools/qdoc3/cppcodeparser.h +++ b/tools/qdoc3/cppcodeparser.h @@ -90,6 +90,21 @@ class CppCodeParser : public CodeParser virtual Node *processTopicCommand(const Doc& doc, const QString& command, const QString& arg); +#ifdef QDOC_QML + // might need to implement this in QsCodeParser as well. + virtual Node *processTopicCommandGroup(const Doc& doc, + const QString& command, + const QStringList& args); + bool splitQmlPropertyArg(const Doc& doc, + const QString& arg, + QString& type, + QString& element, + QString& property); + bool splitQmlArg(const Doc& doc, + const QString& arg, + QString& element, + QString& name); +#endif virtual QSet<QString> otherMetaCommands(); virtual void processOtherMetaCommand(const Doc& doc, const QString& command, diff --git a/tools/qdoc3/doc.cpp b/tools/qdoc3/doc.cpp index d5aca0e..e2f3525 100644 --- a/tools/qdoc3/doc.cpp +++ b/tools/qdoc3/doc.cpp @@ -73,20 +73,20 @@ struct Macro }; enum { - CMD_A, CMD_ABSTRACT, CMD_BADCODE, CMD_BASENAME, CMD_BOLD, - CMD_BRIEF, CMD_C, CMD_CAPTION, CMD_CHAPTER, CMD_CODE, - CMD_CODELINE, CMD_DOTS, CMD_ELSE, CMD_ENDABSTRACT, - CMD_ENDCHAPTER, CMD_ENDCODE, CMD_ENDFOOTNOTE, CMD_ENDIF, - CMD_ENDLEGALESE, CMD_ENDLINK, CMD_ENDLIST, CMD_ENDOMIT, - CMD_ENDPART, CMD_ENDQUOTATION, CMD_ENDRAW, CMD_ENDSECTION1, - CMD_ENDSECTION2, CMD_ENDSECTION3, CMD_ENDSECTION4, - CMD_ENDSIDEBAR, CMD_ENDTABLE, CMD_EXPIRE, CMD_FOOTNOTE, - CMD_GENERATELIST, CMD_GRANULARITY, CMD_HEADER, CMD_I, - CMD_IF, CMD_IMAGE, CMD_INCLUDE, CMD_INLINEIMAGE, CMD_INDEX, - CMD_KEYWORD, CMD_L, CMD_LEGALESE, CMD_LINK, CMD_LIST, - CMD_META, CMD_NEWCODE, CMD_O, CMD_OLDCODE, CMD_OMIT, - CMD_OMITVALUE, CMD_OVERLOAD, - CMD_PART, CMD_PRINTLINE, CMD_PRINTTO, + CMD_A, CMD_ABSTRACT, CMD_ANNOTATEDLIST, CMD_BADCODE, + CMD_BASENAME, CMD_BOLD, CMD_BRIEF, CMD_C, CMD_CAPTION, + CMD_CHAPTER, CMD_CODE, CMD_CODELINE, CMD_DOTS, CMD_ELSE, + CMD_ENDABSTRACT, CMD_ENDCHAPTER, CMD_ENDCODE, + CMD_ENDFOOTNOTE, CMD_ENDIF, CMD_ENDLEGALESE, CMD_ENDLINK, + CMD_ENDLIST, CMD_ENDOMIT, CMD_ENDPART, CMD_ENDQUOTATION, + CMD_ENDRAW, CMD_ENDSECTION1, CMD_ENDSECTION2, + CMD_ENDSECTION3, CMD_ENDSECTION4, CMD_ENDSIDEBAR, + CMD_ENDTABLE, CMD_EXPIRE, CMD_FOOTNOTE, CMD_GENERATELIST, + CMD_GRANULARITY, CMD_HEADER, CMD_I, CMD_IF, CMD_IMAGE, + CMD_INCLUDE, CMD_INLINEIMAGE, CMD_INDEX, CMD_KEYWORD, + CMD_L, CMD_LEGALESE, CMD_LINK, CMD_LIST, CMD_META, + CMD_NEWCODE, CMD_O, CMD_OLDCODE, CMD_OMIT, CMD_OMITVALUE, + CMD_OVERLOAD, CMD_PART, CMD_PRINTLINE, CMD_PRINTTO, CMD_PRINTUNTIL, CMD_QUOTATION, CMD_QUOTEFILE, CMD_QUOTEFROMFILE, CMD_QUOTEFUNCTION, CMD_RAW, CMD_ROW, CMD_SA, CMD_SECTION1, CMD_SECTION2, CMD_SECTION3, @@ -108,6 +108,7 @@ static struct { } cmds[] = { { "a", CMD_A, 0 }, { "abstract", CMD_ABSTRACT, 0 }, + { "annotatedlist", CMD_ANNOTATEDLIST, 0 }, { "badcode", CMD_BADCODE, 0 }, { "basename", CMD_BASENAME, 0 }, // ### don't document for now { "bold", CMD_BOLD, 0 }, @@ -723,6 +724,9 @@ void DocParser::parse(const QString& source, paraState = OutsidePara; // ### } break; + case CMD_ANNOTATEDLIST: + append(Atom::AnnotatedList, getArgument()); + break; case CMD_GENERATELIST: append(Atom::GeneratedList, getArgument()); break; diff --git a/tools/qdoc3/generator.cpp b/tools/qdoc3/generator.cpp index e97b7f2..e92f53b 100644 --- a/tools/qdoc3/generator.cpp +++ b/tools/qdoc3/generator.cpp @@ -42,7 +42,7 @@ /* generator.cpp */ - +#include <QtCore> #include <qdir.h> #include <qdebug.h> #include "codemarker.h" @@ -77,7 +77,11 @@ static void singularPlural(Text& text, const NodeList& nodes) } Generator::Generator() - : amp("&"), lt("<"), gt(">"), quot("""), tag("</?@[^>]*>") + : amp("&"), + lt("<"), + gt(">"), + quot("""), + tag("</?@[^>]*>") { generators.prepend(this); } @@ -125,7 +129,8 @@ void Generator::initialize(const Config &config) QSet<QString> formats = config.subVars(imagesDotFileExtensions); QSet<QString>::ConstIterator f = formats.begin(); while (f != formats.end()) { - imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + Config::dot + *f); + imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + + Config::dot + *f); ++f; } @@ -133,16 +138,22 @@ void Generator::initialize(const Config &config) while (g != generators.end()) { if (outputFormats.contains((*g)->format())) { (*g)->initializeGenerator(config); - QStringList extraImages = config.getStringList(CONFIG_EXTRAIMAGES + Config::dot - + (*g)->format()); + QStringList extraImages = config.getStringList(CONFIG_EXTRAIMAGES + + Config::dot + + (*g)->format()); QStringList::ConstIterator e = extraImages.begin(); while (e != extraImages.end()) { QString userFriendlyFilePath; - QString filePath = Config::findFile(config.lastLocation(), imageFiles, imageDirs, *e, - imgFileExts[(*g)->format()], userFriendlyFilePath); + QString filePath = Config::findFile(config.lastLocation(), + imageFiles, imageDirs, *e, + imgFileExts[(*g)->format()], + userFriendlyFilePath); if (!filePath.isEmpty()) - Config::copyFile(config.lastLocation(), filePath, userFriendlyFilePath, - (*g)->outputDir() + "/images"); + Config::copyFile(config.lastLocation(), + filePath, + userFriendlyFilePath, + (*g)->outputDir() + + "/images"); ++e; } } @@ -158,20 +169,23 @@ void Generator::initialize(const Config &config) QSet<QString> formats = config.subVars(formattingDotName); QSet<QString>::ConstIterator f = formats.begin(); while (f != formats.end()) { - QString def = config.getString(formattingDotName + Config::dot + - *f); + QString def = config.getString(formattingDotName + + Config::dot + *f); if (!def.isEmpty()) { int numParams = Config::numParams(def); int numOccs = def.count("\1"); if (numParams != 1) { - config.lastLocation().warning(tr("Formatting '%1' must have exactly one" - " parameter (found %2)") - .arg(*n).arg(numParams)); + config.lastLocation().warning(tr("Formatting '%1' must " + "have exactly one " + "parameter (found %2)") + .arg(*n).arg(numParams)); } else if (numOccs > 1) { - config.lastLocation().fatal(tr("Formatting '%1' must contain exactly one" - " occurrence of '\\1' (found %2)") + config.lastLocation().fatal(tr("Formatting '%1' must " + "contain exactly one " + "occurrence of '\\1' " + "(found %2)") .arg(*n).arg(numOccs)); } else { @@ -262,9 +276,14 @@ bool Generator::generateText(const Text& text, } #ifdef QDOC_QML +/*! + Extract sections of markup text surrounded by \e qmltext + and \e endqmltext and output them. + */ bool Generator::generateQmlText(const Text& text, const Node *relative, - CodeMarker *marker) + CodeMarker *marker, + const QString& qmlName) { const Atom* atom = text.firstAtom(); if (atom == 0) @@ -301,9 +320,9 @@ void Generator::generateBody(const Node *node, CodeMarker *marker) } else if (node->type() == Node::Fake) { const FakeNode *fake = static_cast<const FakeNode *>(node); - if (fake->subType() == FakeNode::Example) + if (fake->subType() == Node::Example) generateExampleFiles(fake, marker); - else if (fake->subType() == FakeNode::File) + else if (fake->subType() == Node::File) quiet = true; } @@ -395,7 +414,8 @@ void Generator::generateBody(const Node *node, CodeMarker *marker) FunctionNode *primaryFunc = func->parent()->findFunctionNode(func->name()); if (primaryFunc) { - foreach (const Parameter ¶m, primaryFunc->parameters()) { + foreach (const Parameter ¶m, + primaryFunc->parameters()) { if (param.name() == *a) { needWarning = false; break; @@ -405,7 +425,8 @@ void Generator::generateBody(const Node *node, CodeMarker *marker) } if (needWarning && !func->isReimp()) node->doc().location().warning( - tr("Undocumented parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node))); + tr("Undocumented parameter '%1' in %2") + .arg(*a).arg(marker->plainFullName(node))); } ++a; } @@ -427,7 +448,7 @@ void Generator::generateBody(const Node *node, CodeMarker *marker) if (node->type() == Node::Fake) { const FakeNode *fake = static_cast<const FakeNode *>(node); - if (fake->subType() == FakeNode::File) { + if (fake->subType() == Node::File) { Text text; Quoter quoter; Doc::quoteFromFile(fake->doc().location(), quoter, fake->name()); @@ -474,7 +495,8 @@ void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker) if ((*r).access == Node::Protected) { text << " (protected)"; - } else if ((*r).access == Node::Private) { + } + else if ((*r).access == Node::Private) { text << " (private)"; } text << separator(index++, classe->baseClasses().count()); @@ -485,6 +507,15 @@ void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker) } } +#ifdef QDOC_QML +/*! + */ +void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* ) +{ + // stub. +} +#endif + void Generator::generateInheritedBy(const ClassNode *classe, CodeMarker *marker) { @@ -512,18 +543,21 @@ void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker) QString exampleFile = child->name(); openedList.next(); text << Atom(Atom::ListItemNumber, openedList.numberString()) - << Atom(Atom::ListItemLeft, openedList.styleString()) << Atom::ParaLeft + << Atom(Atom::ListItemLeft, openedList.styleString()) + << Atom::ParaLeft << Atom(Atom::Link, exampleFile) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) << exampleFile << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) - << Atom::ParaRight << Atom(Atom::ListItemRight, openedList.styleString()); + << Atom::ParaRight + << Atom(Atom::ListItemRight, openedList.styleString()); } text << Atom(Atom::ListRight, openedList.styleString()); generateText(text, fake, marker); } -void Generator::generateModuleWarning(const ClassNode *classe, CodeMarker *marker) +void Generator::generateModuleWarning(const ClassNode *classe, + CodeMarker *marker) { QString module = classe->moduleName(); if (!module.isEmpty()) { @@ -544,8 +578,10 @@ void Generator::generateModuleWarning(const ClassNode *classe, CodeMarker *marke << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << Atom::ParaRight; } - else if (module == "Qt3Support" && Tokenizer::isTrue("defined(opensourceedition)")) { - text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) + else if (module == "Qt3Support" && + Tokenizer::isTrue("defined(opensourceedition)")) { + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "Note to Qt Desktop Light Edition users:" << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " This class is only available in the " @@ -573,10 +609,12 @@ QString Generator::indent(int level, const QString& markedCode) if (markedCode.at(i - 1) == QLatin1Char('>')) break; } - } else { + } + else { if (markedCode.at(i) == QLatin1Char('\n')) { column = 0; - } else { + } + else { if (column == 0) { for (int j = 0; j < level; j++) t += QLatin1Char(' '); @@ -645,7 +683,7 @@ void Generator::setImageFileExtensions(const QStringList& extensions) void Generator::unknownAtom(const Atom *atom) { Location::internalError(tr("unknown atom type '%1' in %2 generator") - .arg(atom->typeString()).arg(format())); + .arg(atom->typeString()).arg(format())); } bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType) @@ -674,7 +712,8 @@ void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList) alternateFunc = func->parent()->findFunctionNode(alternateName); } } - } else if (!func->name().isEmpty()) { + } + else if (!func->name().isEmpty()) { alternateName = "set"; alternateName += func->name()[0].toUpper(); alternateName += func->name().mid(1); @@ -730,9 +769,13 @@ void Generator::generateStatus(const Node *node, CodeMarker *marker) case Node::Main: break; case Node::Preliminary: - text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "This " - << typeString(node) << " is under development and is subject to change." - << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << Atom::ParaRight; + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) + << "This " + << typeString(node) + << " is under development and is subject to change." + << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) + << Atom::ParaRight; break; case Node::Deprecated: text << Atom::ParaLeft; @@ -750,16 +793,21 @@ void Generator::generateStatus(const Node *node, CodeMarker *marker) text << "This " << typeString(node) << " is obsolete."; if (node->isInnerNode()) text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD); - text << " It is provided to keep old source code working. We strongly advise against " + text << " It is provided to keep old source code working. " + << "We strongly advise against " << "using it in new code." << Atom::ParaRight; break; case Node::Compat: // reimplemented in HtmlGenerator subclass if (node->isInnerNode()) { - text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) << "This " - << typeString(node) << " is part of the Qt 3 compatibility layer." + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) + << "This " + << typeString(node) + << " is part of the Qt 3 compatibility layer." << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) - << " It is provided to keep old source code working. We strongly advise against " + << " It is provided to keep old source code working. " + << "We strongly advise against " << "using it in new code. See " << Atom(Atom::AutoLink, "Porting to Qt 4") << " for more information." @@ -915,7 +963,9 @@ void Generator::generateSince(const Node *node, CodeMarker *marker) { if (!node->since().isEmpty()) { Text text; - text << Atom::ParaLeft << "This " << typeString(node) + text << Atom::ParaLeft + << "This " + << typeString(node) << " was introduced in "; if (project.isEmpty()) text << "version"; @@ -945,7 +995,8 @@ void Generator::generateReimplementedFrom(const FunctionNode *func, { if (func->reimplementedFrom() != 0) { const FunctionNode *from = func->reimplementedFrom(); - if (from->access() != Node::Private && from->parent()->access() != Node::Private) { + if (from->access() != Node::Private && + from->parent()->access() != Node::Private) { Text text; text << Atom::ParaLeft << "Reimplemented from "; QString fullName = from->parent()->name() + "::" + from->name() + "()"; @@ -987,8 +1038,8 @@ const Atom *Generator::generateAtomList(const Atom *atom, if (atom->type() == Atom::FormatEndif) { if (generate && numAtoms0 == numAtoms) { - relative->location().warning(tr("Output format %1 not handled"). - arg(format())); + relative->location().warning(tr("Output format %1 not handled") + .arg(format())); Atom unhandledFormatAtom(Atom::UnhandledFormat, format()); generateAtomList(&unhandledFormatAtom, relative, @@ -999,7 +1050,8 @@ const Atom *Generator::generateAtomList(const Atom *atom, atom = atom->next(); } } - else if (atom->type() == Atom::FormatElse || atom->type() == Atom::FormatEndif) { + else if (atom->type() == Atom::FormatElse || + atom->type() == Atom::FormatEndif) { return atom; } else { @@ -1067,7 +1119,8 @@ void Generator::appendSortedNames(Text& text, r = classes.begin(); while (r != classes.end()) { - if ((*r).node->access() == Node::Public && (*r).node->status() != Node::Internal + if ((*r).node->access() == Node::Public && + (*r).node->status() != Node::Internal && !(*r).node->doc().isEmpty()) { Text className; appendFullName(className, (*r).node, classe, marker); diff --git a/tools/qdoc3/generator.h b/tools/qdoc3/generator.h index cdc4c29..8e3c57e 100644 --- a/tools/qdoc3/generator.h +++ b/tools/qdoc3/generator.h @@ -99,7 +99,10 @@ class Generator #ifdef QDOC_QML virtual bool generateQmlText(const Text& text, const Node *relative, - CodeMarker *marker); + CodeMarker *marker, + const QString& qmlName); + virtual void generateQmlInherits(const QmlClassNode* cn, + CodeMarker* marker); #endif virtual void generateBody(const Node *node, CodeMarker *marker); virtual void generateAlsoList(const Node *node, CodeMarker *marker); diff --git a/tools/qdoc3/helpprojectwriter.cpp b/tools/qdoc3/helpprojectwriter.cpp index cf7c618..f862e55 100644 --- a/tools/qdoc3/helpprojectwriter.cpp +++ b/tools/qdoc3/helpprojectwriter.cpp @@ -118,16 +118,19 @@ void HelpProjectWriter::readSelectors(SubProject &subproject, const QStringList typeHash["variable"] = Node::Variable; typeHash["target"] = Node::Target; - QHash<QString, FakeNode::SubType> subTypeHash; - subTypeHash["example"] = FakeNode::Example; - subTypeHash["headerfile"] = FakeNode::HeaderFile; - subTypeHash["file"] = FakeNode::File; - subTypeHash["group"] = FakeNode::Group; - subTypeHash["module"] = FakeNode::Module; - subTypeHash["page"] = FakeNode::Page; - subTypeHash["externalpage"] = FakeNode::ExternalPage; - - QSet<FakeNode::SubType> allSubTypes = QSet<FakeNode::SubType>::fromList(subTypeHash.values()); + QHash<QString, Node::SubType> subTypeHash; + subTypeHash["example"] = Node::Example; + subTypeHash["headerfile"] = Node::HeaderFile; + subTypeHash["file"] = Node::File; + subTypeHash["group"] = Node::Group; + subTypeHash["module"] = Node::Module; + subTypeHash["page"] = Node::Page; + subTypeHash["externalpage"] = Node::ExternalPage; +#ifdef QDOC_QML + subTypeHash["qmlclass"] = Node::QmlClass; +#endif + + QSet<Node::SubType> allSubTypes = QSet<Node::SubType>::fromList(subTypeHash.values()); foreach (const QString &selector, selectors) { QStringList pieces = selector.split(":"); @@ -139,7 +142,7 @@ void HelpProjectWriter::readSelectors(SubProject &subproject, const QStringList QString lower = pieces[0].toLower(); pieces = pieces[1].split(","); if (typeHash.contains(lower)) { - QSet<FakeNode::SubType> subTypes; + QSet<Node::SubType> subTypes; for (int i = 0; i < pieces.size(); ++i) { QString lower = pieces[i].toLower(); if (subTypeHash.contains(lower)) @@ -235,7 +238,7 @@ bool HelpProjectWriter::generateSection(HelpProject &project, // mask. const FakeNode *fakeNode = static_cast<const FakeNode *>(node); if (subproject.selectors[node->type()].contains(fakeNode->subType()) && - fakeNode->subType() != FakeNode::ExternalPage && + fakeNode->subType() != Node::ExternalPage && !fakeNode->fullTitle().isEmpty()) project.subprojects[name].nodes[objName] = node; @@ -324,10 +327,10 @@ bool HelpProjectWriter::generateSection(HelpProject &project, // attributes. case Node::Fake: { const FakeNode *fakeNode = static_cast<const FakeNode*>(node); - if (fakeNode->subType() != FakeNode::ExternalPage && + if (fakeNode->subType() != Node::ExternalPage && !fakeNode->fullTitle().isEmpty()) { - if (fakeNode->subType() != FakeNode::File) { + if (fakeNode->subType() != Node::File) { if (fakeNode->doc().hasKeywords()) { foreach (const Atom *keyword, fakeNode->doc().keywords()) { if (!keyword->string().isEmpty()) { @@ -485,7 +488,7 @@ void HelpProjectWriter::writeNode(HelpProject &project, QXmlStreamWriter &writer writer.writeAttribute("title", fakeNode->fullTitle()); // qDebug() << "Title:" << fakeNode->fullTitle(); - if (fakeNode->subType() == FakeNode::HeaderFile) { + if (fakeNode->subType() == Node::HeaderFile) { // Write subsections for all members, obsolete members and Qt 3 // members. @@ -609,7 +612,7 @@ void HelpProjectWriter::generateProject(HelpProject &project) while (nextPage) { writeNode(project, writer, nextPage); nextTitle = nextPage->links().value(Node::NextLink).first; - if (nextTitle.isEmpty()) + if(nextTitle.isEmpty()) break; nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle)); } diff --git a/tools/qdoc3/htmlgenerator.cpp b/tools/qdoc3/htmlgenerator.cpp index 68cad18..df63138 100644 --- a/tools/qdoc3/htmlgenerator.cpp +++ b/tools/qdoc3/htmlgenerator.cpp @@ -224,11 +224,21 @@ void HtmlGenerator::initializeGenerator(const Config &config) i++; } - style = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_STYLE); - postHeader = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_POSTHEADER); - footer = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_FOOTER); - address = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_ADDRESS); - pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_GENERATEMACREFS); + style = config.getString(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_STYLE); + postHeader = config.getString(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_POSTHEADER); + footer = config.getString(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_FOOTER); + address = config.getString(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_ADDRESS); + pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_GENERATEMACREFS); project = config.getString(CONFIG_PROJECT); @@ -242,10 +252,16 @@ void HtmlGenerator::initializeGenerator(const Config &config) QSet<QString>::ConstIterator edition = editionNames.begin(); while (edition != editionNames.end()) { QString editionName = *edition; - QStringList editionModules = config.getStringList( - CONFIG_EDITION + Config::dot + editionName + Config::dot + "modules"); - QStringList editionGroups = config.getStringList( - CONFIG_EDITION + Config::dot + editionName + Config::dot + "groups"); + QStringList editionModules = config.getStringList(CONFIG_EDITION + + Config::dot + + editionName + + Config::dot + + "modules"); + QStringList editionGroups = config.getStringList(CONFIG_EDITION + + Config::dot + + editionName + + Config::dot + + "groups"); if (!editionModules.isEmpty()) editionModuleMap[editionName] = editionModules; @@ -257,11 +273,17 @@ void HtmlGenerator::initializeGenerator(const Config &config) slow = config.getBool(CONFIG_SLOW); - stylesheets = config.getStringList(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_STYLESHEETS); - customHeadElements = config.getStringList(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_CUSTOMHEADELEMENTS); + stylesheets = config.getStringList(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_STYLESHEETS); + customHeadElements = config.getStringList(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_CUSTOMHEADELEMENTS); codeIndent = config.getInt(CONFIG_CODEINDENT); - helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp"); + helpProjectWriter = new HelpProjectWriter(config, + project.toLower() + + ".qhp"); } void HtmlGenerator::terminateGenerator() @@ -305,9 +327,9 @@ void HtmlGenerator::generateTree(const Tree *tree, CodeMarker *marker) findAllFunctions(tree->root()); findAllLegaleseTexts(tree->root()); findAllNamespaces(tree->root()); -#ifdef ZZZ_QDOC_QML +#ifdef ZZZ_QDOC_QML findAllQmlClasses(tree->root()); -#endif +#endif PageGenerator::generateTree(tree, marker); @@ -411,7 +433,8 @@ int HtmlGenerator::generateAtom(const Atom *atom, QString str; atom = atom->next(); while (atom != 0 && atom->type() != Atom::BriefRight) { - if (atom->type() == Atom::String || atom->type() == Atom::AutoLink) + if (atom->type() == Atom::String || + atom->type() == Atom::AutoLink) str += atom->string(); skipAhead++; atom = atom->next(); @@ -454,14 +477,14 @@ int HtmlGenerator::generateAtom(const Atom *atom, marker,relative)) << "</pre>\n"; break; -#ifdef QDOC_QML +#ifdef QDOC_QML case Atom::Qml: out() << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), marker,relative)) << "</pre>\n"; break; -#endif +#endif case Atom::CodeNew: out() << "<p>you can rewrite it as</p>\n" << "<pre>" @@ -514,6 +537,17 @@ int HtmlGenerator::generateAtom(const Atom *atom, out() << formattingRightMap()[atom->string()]; } break; + case Atom::AnnotatedList: + { + QList<Node*> values = tre->groups().values(atom->string()); + QMap<QString, const Node*> nodeMap; + for (int i = 0; i < values.size(); ++i) { + const Node* n = values.at(i); + nodeMap.insert(n->name(),n); + } + generateAnnotatedList(relative, marker, nodeMap); + } + break; case Atom::GeneratedList: if (atom->string() == "annotatedclasses") { generateAnnotatedList(relative, marker, nonCompatClasses); @@ -977,7 +1011,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, case Atom::EndQmlText: // don't do anything with these. They are just tags. break; -#endif +#endif default: unknownAtom(atom); } @@ -1043,6 +1077,12 @@ void HtmlGenerator::generateClassLikeNode(const InnerNode *inner, generateHeader(title, inner, marker, true); generateTitle(title, subtitleText, SmallSubTitle, inner, marker); +#ifdef QDOC_QML + if (classe && !classe->qmlElement().isEmpty()) { + generateInstantiatedBy(classe,marker); + } +#endif + generateBrief(inner, marker); generateIncludes(inner, marker); generateStatus(inner, marker); @@ -1226,16 +1266,19 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker) QList<Section>::const_iterator s; QString htmlTitle = fake->fullTitle(); - if (fake->subType() == FakeNode::File && !fake->subTitle().isEmpty()) { + if (fake->subType() == Node::File && !fake->subTitle().isEmpty()) { subTitleSize = SmallSubTitle; htmlTitle += " (" + fake->subTitle() + ")"; } generateHeader(htmlTitle, fake, marker, true); - generateTitle(fake->fullTitle(), Text() << fake->subTitle(), subTitleSize, - fake, marker); + generateTitle(fake->fullTitle(), + Text() << fake->subTitle(), + subTitleSize, + fake, + marker); - if (fake->subType() == FakeNode::Module) { + if (fake->subType() == Node::Module) { // Generate brief text and status for modules. generateBrief(fake, marker); generateStatus(fake, marker); @@ -1249,7 +1292,7 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker) generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]); } } - else if (fake->subType() == FakeNode::HeaderFile) { + else if (fake->subType() == Node::HeaderFile) { // Generate brief text and status for modules. generateBrief(fake, marker); generateStatus(fake, marker); @@ -1296,7 +1339,49 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker) appendDcfSubSection(&fakeSection, compatSection); } } +#ifdef QDOC_QML + else if (fake->subType() == Node::QmlClass) { + const QmlClassNode* qml_cn = static_cast<const QmlClassNode*>(fake); + const ClassNode* cn = qml_cn->classNode(); + generateQmlInherits(qml_cn, marker); + generateQmlInstantiates(qml_cn, marker); + generateBrief(qml_cn, marker); + sections = marker->qmlSections(qml_cn,CodeMarker::Summary); + s = sections.begin(); + while (s != sections.end()) { + out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n"; + out() << "<h2>" << protect((*s).name) << "</h2>\n"; + generateQmlSummary(*s,fake,marker); + ++s; + } + out() << "<a name=\"" << registerRef("details") << "\"></a>\n"; + out() << "<h2>" << "Detailed Description" << "</h2>\n"; + generateBody(fake, marker); + if (cn) + generateQmlText(cn->doc().body(), cn, marker, fake->name()); + generateAlsoList(fake, marker); + out() << "<hr />\n"; + + sections = marker->qmlSections(qml_cn,CodeMarker::Detailed); + s = sections.begin(); + while (s != sections.end()) { + out() << "<h2>" << protect((*s).name) << "</h2>\n"; + NodeList::ConstIterator m = (*s).members.begin(); + while (m != (*s).members.end()) { + generateDetailedQmlMember(*m, fake, marker); + out() << "<br />\n"; + fakeSection.keywords += qMakePair((*m)->name(), + linkForNode(*m,0)); + ++m; + } + ++s; + } + generateFooter(fake); + return; + } +#endif + sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay); s = sections.begin(); while (s != sections.end()) { @@ -1307,24 +1392,12 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker) } Text brief = fake->doc().briefText(); - if (fake->subType() == FakeNode::Module && !brief.isEmpty()) { + if (fake->subType() == Node::Module && !brief.isEmpty()) { out() << "<a name=\"" << registerRef("details") << "\"></a>\n"; out() << "<h2>" << "Detailed Description" << "</h2>\n"; } generateBody(fake, marker); -#ifdef QDOC_QML - if (fake->subType() == FakeNode::QmlClass) { - //qDebug() << "generateFakeNode(): QML CLASS" << fake->name(); - const QmlNode* qmlNode = static_cast<const QmlNode*>(fake); - const ClassNode* cn = qmlNode->classNode(); - if (cn) { - //qDebug() << " CPP CLASS" << cn->name(); - generateQmlText(cn->doc().body(), cn, marker); - } - } -#endif - generateAlsoList(fake, marker); if (!fake->groupMembers().isEmpty()) { @@ -1354,10 +1427,10 @@ void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker) } generateFooter(fake); - if (fake->subType() == FakeNode::Example) { + if (fake->subType() == Node::Example) { appendDcfSubSection(&dcfExamplesRoot, fakeSection); } - else if (fake->subType() != FakeNode::File) { + else if (fake->subType() != Node::File) { QString contentsPage = fake->links().value(Node::ContentsLink).first; if (contentsPage == "Qt Designer Manual") { @@ -1644,7 +1717,10 @@ void HtmlGenerator::generateTableOfContents(const Node *node, columnSize = 0; } out() << "<li>"; - out() << "<a href=\"" << nodeName << "#" << Doc::canonicalTitle(headingText.toString()) + out() << "<a href=\"" + << nodeName + << "#" + << Doc::canonicalTitle(headingText.toString()) << "\">"; generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms); out() << "</a></li>\n"; @@ -1694,7 +1770,8 @@ void HtmlGenerator::generateNavigationBar(const NavigationBar& bar, } #endif -QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, CodeMarker *marker) +QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, + CodeMarker *marker) { QList<Section> sections; QList<Section>::ConstIterator s; @@ -1745,7 +1822,8 @@ QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner, if (status == CodeMarker::Compat) { title = "Qt 3 Support Members for " + inner->name(); fileName = fileBase(inner) + "-qt3." + fileExtension(inner); - } else { + } + else { title = "Obsolete Members for " + inner->name(); fileName = fileBase(inner) + "-obsolete." + fileExtension(inner); } @@ -1759,10 +1837,11 @@ QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner, "<a href=\"qt3support.html\">Qt 3 support layer</a>.</b> " "They are provided to help you port old code to Qt 4. We advise against " "using them in new code.</p>\n"; - } else { - out() << "<p><b>The following class members are obsolete.</b> They are provided to keep " - "old source code working. We strongly advise against using them in new " - "code.</p>\n"; + } + else { + out() << "<p><b>The following class members are obsolete.</b> " + << "They are provided to keep old source code working. " + << "We strongly advise against using them in new code.</p>\n"; } out() << "<p><ul><li><a href=\"" @@ -1817,8 +1896,10 @@ void HtmlGenerator::generateClassHierarchy(const Node *relative, if (stack.top().isEmpty()) { stack.pop(); out() << "</ul>\n"; - } else { - const ClassNode *child = static_cast<const ClassNode *>(*stack.top().begin()); + } + else { + const ClassNode *child = + static_cast<const ClassNode *>(*stack.top().begin()); out() << "<li>"; generateFullName(child, relative, marker); out() << "</li>\n"; @@ -1837,10 +1918,9 @@ void HtmlGenerator::generateClassHierarchy(const Node *relative, } } -void -HtmlGenerator::generateAnnotatedList(const Node *relative, - CodeMarker *marker, - const QMap<QString,const Node *>&nodeMap) +void HtmlGenerator::generateAnnotatedList(const Node *relative, + CodeMarker *marker, + const QMap<QString, const Node *> &nodeMap) { out() << "<p><table width=\"100%\" class=\"annotated\" cellpadding=\"2\" " << "cellspacing=\"1\" border=\"0\">\n"; @@ -1867,7 +1947,8 @@ HtmlGenerator::generateAnnotatedList(const Node *relative, generateText(brief, node, marker); out() << "</td>"; } - } else { + } + else { out() << "<td>"; out() << protect(node->doc().briefText().toString()); out() << "</td>"; @@ -1877,10 +1958,9 @@ HtmlGenerator::generateAnnotatedList(const Node *relative, out() << "</table></p>\n"; } -void -HtmlGenerator::generateCompactList(const Node *relative, - CodeMarker *marker, - const QMap<QString,const Node*> &classMap) +void HtmlGenerator::generateCompactList(const Node *relative, + CodeMarker *marker, + const QMap<QString, const Node *> &classMap) { const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_' const int NumColumns = 4; // number of columns in the result @@ -1923,8 +2003,9 @@ HtmlGenerator::generateCompactList(const Node *relative, last = classMap.begin().key(); if (classMap.size() > 1) { - while (commonPrefixLen < first.length() + 1 && commonPrefixLen < last.length() + 1 - && first[commonPrefixLen] == last[commonPrefixLen]) + while (commonPrefixLen < first.length() + 1 && + commonPrefixLen < last.length() + 1 && + first[commonPrefixLen] == last[commonPrefixLen]) ++commonPrefixLen; } @@ -1953,7 +2034,8 @@ HtmlGenerator::generateCompactList(const Node *relative, if (key[0].digitValue() != -1) { paragraphNo = key[0].digitValue(); - } else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) { + } + else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) { paragraphNo = 10 + key[0].unicode() - 'a'; } @@ -2015,6 +2097,11 @@ HtmlGenerator::generateCompactList(const Node *relative, currentOffsetInParagraph[i] = 0; } + if (currentParagraphNo[i] >= NumParagraphs) { + qDebug() << "### Internal error ###" << __FILE__ << __LINE__; + currentParagraphNo[i] = NumParagraphs - 1; + } + out() << "<td align=\"right\">"; if (currentOffsetInParagraph[i] == 0) { // start a new paragraph @@ -2106,7 +2193,8 @@ void HtmlGenerator::generateFunctionIndex(const Node *relative, #endif } -void HtmlGenerator::generateLegaleseList(const Node *relative, CodeMarker *marker) +void HtmlGenerator::generateLegaleseList(const Node *relative, + CodeMarker *marker) { QMap<Text, const Node *>::ConstIterator it = legaleseTexts.begin(); while (it != legaleseTexts.end()) { @@ -2124,6 +2212,77 @@ void HtmlGenerator::generateLegaleseList(const Node *relative, CodeMarker *marke } } +/*void HtmlGenerator::generateSynopsis(const Node *node, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style) +{ + QString marked = marker->markedUpSynopsis(node, relative, style); + QRegExp templateTag("(<[^@>]*>)"); + if (marked.indexOf(templateTag) != -1) { + QString contents = protect(marked.mid(templateTag.pos(1), + templateTag.cap(1).length())); + marked.replace(templateTag.pos(1), templateTag.cap(1).length(), + contents); + } + marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), + "<i>\\1<sub>\\2</sub></i>"); + marked.replace("<@param>", "<i>"); + marked.replace("</@param>", "</i>"); + + if (style == CodeMarker::Summary) + marked.replace("@name>", "b>"); + + if (style == CodeMarker::SeparateList) { + QRegExp extraRegExp("<@extra>.*</@extra>"); + extraRegExp.setMinimal(true); + marked.replace(extraRegExp, ""); + } + else { + marked.replace("<@extra>", " <tt>"); + marked.replace("</@extra>", "</tt>"); + } + + if (style != CodeMarker::Detailed) { + marked.replace("<@type>", ""); + marked.replace("</@type>", ""); + } + out() << highlightedCode(marked, marker, relative); +}*/ + +#ifdef QDOC_QML +void HtmlGenerator::generateQmlItem(const Node *node, + const Node *relative, + CodeMarker *marker, + bool summary) +{ + QString marked = marker->markedUpQmlItem(node,summary); + QRegExp templateTag("(<[^@>]*>)"); + if (marked.indexOf(templateTag) != -1) { + QString contents = protect(marked.mid(templateTag.pos(1), + templateTag.cap(1).length())); + marked.replace(templateTag.pos(1), templateTag.cap(1).length(), + contents); + } + marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), + "<i>\\1<sub>\\2</sub></i>"); + marked.replace("<@param>", "<i>"); + marked.replace("</@param>", "</i>"); + + if (summary) + marked.replace("@name>", "b>"); + + marked.replace("<@extra>", " <tt>"); + marked.replace("</@extra>", "</tt>"); + + if (summary) { + marked.replace("<@type>", ""); + marked.replace("</@type>", ""); + } + out() << highlightedCode(marked, marker, relative); +} +#endif + void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */) { QMap<const FakeNode *, QMap<QString, FakeNode *> > fakeNodeMap; @@ -2146,7 +2305,7 @@ void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* m } // there are too many examples; they would clutter the list - if (fakeNode->subType() == FakeNode::Example) + if (fakeNode->subType() == Node::Example) continue; // not interested either in individual (Qt Designer etc.) manual chapters @@ -2154,7 +2313,7 @@ void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* m continue; // Discard external nodes. - if (fakeNode->subType() == FakeNode::ExternalPage) + if (fakeNode->subType() == Node::ExternalPage) continue; QString sortKey = fakeNode->fullTitle().toLower(); @@ -2179,7 +2338,8 @@ void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* m groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode *>(fakeNode); } } - } else if (!isGroupPage) { + } + else if (!isGroupPage) { // If we encounter a page that belongs to a group then // we add that page to the list for that group. const FakeNode *groupNode = static_cast<const FakeNode *>(tre->root()->findNode(group, Node::Fake)); @@ -2738,8 +2898,8 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, if (src.at(i) == charLangle && src.at(i + 1) == charAt) { i += 2; if (parseArg(src, linkTag, &i, n, &arg, &par1)) { - QString link = linkForNode( - CodeMarker::nodeForString(par1.toString()), relative); + const Node* node = CodeMarker::nodeForString(par1.toString()); + QString link = linkForNode(node, relative); addLink(link, arg, &html); } else { @@ -2752,7 +2912,6 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, } } - if (slow) { // is this block ever used at all? // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)" @@ -3077,10 +3236,9 @@ QString HtmlGenerator::fileBase(const Node *node, QString HtmlGenerator::fileName(const Node *node) { if (node->type() == Node::Fake) { - if (static_cast<const FakeNode *>(node)->subType() == FakeNode::ExternalPage) + if (static_cast<const FakeNode *>(node)->subType() == Node::ExternalPage) return node->name(); } - return PageGenerator::fileName(node); } @@ -3102,7 +3260,8 @@ QString HtmlGenerator::refForNode(const Node *node) typedeffe = static_cast<const TypedefNode *>(node); if (typedeffe->associatedEnum()) { return refForNode(typedeffe->associatedEnum()); - } else { + } + else { ref = node->name() + "-typedef"; } break; @@ -3110,15 +3269,27 @@ QString HtmlGenerator::refForNode(const Node *node) func = static_cast<const FunctionNode *>(node); if (func->associatedProperty()) { return refForNode(func->associatedProperty()); - } else { + } + else { ref = func->name(); if (func->overloadNumber() != 1) ref += "-" + QString::number(func->overloadNumber()); } break; case Node::Property: +#ifdef QDOC_QML + case Node::QmlProperty: +#endif ref = node->name() + "-prop"; break; +#ifdef QDOC_QML + case Node::QmlSignal: + ref = node->name() + "-signal"; + break; + case Node::QmlMethod: + ref = node->name() + "-method"; + break; +#endif case Node::Variable: ref = node->name() + "-var"; break; @@ -3167,9 +3338,11 @@ QString HtmlGenerator::refForAtom(Atom *atom, const Node * /* node */) { if (atom->type() == Atom::SectionLeft) { return Doc::canonicalTitle(Text::sectionHeading(atom).toString()); - } else if (atom->type() == Atom::Target) { + } + else if (atom->type() == Atom::Target) { return Doc::canonicalTitle(atom->string()); - } else { + } + else { return QString(); } } @@ -3213,7 +3386,10 @@ void HtmlGenerator::generateDetailedMember(const Node *node, out() << "<a name=\"" + refForNode(node) + "\"></a>"; generateSynopsis(enume, relative, marker, CodeMarker::Detailed); out() << "<br />"; - generateSynopsis(enume->flagsType(), relative, marker, CodeMarker::Detailed); + generateSynopsis(enume->flagsType(), + relative, + marker, + CodeMarker::Detailed); out() << "</h3>\n"; } else { @@ -3365,7 +3541,7 @@ void HtmlGenerator::findAllNamespaces(const InnerNode *node) } } -#ifdef ZZZ_QDOC_QML +#ifdef ZZZ_QDOC_QML /*! This function finds all the qml element nodes and stores them in a map for later use. @@ -3376,24 +3552,16 @@ void HtmlGenerator::findAllQmlClasses(const InnerNode *node) while (c != node->childNodes().constEnd()) { if ((*c)->type() == Node::Fake) { const FakeNode* fakeNode = static_cast<const FakeNode *>(*c); - if (fakeNode->subType() == FakeNode::QmlClass) { - const QmlNode* qmlNode = static_cast<const QmlNode*>(fakeNode); - //qDebug() << "HtmlGenerator: QML CLASS" << qmlNode->name(); + if (fakeNode->subType() == Node::QmlClass) { + const QmlClassNode* qmlNode = + static_cast<const QmlClassNode*>(fakeNode); const Node* n = qmlNode->classNode(); - if (n) - //qDebug() << " FOUND IT!" << n->name(); } qmlClasses.insert(fakeNode->name(),*c); } ++c; } } -#endif - -#if 0 - else if ((*c)->isInnerNode()) { - findAllClasses(static_cast<InnerNode *>(*c)); - } #endif int HtmlGenerator::hOffset(const Node *node) @@ -3708,3 +3876,214 @@ void HtmlGenerator::endLink() } QT_END_NAMESPACE + +#ifdef QDOC_QML + +/*! + Generates the summary for for the \a section. Only used for + sections of QML element documentation. + + Currently handles only the QML property group. + */ +void HtmlGenerator::generateQmlSummary(const Section& section, + const Node *relative, + CodeMarker *marker) +{ + if (!section.members.isEmpty()) { + NodeList::ConstIterator m; + int count = section.members.size(); + bool twoColumn = false; + if (section.members.first()->type() == Node::QmlProperty) { + twoColumn = (count >= 5); + } + if (twoColumn) + out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\"" + " cellspacing=\"0\">\n" + << "<tr><td width=\"45%\" valign=\"top\">"; + out() << "<ul>\n"; + + int row = 0; + m = section.members.begin(); + while (m != section.members.end()) { + if (twoColumn && row == (int) (count + 1) / 2) + out() << "</ul></td><td valign=\"top\"><ul>\n"; + out() << "<li><div class=\"fn\"></div>"; + generateQmlItem(*m,relative,marker,true); + out() << "</li>\n"; + row++; + ++m; + } + out() << "</ul>\n"; + if (twoColumn) + out() << "</td></tr>\n</table></p>\n"; + } +} + +/*! + Outputs the html detailed documentation for a section + on a QML element reference page. + */ +void HtmlGenerator::generateDetailedQmlMember(const Node *node, + const InnerNode *relative, + CodeMarker *marker) +{ + const QmlPropertyNode* qpn = 0; + generateMacRef(node, marker); + out() << "<div class=\"qmlitem\">"; + if (node->subType() == Node::QmlPropertyGroup) { + const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node); + NodeList::ConstIterator p = qpgn->childNodes().begin(); + out() << "<div class=\"qmlproto\">"; + out() << "<table class=\"qmlname\">"; + + while (p != qpgn->childNodes().end()) { + if ((*p)->type() == Node::QmlProperty) { + qpn = static_cast<const QmlPropertyNode*>(*p); + out() << "<tr><td>"; + out() << "<a name=\"" + refForNode(qpn) + "\"></a>"; + generateQmlItem(qpn, relative, marker, false); + out() << "</td></tr>"; + if (qpgn->isDefault()) { + out() << "</table>" + << "</div></div>" + << "<div class=\"qmlitem\">" + << "<div class=\"qmlproto\">" + << "<table class=\"qmlname\">" + << "<tr><td><font color=\"green\">" + << "default</font></td></tr>"; + } + } + ++p; + } + out() << "</table>"; + out() << "</div>"; + } + else if (node->type() == Node::QmlSignal) { + const QmlSignalNode* qsn = static_cast<const QmlSignalNode*>(node); + out() << "<div class=\"qmlproto\">"; + out() << "<table class=\"qmlname\">"; + out() << "<tr><td>"; + out() << "<a name=\"" + refForNode(qsn) + "\"></a>"; + generateQmlItem(qsn,relative,marker,false); + out() << "</td></tr>"; + out() << "</table>"; + out() << "</div>"; + } + else if (node->type() == Node::QmlMethod) { + const QmlMethodNode* qmn = static_cast<const QmlMethodNode*>(node); + out() << "<div class=\"qmlproto\">"; + out() << "<table class=\"qmlname\">"; + out() << "<tr><td>"; + out() << "<a name=\"" + refForNode(qmn) + "\"></a>"; + generateQmlItem(qmn,relative,marker,false); + out() << "</td></tr>"; + out() << "</table>"; + out() << "</div>"; + } + out() << "<div class=\"qmldoc\">"; + generateStatus(node, marker); + generateBody(node, marker); + generateThreadSafeness(node, marker); + generateSince(node, marker); + generateAlsoList(node, marker); + out() << "</div>"; + out() << "</div>"; +} + +/*! + Output the "Inherits" line for the QML element, + if there should be one. + */ +void HtmlGenerator::generateQmlInherits(const QmlClassNode* cn, + CodeMarker* marker) +{ + if (cn && !cn->links().empty()) { + if (cn->links().contains(Node::InheritsLink)) { + QPair<QString,QString> linkPair; + linkPair = cn->links()[Node::InheritsLink]; + QStringList strList(linkPair.first); + const Node* n = tre->findNode(strList,Node::Fake); + if (n && n->subType() == Node::QmlClass) { + const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n); + out() << "<p style=\"text-align: center\">"; + Text text; + text << "[Inherits "; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, linkPair.second); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << "]"; + generateText(text, cn, marker); + out() << "</p>"; + } +// else +// qDebug() << "generateQmlInherits(): " +// << "Inherited element not documented -->" +// << linkPair.first; + } + } +} + +/*! + Output the "[Xxx instantiates the C++ class QFxXxx]" + line for the QML element, if there should be one. + + If there is no class node, or if the class node status + is set to Node::Internal, do nothing. + */ +void HtmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn, + CodeMarker* marker) +{ + const ClassNode* cn = qcn->classNode(); + if (cn && (cn->status() != Node::Internal)) { + out() << "<p style=\"text-align: center\">"; + Text text; + text << "["; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, qcn->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << " instantiates the C++ class "; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, cn->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << "]"; + generateText(text, qcn, marker); + out() << "</p>"; + } +} + +/*! + Output the "[QFxXxx is instantiated by QML element Xxx]" + line for the class, if there should be one. + + If there is no QML element, or if the class node status + is set to Node::Internal, do nothing. + */ +void HtmlGenerator::generateInstantiatedBy(const ClassNode* cn, + CodeMarker* marker) +{ + if (cn && cn->status() != Node::Internal && !cn->qmlElement().isEmpty()) { + const Node* n = tre->root()->findNode(cn->qmlElement(),Node::Fake); + if (n && n->subType() == Node::QmlClass) { + out() << "<p style=\"text-align: center\">"; + Text text; + text << "["; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, cn->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << " is instantiated by QML element "; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(n)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, n->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << "]"; + generateText(text, cn, marker); + out() << "</p>"; + } + } +} + +#endif diff --git a/tools/qdoc3/htmlgenerator.h b/tools/qdoc3/htmlgenerator.h index 2ad4697..c22fe20 100644 --- a/tools/qdoc3/htmlgenerator.h +++ b/tools/qdoc3/htmlgenerator.h @@ -145,6 +145,21 @@ class HtmlGenerator : public PageGenerator const Node *relative, CodeMarker *marker, CodeMarker::SynopsisStyle style); +#ifdef QDOC_QML + void generateQmlSummary(const Section& section, + const Node *relative, + CodeMarker *marker); + void generateQmlItem(const Node *node, + const Node *relative, + CodeMarker *marker, + bool summary); + void generateDetailedQmlMember(const Node *node, + const InnerNode *relative, + CodeMarker *marker); + void generateQmlInherits(const QmlClassNode* cn, CodeMarker* marker); + void generateQmlInstantiates(const QmlClassNode* qcn, CodeMarker* marker); + void generateInstantiatedBy(const ClassNode* cn, CodeMarker* marker); +#endif #ifdef QDOC_NAME_ALIGNMENT void generateSection(const NodeList& nl, const Node *relative, @@ -180,8 +195,12 @@ class HtmlGenerator : public PageGenerator const Node *relative, CodeMarker *marker, const Node *actualNode = 0); - void generateDetailedMember(const Node *node, const InnerNode *relative, CodeMarker *marker); - void generateLink(const Atom *atom, const Node *relative, CodeMarker *marker); + void generateDetailedMember(const Node *node, + const InnerNode *relative, + CodeMarker *marker); + void generateLink(const Atom *atom, + const Node *relative, + CodeMarker *marker); void generateStatus(const Node *node, CodeMarker *marker); QString registerRef(const QString& ref); diff --git a/tools/qdoc3/jambiapiparser.cpp b/tools/qdoc3/jambiapiparser.cpp index f981e6d..70e9260 100644 --- a/tools/qdoc3/jambiapiparser.cpp +++ b/tools/qdoc3/jambiapiparser.cpp @@ -234,8 +234,9 @@ void JambiApiParser::doneParsingSourceFiles(Tree * /* tree */) foreach (Node *cppNode, cppTre->root()->childNodes()) { if (cppNode->type() == Node::Fake) { FakeNode *cppFake = static_cast<FakeNode *>(cppNode); - if (cppFake->subType() == FakeNode::Page) { - FakeNode *javaFake = new FakeNode(javaTre->root(), cppFake->name(), + if (cppFake->subType() == Node::Page) { + FakeNode *javaFake = new FakeNode(javaTre->root(), + cppFake->name(), cppFake->subType()); javaFake->setModuleName("com.trolltech.qt"); // ### hard-coded javaFake->setTitle(cppFake->title()); diff --git a/tools/qdoc3/node.cpp b/tools/qdoc3/node.cpp index b2e53ab..610249d 100644 --- a/tools/qdoc3/node.cpp +++ b/tools/qdoc3/node.cpp @@ -43,6 +43,7 @@ node.cpp */ +#include <QtCore> #include "node.h" QT_BEGIN_NAMESPACE @@ -113,6 +114,9 @@ void Node::setRelates(InnerNode *pseudoParent) } /*! + This function creates a pair that describes a link. + The pair is composed from \a link and \a desc. The + \a linkType is the map index the pair is filed under. */ void Node::setLink(LinkType linkType, const QString &link, const QString &desc) { @@ -623,7 +627,7 @@ void InnerNode::removeRelated(Node *pseudoChild) */ /*! - Returns false because this is an InnerNode. + Returns false because this is a LeafNode. */ bool LeafNode::isInnerNode() const { @@ -713,9 +717,11 @@ void ClassNode::fixBaseClasses() */ /*! + The type of a FakeNode is Fake, and it has a \a subtype, + which specifies the type of FakeNode. */ -FakeNode::FakeNode(InnerNode *parent, const QString& name, SubType subType) - : InnerNode(Fake, parent, name), sub(subType) +FakeNode::FakeNode(InnerNode *parent, const QString& name, SubType subtype) + : InnerNode(Fake, parent, name), sub(subtype) { } @@ -1033,4 +1039,98 @@ bool TargetNode::isInnerNode() const return false; } +#ifdef QDOC_QML +/*! + Constructor for the Qml class node. + */ +QmlClassNode::QmlClassNode(InnerNode *parent, + const QString& name, + const ClassNode* cn) + : FakeNode(parent, name, QmlClass), cnode(cn) +{ + setTitle("QML " + name + " Element Reference"); +} + +/*! + The base file name for this kind of node has "qml_" + prepended to it. + + But not yet. Still testing. + */ +QString QmlClassNode::fileBase() const +{ +#if 0 + if (Node::fileBase() == "item") + qDebug() << "FILEBASE: qmlitem" << name(); + return "qml_" + Node::fileBase(); +#endif + return Node::fileBase(); +} + +/*! + Constructor for the Qml property group node. \a parent is + always a QmlClassNode. + */ +QmlPropGroupNode::QmlPropGroupNode(QmlClassNode* parent, const QString& name) + : FakeNode(parent, name, QmlPropertyGroup), isdefault(false) +{ + // nothing. +} + +/*! + Constructor for the QML property node. + */ +QmlPropertyNode::QmlPropertyNode(QmlPropGroupNode *parent, + const QString& name, + const QString& type) + : LeafNode(QmlProperty, parent, name), + dt(type), + sto(Trool_Default), + des(Trool_Default) +{ + // nothing. +} + +/*! + I don't know what this is. + */ +QmlPropertyNode::Trool QmlPropertyNode::toTrool(bool boolean) +{ + return boolean ? Trool_True : Trool_False; +} + +/*! + I don't know what this is either. + */ +bool QmlPropertyNode::fromTrool(Trool troolean, bool defaultValue) +{ + switch (troolean) { + case Trool_True: + return true; + case Trool_False: + return false; + default: + return defaultValue; + } +} + +/*! + Constructor for the QML signal node. + */ +QmlSignalNode::QmlSignalNode(QmlClassNode *parent, const QString& name) + : LeafNode(QmlSignal, parent, name) +{ + // nothing. +} + +/*! + Constructor for the QML method node. + */ +QmlMethodNode::QmlMethodNode(QmlClassNode *parent, const QString& name) + : LeafNode(QmlMethod, parent, name) +{ + // nothing. +} +#endif + QT_END_NAMESPACE diff --git a/tools/qdoc3/node.h b/tools/qdoc3/node.h index a35bc17..17ec447 100644 --- a/tools/qdoc3/node.h +++ b/tools/qdoc3/node.h @@ -72,7 +72,31 @@ class Node Function, Property, Variable, +#ifdef QDOC_QML + Target, + QmlProperty, + QmlSignal, + QmlMethod +#else Target +#endif + }; + + enum SubType { + NoSubType, + Example, + HeaderFile, + File, + Group, + Module, + Page, +#ifdef QDOC_QML + ExternalPage, + QmlClass, + QmlPropertyGroup +#else + ExternalPage +#endif }; enum Access { Public, Protected, Private }; @@ -99,7 +123,8 @@ class Node NextLink, PreviousLink, ContentsLink, - IndexLink /*, + IndexLink, + InheritsLink /*, GlossaryLink, CopyrightLink, ChapterLink, @@ -125,6 +150,7 @@ class Node virtual bool isInnerNode() const = 0; virtual bool isReimp() const { return false; } Type type() const { return typ; } + virtual SubType subType() const { return NoSubType; } InnerNode *parent() const { return par; } InnerNode *relates() const { return rel; } const QString& name() const { return nam; } @@ -144,7 +170,7 @@ class Node void clearRelated() { rel = 0; } - QString fileBase() const; + virtual QString fileBase() const; protected: Node(Type type, InnerNode *parent, const QString& name); @@ -181,7 +207,7 @@ typedef QList<Node *> NodeList; class InnerNode : public Node { public: - ~InnerNode(); + virtual ~InnerNode(); Node *findNode(const QString& name); Node *findNode(const QString& name, Type type); @@ -203,6 +229,7 @@ class InnerNode : public Node const EnumNode *findEnumNodeForValue(const QString &enumValue) const; const NodeList & childNodes() const { return children; } const NodeList & relatedNodes() const { return related; } + int count() const { return children.size(); } int overloadNumber(const FunctionNode *func) const; int numOverloads(const QString& funcName) const; NodeList overloads(const QString &funcName) const; @@ -232,17 +259,19 @@ class LeafNode : public Node { public: LeafNode(); + virtual ~LeafNode() { } virtual bool isInnerNode() const; protected: - LeafNode(Type type, InnerNode *parent, const QString& name); + LeafNode(Type type, InnerNode* parent, const QString& name); }; class NamespaceNode : public InnerNode { public: NamespaceNode(InnerNode *parent, const QString& name); + virtual ~NamespaceNode() { } }; class ClassNode; @@ -266,6 +295,7 @@ class ClassNode : public InnerNode { public: ClassNode(InnerNode *parent, const QString& name); + virtual ~ClassNode() { } void addBaseClass(Access access, ClassNode *node, @@ -280,29 +310,23 @@ class ClassNode : public InnerNode QString serviceName() const { return sname; } void setServiceName(const QString& value) { sname = value; } + QString qmlElement() const { return qmlelement; } + void setQmlElement(const QString& value) { qmlelement = value; } private: QList<RelatedClass> bas; QList<RelatedClass> der; bool hidden; QString sname; + QString qmlelement; }; class FakeNode : public InnerNode { public: - enum SubType { - Example, - HeaderFile, - File, - Group, - Module, - Page, - ExternalPage, - QmlClass - }; FakeNode(InnerNode *parent, const QString& name, SubType subType); + virtual ~FakeNode() { } void setTitle(const QString &title) { tle = title; } void setSubTitle(const QString &subTitle) { stle = subTitle; } @@ -321,18 +345,85 @@ class FakeNode : public InnerNode NodeList gr; }; -class QmlNode : public FakeNode +#ifdef QDOC_QML +class QmlClassNode : public FakeNode { public: - QmlNode(InnerNode *parent, const QString& name, const ClassNode* cn) - : FakeNode(parent, name, QmlClass), cnode(cn) { } + QmlClassNode(InnerNode *parent, + const QString& name, + const ClassNode* cn); + virtual ~QmlClassNode() { } const ClassNode* classNode() const { return cnode; } + virtual QString fileBase() const; private: const ClassNode* cnode; }; +class QmlPropGroupNode : public FakeNode +{ + public: + QmlPropGroupNode(QmlClassNode* parent, const QString& name); + virtual ~QmlPropGroupNode() { } + + const QString& element() const { return name(); } + void setDefault() { isdefault = true; } + bool isDefault() const { return isdefault; } + + private: + bool isdefault; +}; + +class QmlPropertyNode : public LeafNode +{ + public: + QmlPropertyNode(QmlPropGroupNode* parent, + const QString& name, + const QString& type); + virtual ~QmlPropertyNode() { } + + void setDataType(const QString& dataType) { dt = dataType; } + void setStored(bool stored) { sto = toTrool(stored); } + void setDesignable(bool designable) { des = toTrool(designable); } + + const QString &dataType() const { return dt; } + QString qualifiedDataType() const { return dt; } + bool isStored() const { return fromTrool(sto,true); } + bool isDesignable() const { return fromTrool(des,false); } + + const QString& element() const { return parent()->name(); } + + private: + enum Trool { Trool_True, Trool_False, Trool_Default }; + + static Trool toTrool(bool boolean); + static bool fromTrool(Trool troolean, bool defaultValue); + + QString dt; + Trool sto; + Trool des; +}; + +class QmlSignalNode : public LeafNode +{ + public: + QmlSignalNode(QmlClassNode* parent, const QString& name); + virtual ~QmlSignalNode() { } + + const QString& element() const { return parent()->name(); } +}; + +class QmlMethodNode : public LeafNode +{ + public: + QmlMethodNode(QmlClassNode* parent, const QString& name); + virtual ~QmlMethodNode() { } + + const QString& element() const { return parent()->name(); } +}; +#endif + class EnumItem { public: @@ -358,6 +449,7 @@ class EnumNode : public LeafNode { public: EnumNode(InnerNode *parent, const QString& name); + virtual ~EnumNode() { } void addItem(const EnumItem& item); void setFlagsType(TypedefNode *typedeff); @@ -378,6 +470,7 @@ class TypedefNode : public LeafNode { public: TypedefNode(InnerNode *parent, const QString& name); + virtual ~TypedefNode() { } const EnumNode *associatedEnum() const { return ae; } @@ -438,6 +531,7 @@ class FunctionNode : public LeafNode enum Virtualness { NonVirtual, ImpureVirtual, PureVirtual }; FunctionNode(InnerNode *parent, const QString &name); + virtual ~FunctionNode() { } void setReturnType(const QString& returnType) { rt = returnType; } void setMetaness(Metaness metaness) { met = metaness; } @@ -500,6 +594,7 @@ class PropertyNode : public LeafNode enum { NumFunctionRoles = Resetter + 1 }; PropertyNode(InnerNode *parent, const QString& name); + virtual ~PropertyNode() { } void setDataType(const QString& dataType) { dt = dataType; } void addFunction(FunctionNode *function, FunctionRole role); @@ -557,6 +652,7 @@ class VariableNode : public LeafNode { public: VariableNode(InnerNode *parent, const QString &name); + virtual ~VariableNode() { } void setLeftType(const QString &leftType) { lt = leftType; } void setRightType(const QString &rightType) { rt = rightType; } @@ -582,6 +678,7 @@ class TargetNode : public LeafNode { public: TargetNode(InnerNode *parent, const QString& name); + virtual ~TargetNode() { } virtual bool isInnerNode() const; }; diff --git a/tools/qdoc3/pagegenerator.cpp b/tools/qdoc3/pagegenerator.cpp index 06ff398..8715f4a 100644 --- a/tools/qdoc3/pagegenerator.cpp +++ b/tools/qdoc3/pagegenerator.cpp @@ -81,30 +81,50 @@ QString PageGenerator::fileBase(const Node *node) { if (node->relates()) node = node->relates(); - else if (!node->isInnerNode()) + else if (!node->isInnerNode()) { node = node->parent(); +#ifdef QDOC_QML + if (node->subType() == Node::QmlPropertyGroup) { + node = node->parent(); + } +#endif + } QString base = node->doc().baseName(); if (!base.isEmpty()) return base; - const Node *p = node; - - forever { - base.prepend(p->name()); + const Node *p = node; + + forever { + base.prepend(p->name()); +#ifdef QDOC_QML + /* + To avoid file name conflicts in the html directory, + we prepend "qml-" to the file name of QML element doc + files. + */ + if ((p->subType() == Node::QmlClass) || + (p->subType() == Node::QmlPropertyGroup)) + base.prepend("qml-"); + else if ((p->type() == Node::QmlProperty) || + (p->type() == Node::QmlSignal) || + (p->type() == Node::QmlMethod)) + base.prepend("qml-"); +#endif const Node *pp = p->parent(); if (!pp || pp->name().isEmpty() || pp->type() == Node::Fake) - break; + break; base.prepend(QLatin1Char('-')); p = pp; - } - - if (node->type() == Node::Fake) { + } + + if (node->type() == Node::Fake) { #ifdef QDOC2_COMPAT - if (base.endsWith(".html")) - base.truncate(base.length() - 5); + if (base.endsWith(".html")) + base.truncate(base.length() - 5); #endif - } + } // the code below is effectively equivalent to: // base.replace(QRegExp("[^A-Za-z0-9]+"), " "); @@ -126,7 +146,8 @@ QString PageGenerator::fileBase(const Node *node) if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) { res += QLatin1Char(u); begun = true; - } else if (begun) { + } + else if (begun) { res += QLatin1Char('-'); begun = false; } @@ -187,8 +208,12 @@ void PageGenerator::generateInnerNode(const InnerNode *node, if (node->type() == Node::Fake) { const FakeNode *fakeNode = static_cast<const FakeNode *>(node); - if (fakeNode->subType() == FakeNode::ExternalPage) + if (fakeNode->subType() == Node::ExternalPage) + return; +#ifdef QDOC_QML + if (fakeNode->subType() == Node::QmlPropertyGroup) return; +#endif } if (node->parent() != 0) { @@ -197,12 +222,6 @@ void PageGenerator::generateInnerNode(const InnerNode *node, generateClassLikeNode(node, marker); } else if (node->type() == Node::Fake) { - const FakeNode* fakeNode = static_cast<const FakeNode *>(node); -#ifdef QDOC_QML - if (fakeNode->subType() == FakeNode::QmlClass) { - //qDebug() << "FILENAME:" << fileName(node); - } -#endif generateFakeNode(static_cast<const FakeNode *>(node), marker); } endSubPage(); diff --git a/tools/qdoc3/test/classic.css b/tools/qdoc3/test/classic.css index 3816164..4225a1b 100644 --- a/tools/qdoc3/test/classic.css +++ b/tools/qdoc3/test/classic.css @@ -225,3 +225,52 @@ span.string,span.char { font-size: 0.65em } + +.qmlitem { + padding: 0; +} + +.qmlname { + white-space: nowrap; + font-weight: bold; + font-size: 125%; +} + +.qmltype { + font-weight: bold; + font-size: 125%; +} + +.qmlproto, .qmldoc { + // border-top: 1px solid #84b0c7; +} + +.qmlproto { + padding: 0; + //background-color: #e4e4e4;//#d5e1e8; + //font-weight: bold; + //-webkit-border-top-left-radius: 8px; + //-webkit-border-top-right-radius: 8px; + //-moz-border-radius-topleft: 8px; + //-moz-border-radius-topright: 8px; +} + +.qmldoc { + border-top: 1px solid #e4e4e4; + //padding: 2px 5px; + //background-color: #eef3f5; + //border-top-width: 0; + //-webkit-border-bottom-left-radius: 8px; + //-webkit-border-bottom-right-radius: 8px; + //-moz-border-radius-bottomleft: 8px; + //-moz-border-radius-bottomright: 8px; +} + +.qmldoc p, .qmldoc dl, .qmldoc ul { + //margin: 6px 0; +} + +*.qmlitem p { + //margin-top: 0px; + //margin-bottom: 0px; +} diff --git a/tools/qdoc3/tree.cpp b/tools/qdoc3/tree.cpp index 308ba0e..f832062 100644 --- a/tools/qdoc3/tree.cpp +++ b/tools/qdoc3/tree.cpp @@ -176,6 +176,8 @@ const Node *Tree::findNode(const QStringList &path, } /*! + Find the node with the specified \a path name of the + specified \a type. */ Node *Tree::findNode(const QStringList &path, Node::Type type, @@ -189,6 +191,8 @@ Node *Tree::findNode(const QStringList &path, } /*! + Find the node with the specified \a path name of the + specified \a type. */ const Node *Tree::findNode(const QStringList &path, Node::Type type, @@ -208,7 +212,9 @@ FunctionNode *Tree::findFunctionNode(const QStringList& path, int findFlags) { return const_cast<FunctionNode *>( - const_cast<const Tree *>(this)->findFunctionNode(path, relative, findFlags)); + const_cast<const Tree *>(this)->findFunctionNode(path, + relative, + findFlags)); } /*! @@ -233,7 +239,8 @@ const FunctionNode *Tree::findFunctionNode(const QStringList &path, else next = ((InnerNode *) node)->findNode(path.at(i)); - if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { + if (!next && node->type() == Node::Class && + (findFlags & SearchBaseClasses)) { NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node)); foreach (const Node *baseClass, baseClasses) { if (i == path.size() - 1) @@ -412,6 +419,8 @@ void Tree::addPropertyFunction(PropertyNode *property, } /*! + This function adds the \a node to the \a group. The group + can be listed anywhere using the \e{annotated list} command. */ void Tree::addToGroup(Node *node, const QString &group) { @@ -563,13 +572,15 @@ void Tree::resolveGroups() FakeNode *fake = static_cast<FakeNode*>(findNode(QStringList(i.key()),Node::Fake)); - if (fake && fake->subType() == FakeNode::Group) { + if (fake && fake->subType() == Node::Group) { fake->addGroupMember(i.value()); } +#if 0 else { if (prevGroup != i.key()) i.value()->doc().location().warning(tr("No such group '%1'").arg(i.key())); } +#endif prevGroup = i.key(); } @@ -770,21 +781,21 @@ void Tree::readIndexSection(const QDomElement &element, } else if (element.nodeName() == "page") { - FakeNode::SubType subtype; + Node::SubType subtype; if (element.attribute("subtype") == "example") - subtype = FakeNode::Example; + subtype = Node::Example; else if (element.attribute("subtype") == "header") - subtype = FakeNode::HeaderFile; + subtype = Node::HeaderFile; else if (element.attribute("subtype") == "file") - subtype = FakeNode::File; + subtype = Node::File; else if (element.attribute("subtype") == "group") - subtype = FakeNode::Group; + subtype = Node::Group; else if (element.attribute("subtype") == "module") - subtype = FakeNode::Module; + subtype = Node::Module; else if (element.attribute("subtype") == "page") - subtype = FakeNode::Page; + subtype = Node::Page; else if (element.attribute("subtype") == "externalpage") - subtype = FakeNode::ExternalPage; + subtype = Node::ExternalPage; else return; @@ -1226,25 +1237,25 @@ bool Tree::generateIndexSection(QXmlStreamWriter &writer, const FakeNode *fakeNode = static_cast<const FakeNode*>(node); switch (fakeNode->subType()) { - case FakeNode::Example: + case Node::Example: writer.writeAttribute("subtype", "example"); break; - case FakeNode::HeaderFile: + case Node::HeaderFile: writer.writeAttribute("subtype", "header"); break; - case FakeNode::File: + case Node::File: writer.writeAttribute("subtype", "file"); break; - case FakeNode::Group: + case Node::Group: writer.writeAttribute("subtype", "group"); break; - case FakeNode::Module: + case Node::Module: writer.writeAttribute("subtype", "module"); break; - case FakeNode::Page: + case Node::Page: writer.writeAttribute("subtype", "page"); break; - case FakeNode::ExternalPage: + case Node::ExternalPage: writer.writeAttribute("subtype", "externalpage"); break; default: @@ -1383,7 +1394,7 @@ bool Tree::generateIndexSection(QXmlStreamWriter &writer, bool external = false; if (inner->type() == Node::Fake) { const FakeNode *fakeNode = static_cast<const FakeNode *>(inner); - if (fakeNode->subType() == FakeNode::ExternalPage) + if (fakeNode->subType() == Node::ExternalPage) external = true; } @@ -1863,7 +1874,7 @@ void Tree::generateTagFile(const QString &fileName) const */ void Tree::addExternalLink(const QString &url, const Node *relative) { - FakeNode *fakeNode = new FakeNode(root(), url, FakeNode::ExternalPage); + FakeNode *fakeNode = new FakeNode(root(), url, Node::ExternalPage); fakeNode->setAccess(Node::Public); // Create some content for the node. @@ -1898,6 +1909,11 @@ QString Tree::fullDocumentLocation(const Node *node) const return ""; } else if (node->type() == Node::Fake) { +#ifdef QDOC_QML + if (node->subType() == Node::QmlClass) + return "qml-" + node->fileBase() + ".html"; + else +#endif parentName = node->fileBase() + ".html"; } else if (node->fileBase().isEmpty()) diff --git a/tools/qdoc3/webxmlgenerator.cpp b/tools/qdoc3/webxmlgenerator.cpp index c5209b8..e87e812 100644 --- a/tools/qdoc3/webxmlgenerator.cpp +++ b/tools/qdoc3/webxmlgenerator.cpp @@ -191,7 +191,7 @@ void WebXMLGenerator::generateIndexSections(QXmlStreamWriter &writer, generateRelations(writer, node, marker); - if (fake->subType() == FakeNode::Module) { + if (fake->subType() == Node::Module) { writer.writeStartElement("generatedlist"); writer.writeAttribute("contents", "classesbymodule"); @@ -264,7 +264,7 @@ void WebXMLGenerator::generateInnerNode(const InnerNode *node, CodeMarker *marke if (node->type() == Node::Fake) { const FakeNode *fakeNode = static_cast<const FakeNode *>(node); - if (fakeNode->subType() == FakeNode::ExternalPage) + if (fakeNode->subType() == Node::ExternalPage) return; } diff --git a/tools/qtestlib/wince/cetcpsync/cetcpsync.pro b/tools/qtestlib/wince/cetcpsync/cetcpsync.pro new file mode 100644 index 0000000..d1d7c99 --- /dev/null +++ b/tools/qtestlib/wince/cetcpsync/cetcpsync.pro @@ -0,0 +1,22 @@ +TARGET = cetcpsync +DESTDIR = ../../../../bin +CONFIG += console +CONFIG -= app_bundle +QT += network +QT -= gui +TEMPLATE = app + +build_all:!build_pass { + CONFIG -= build_all + CONFIG += release +} + +INCLUDEPATH += ../cetcpsyncserver + +SOURCES += main.cpp \ + remoteconnection.cpp \ + qtcesterconnection.cpp + +HEADERS += \ + remoteconnection.h \ + qtcesterconnection.h diff --git a/tools/qtestlib/wince/cetcpsync/main.cpp b/tools/qtestlib/wince/cetcpsync/main.cpp new file mode 100644 index 0000000..73c7350 --- /dev/null +++ b/tools/qtestlib/wince/cetcpsync/main.cpp @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <iostream> +#include "qtcesterconnection.h" + +using namespace std; + +static void showUsage() +{ + cout << "cetcpsync is meant to be used by cetest internally." << endl + << "For usage instructions remoteconnection.h could be useful." << endl; +} + +const int debugLevel = 0; +void debugOutput(const QString& text, int level) +{ + if (level <= debugLevel) + cout << qPrintable(text) << endl; +} + +class Exception +{ +public: + Exception(const QString& msg = QString()) + : m_message(msg) + {} + + QString message() { return m_message; } + +protected: + QString m_message; +}; + +class TooFewParametersException : public Exception +{ +public: + TooFewParametersException(const QLatin1String& cmd, int expectedParameterCount) + { + m_message = QLatin1String("Command ") + cmd + QLatin1String(" needs at least "); + m_message.append(QString::number(expectedParameterCount)); + m_message.append(QLatin1String(" parameters.")); + } +}; + +static void fileTimeFromString(FILETIME& ft, const QString& str) +{ + int idx = str.indexOf("*"); + if (idx <= 0) + return; + ft.dwLowDateTime = str.left(idx).toULong(); + ft.dwHighDateTime = str.mid(idx+1).toULong(); +} + +static QString fileTimeToString(FILETIME& ft) +{ + return QString::number(ft.dwLowDateTime) + "*" + QString::number(ft.dwHighDateTime); +} + +static int execCommand(const QLatin1String& cmd, int argc, char* argv[]) +{ + int retval = 0; + bool success = true; + QtCesterConnection connection; + if (cmd == "copyFileToDevice") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyFileToDevice(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "copyDirectoryToDevice") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyDirectoryToDevice(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "copyFileFromDevice") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyFileFromDevice(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "copyDirectoryFromDevice") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyDirectoryFromDevice(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "timeStampForLocalFileTime") { + if (argc < 1) + throw TooFewParametersException(cmd, 1); + FILETIME ft; + fileTimeFromString(ft, argv[0]); + success = connection.timeStampForLocalFileTime(&ft); + if (success) + cout << qPrintable(fileTimeToString(ft)); + } else if (cmd == "fileCreationTime") { + if (argc < 1) + throw TooFewParametersException(cmd, 1); + FILETIME ft; + success = connection.fileCreationTime(argv[0], &ft); + if (success) + cout << qPrintable(fileTimeToString(ft)); + } else if (cmd == "copyFile") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyFile(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "copyDirectory") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.copyDirectory(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "deleteFile") { + if (argc < 1) + throw TooFewParametersException(cmd, 1); + success = connection.deleteFile(argv[0]); + } else if (cmd == "deleteDirectory") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.deleteDirectory(argv[0], argv[1] == "true", argv[2] == "true"); + } else if (cmd == "moveFile") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.moveFile(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "moveDirectory") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + success = connection.moveDirectory(argv[0], argv[1], argv[2] == "true"); + } else if (cmd == "createDirectory") { + if (argc < 2) + throw TooFewParametersException(cmd, 2); + success = connection.createDirectory(argv[0], argv[1] == "true"); + } else if (cmd == "execute") { + if (argc < 3) + throw TooFewParametersException(cmd, 3); + int timeout = QString(argv[2]).toInt(); + success = connection.execute(argv[0], argv[1], timeout, &retval); + } else if (cmd == "noop") { + // do nothing :) + success = true; + } else { + throw Exception("unknown command"); + } + + return success ? retval : 1; +} + +int main(int argc, char *argv[]) +{ + if (argc <= 1) { + showUsage(); + return 0; + } + + QLatin1String param(argv[1]); + int result = 1; + try { + result = execCommand(param, argc - 2, argv + 2); + } catch (Exception e) { + cerr << "Error: " << qPrintable(e.message()); + } + return result; +} diff --git a/tools/qtestlib/wince/cetcpsync/qtcesterconnection.cpp b/tools/qtestlib/wince/cetcpsync/qtcesterconnection.cpp new file mode 100644 index 0000000..76d3b4b --- /dev/null +++ b/tools/qtestlib/wince/cetcpsync/qtcesterconnection.cpp @@ -0,0 +1,552 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qtcesterconnection.h" +#include <transfer_global.h> +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtNetwork/QTcpSocket> +#include <QtNetwork/QHostAddress> + +extern void debugOutput(const QString& text, int level); + +#pragma warning(disable:4996) + +#define END_ERROR(s, a) \ + if(a) qDebug() << a; \ + _freeSocket(s); \ + return false; + +QtCesterConnection::QtCesterConnection() + : AbstractRemoteConnection() +{ +} + +QtCesterConnection::~QtCesterConnection() +{ +} + +bool QtCesterConnection::connect(QVariantList&) +{ + // We connect with each command, so this is always true + // The command itself will fail then + connected = true; + return true; +} + +void QtCesterConnection::disconnect() +{ + connected = false; +} + +bool QtCesterConnection::isConnected() const +{ + return connected; +} + +bool QtCesterConnection::copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists) +{ + debugOutput( qPrintable(QString::fromLatin1("Copy File: %1 -> %2").arg(localSource).arg(deviceDest)),0); + QFile localFile(localSource); + QFileInfo info(localSource); + if (!localFile.exists() || !localFile.open(QIODevice::ReadOnly)) { + qDebug() << "Could not open File!"; + return false; + } + + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_CREATE_FILE)) { + END_ERROR(socket, "Could not initialized command"); + } + + CreateFileOptions option; + strcpy(option.fileName, qPrintable(deviceDest)); +#ifdef Q_OS_WIN + // Copy FileTime for update verification + FILETIME creationTime, accessTime, writeTime; + HANDLE localHandle = CreateFile(localSource.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + if (localHandle != INVALID_HANDLE_VALUE) { + if (GetFileTime(localHandle, &creationTime, &accessTime, &writeTime)) { + LocalFileTimeToFileTime(&writeTime, &writeTime); + option.fileTime = writeTime; + } + CloseHandle(localHandle); + } + DWORD attributes = GetFileAttributes(localSource.utf16()); + if (attributes != -1 ) + option.fileAttributes = attributes; +#endif + option.fileSize = info.size(); + option.overwriteExisting = !failIfExists; + + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send options..."); + } + + if (!_checkResult(socket)) { + END_ERROR(socket, "Server did not accept configuration"); + } + + int bytesWritten = 0; + const int bufferSize = 1024; + QByteArray data; + while (bytesWritten < option.fileSize) { + data = localFile.read(bufferSize); + bytesWritten += data.size(); +#ifdef Q_OS_WIN + wprintf( L"%s -> %s (%d / %d) %d %%\r", localSource.utf16() , deviceDest.utf16(), + bytesWritten , option.fileSize, (100*bytesWritten)/option.fileSize ); +#endif + if (!_sendData(socket, data.constData(), data.size())) { + END_ERROR(socket, "Error during file transfer"); + } + if (!_checkResult(socket)) { + END_ERROR(socket, "Got some strange result"); + } + } +#ifdef Q_OS_WIN + wprintf( L"\n"); // We should jump to next line... +#endif + if (bytesWritten != option.fileSize) { + END_ERROR(socket, "Did not send sufficient data"); + } + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive) +{ + QTcpSocket* socket = NULL; + QFileInfo info(localSource); + if (!info.exists() || !info.isDir()) { + END_ERROR(socket, "Input directory invalid"); + } + + createDirectory(deviceDest, true); + QDir dir(localSource); + QFileInfoList list = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + foreach(QFileInfo item, list) { + QString targetName = deviceDest + QLatin1String("\\") + item.fileName(); + if (item.isDir()) { + if (recursive) { + if (!copyDirectoryToDevice(item.absoluteFilePath() , targetName, recursive)) + return false; + } + } else { + if (!copyFileToDevice(item.absoluteFilePath(), targetName)) + return false; + } + } + return true; +} + +bool QtCesterConnection::copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists) +{ + QFile targetFile(localDest); + QTcpSocket* socket = 0; + if (targetFile.exists() && failIfExists) { + END_ERROR(socket, "Local file not supposed to be overwritten"); + } + + if (!targetFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + END_ERROR(socket, "Could not open local file for writing"); + } + + if (!_initCommand(socket, COMMAND_READ_FILE)) { + END_ERROR(socket, "Could not establish connection"); + } + + ReadFileOptions option; + strcpy(option.fileName, qPrintable(deviceSource)); + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send options"); + } + + QByteArray data; + if (!_receiveData(socket, data)) { + END_ERROR(socket, "Did not receive any data"); + } + + ReadFileReply* reply = (ReadFileReply*) data.data(); + if (!reply->fileValid) { + END_ERROR(socket, "Requested file invalid"); + } + + int fileSize = reply->fileSize; + int currentSize = 0; + // ### TODO: make a little bit more error-prone + do { + _sendData(socket, COMMAND_SUCCESS, strlen(COMMAND_SUCCESS)); + _receiveData(socket, data); + currentSize += data.size(); + targetFile.write(data); + } while(currentSize < fileSize); + + _freeSocket(socket); + targetFile.close(); + return true; +} + +bool QtCesterConnection::copyDirectoryFromDevice(const QString& /*deviceSource*/ + , const QString& /*localDest*/ + , bool /*recursive*/) +{ + qDebug() << "To be implemented!! Should not be needed for autotest system"; + exit(-1); + return false; +} + +bool QtCesterConnection::copyFile(const QString &srcFile, const QString &destFile, bool failIfExists) +{ + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_COPY_FILE)) { + END_ERROR(socket, "Could not establish connection for copy"); + } + + CopyFileOptions option; + strcpy(option.from, qPrintable(srcFile)); + strcpy(option.to, qPrintable(destFile)); + option.overwriteExisting = !failIfExists; + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send copy options"); + } + + if (!_checkResult(socket)) { + END_ERROR(socket, "Copy failed"); + } + + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::copyDirectory(const QString &srcDirectory, const QString &destDirectory, + bool recursive) +{ + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_COPY_DIRECTORY)) { + END_ERROR(socket, "Could not establish connection for dir copy"); + } + + CopyDirectoryOptions option; + strcpy(option.from, qPrintable(srcDirectory)); + strcpy(option.to, qPrintable(destDirectory)); + option.recursive = recursive; + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send dir copy options"); + } + + if (!_checkResult(socket)) { + END_ERROR(socket, "Dir Copy failed"); + } + + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::deleteFile(const QString &fileName) +{ + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_DELETE_FILE)) { + END_ERROR(socket, "Could not establish connection for file deletion"); + } + + DeleteFileOptions option; + strcpy(option.fileName, qPrintable(fileName)); + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send file options"); + } + + if (!_checkResult(socket)) { + //END_ERROR(socket, "File Deletion failed"); + // This is actually not an error so ignore it. + } + + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::deleteDirectory(const QString &directory, bool recursive, bool failIfContentExists) +{ + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_DELETE_DIRECTORY)) { + END_ERROR(socket, "Could not establish connection for dir deletion"); + } + + DeleteDirectoryOptions option; + strcpy(option.dirName, qPrintable(directory)); + option.recursive = recursive; + option.failIfContentExists = failIfContentExists; + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send dir options"); + } + + if (!_checkResult(socket)) { + // we do not write an error as this will fail a lot on recursive. + END_ERROR(socket, 0); + } + + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::execute(QString program, + QString arguments, + int timeout, + int *returnValue) +{ + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_EXECUTE)) { + END_ERROR(socket, "Could not establish connection for dir deletion"); + } + + ExecuteOptions options; + strcpy(options.appName, qPrintable(program)); + QStringList argList = arguments.split(QLatin1Char(' ')); + options.argumentsCount = qMin(argList.size(), MAX_ARGUMENTS); + options.waitForFinished = true; + options.timeout = timeout; + if (!_sendData(socket, (char*) &options, sizeof(options))) { + END_ERROR(socket, "Could not send dir options"); + } + if (!_checkResult(socket)) { + END_ERROR(socket, "Did not receive an answer"); + } + + for (int i=0; i < options.argumentsCount; ++i) { + char someData[MAX_NAME_LENGTH]; + strcpy(someData, qPrintable(argList[i])); + if (!_sendData(socket, someData, MAX_NAME_LENGTH)) { + END_ERROR(socket, "Could not send argument"); + } + if (!_checkResult(socket)) { + END_ERROR(socket, "Failure in argument send"); + } + } + + // trigger the startup + if (!_sendData(socket, COMMAND_SUCCESS, strlen(COMMAND_SUCCESS))) { + END_ERROR(socket, "Could not trigger startup"); + } + + const int waitTime = 60 * 60 * 1000; + if (!socket->waitForReadyRead(waitTime)) { + END_ERROR(socket, "Process timed out"); + } + + QByteArray result = socket->readAll(); + if (result != COMMAND_SUCCESS) { + if (returnValue) + *returnValue = -1; // just some at least + END_ERROR(socket, "Application did not start or returned error"); + } + + if (returnValue) + *returnValue = 0; + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::createDirectory(const QString &path, bool deleteBefore) +{ + if (deleteBefore) + deleteDirectory(path, true, true); + + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_CREATE_DIRECTORY)) { + END_ERROR(socket, "Could not establish connection for dir creation"); + } + + CreateDirectoryOptions option; + strcpy(option.dirName, qPrintable(path)); + option.recursively = true; + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send dir options"); + } + + if (!_checkResult(socket)) { + END_ERROR(socket, "Dir creation failed"); + } + + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::timeStampForLocalFileTime(FILETIME* fTime) const +{ + if (!fTime) + return false; + + FILETIME copyTime = *fTime; + LocalFileTimeToFileTime(©Time, ©Time); + + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_TIME_STAMP)) { + END_ERROR(socket, "Could not establish time stamp connection"); + } + + if (!_sendData(socket, (char*) ©Time, sizeof(copyTime))) { + END_ERROR(socket, "Could not send stamp time"); + } + + QByteArray data; + if (!_receiveData(socket, data)) { + END_ERROR(socket, "Did not receive time stamp or connection interrupted"); + } + + copyTime = *((FILETIME*)data.data()); + if (copyTime.dwLowDateTime == -1 && copyTime.dwHighDateTime == -1) { + END_ERROR(socket, "remote Time stamp failed!"); + } + + *fTime = copyTime; + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::fileCreationTime(const QString &fileName, FILETIME* deviceCreationTime) const +{ + if (!deviceCreationTime) + return false; + + QTcpSocket* socket = 0; + if (!_initCommand(socket, COMMAND_FILE_TIME)) { + END_ERROR(socket, "Could not establish connection for file time access"); + } + + FileTimeOptions option; + strcpy(option.fileName, qPrintable(fileName)); + if (!_sendData(socket, (char*) &option, sizeof(option))) { + END_ERROR(socket, "Could not send file time name"); + } + + QByteArray data; + if (!_receiveData(socket, data)) { + END_ERROR(socket, "File Time request failed"); + } + + FILETIME* resultTime = (FILETIME*) data.data(); + if (resultTime->dwLowDateTime == -1 && resultTime->dwHighDateTime == -1) { + END_ERROR(socket, 0); + debugOutput("Could not access file time", 0); + } + + *deviceCreationTime = *resultTime; + _freeSocket(socket); + return true; +} + +bool QtCesterConnection::_createSocket(QTcpSocket*& result) const +{ + QTcpSocket* sock = new QTcpSocket(); + QByteArray ipAddress = qgetenv("DEVICE_IP"); + if (ipAddress.isEmpty()) { + qWarning("Error: You need to have DEVICE_IP set"); + exit(0); + } + sock->connectToHost(QHostAddress(QString(ipAddress)), 12145); + + if (!sock->waitForConnected()) { + qDebug() << "connection timeout..."; + result = NULL; + return false; + } + result = sock; + return true; +} + +void QtCesterConnection::_freeSocket(QTcpSocket*& sock) const +{ + if (!sock) + return; + if (sock->state() == QAbstractSocket::ConnectedState) { + sock->disconnectFromHost(); + // seems like no need to wait + //sock->waitForDisconnected(); + } + delete sock; + sock = NULL; +#ifdef Q_OS_WIN + Sleep(100); +#endif +} + +bool QtCesterConnection::_initCommand(QTcpSocket*& sock, const char* command) const +{ + QTcpSocket* socket = NULL; + if (!_createSocket(socket)) { + END_ERROR(socket, "Could not connect to server"); + } + + if (!_sendData(socket, command, strlen(command)) || + !_checkResult(socket)) { + END_ERROR(socket, "Cound not send command"); + } + sock = socket; + return true; +} + +bool QtCesterConnection::_sendData(QTcpSocket*& sock, const char* data, int dataSize) const +{ + int amount = sock->write(data, dataSize); + if (amount != dataSize) { + fprintf(stderr, "*******COULD NOT SEND ENOUGH DATA*************\n"); + } + return sock->waitForBytesWritten(); +} + +bool QtCesterConnection::_receiveData(QTcpSocket*& sock, QByteArray& data) const +{ + if (!sock->waitForReadyRead()) { + qDebug() << "did not receive any data"; + return false; + } + data = sock->readAll(); + return true; +} + +bool QtCesterConnection::_checkResult(QTcpSocket*& sock) const +{ + QByteArray response; + if (!_receiveData(sock, response) || response != COMMAND_SUCCESS) + return false; + return true; +} + diff --git a/tools/qtestlib/wince/cetcpsync/qtcesterconnection.h b/tools/qtestlib/wince/cetcpsync/qtcesterconnection.h new file mode 100644 index 0000000..d7b8393 --- /dev/null +++ b/tools/qtestlib/wince/cetcpsync/qtcesterconnection.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef ACTIVESYNC_REMOTECONNECTION_H +#define ACTIVESYNC_REMOTECONNECTION_H + +#include "remoteconnection.h" + +class QTcpSocket; + +class QtCesterConnection : public AbstractRemoteConnection +{ +public: + QtCesterConnection(); + virtual ~QtCesterConnection(); + + bool connect(QVariantList &list = QVariantList()); + void disconnect(); + bool isConnected() const; + + // These functions are designed for transfer between desktop and device + // Caution: deviceDest path has to be device specific (eg. no drive letters for CE) + bool copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists = false); + bool copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive = true); + bool copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists = false); + bool copyDirectoryFromDevice(const QString &deviceSource, const QString &localDest, bool recursive = true); + + bool timeStampForLocalFileTime(FILETIME*) const; + bool fileCreationTime(const QString &fileName, FILETIME*) const; + + // These functions only work on files existing on the device + bool copyFile(const QString&, const QString&, bool failIfExists = false); + bool copyDirectory(const QString&, const QString&, bool recursive = true); + bool deleteFile(const QString&); + bool deleteDirectory(const QString&, bool recursive = true, bool failIfContentExists = false); + bool createDirectory(const QString&, bool deleteBefore=false); + + bool execute(QString program, QString arguments = QString(), int timeout = -1, int *returnValue = NULL); +private: + bool _createSocket(QTcpSocket*&) const; + void _freeSocket(QTcpSocket*&) const; + bool _initCommand(QTcpSocket*&, const char*) const; + bool _sendData(QTcpSocket*&, const char* data, int dataSize) const; + bool _receiveData(QTcpSocket*&, QByteArray&) const; + bool _checkResult(QTcpSocket*&) const; + bool connected; +}; + +#endif diff --git a/tools/qtestlib/wince/cetcpsync/remoteconnection.cpp b/tools/qtestlib/wince/cetcpsync/remoteconnection.cpp new file mode 100644 index 0000000..b197c5c --- /dev/null +++ b/tools/qtestlib/wince/cetcpsync/remoteconnection.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "remoteconnection.h" + +AbstractRemoteConnection::AbstractRemoteConnection() +{ +} + +AbstractRemoteConnection::~AbstractRemoteConnection() +{ +} + +// Slow but should be ok... +bool AbstractRemoteConnection::moveFile(const QString &src, const QString &dest, bool FailIfExists) +{ + bool result = copyFile(src, dest, FailIfExists); + deleteFile(src); + return result; +} + +// Slow but should be ok... +bool AbstractRemoteConnection::moveDirectory(const QString &src, const QString &dest, bool recursive) +{ + bool result = copyDirectory(src, dest, true); + deleteDirectory(src, recursive); + return result; +} diff --git a/tools/qtestlib/wince/cetcpsync/remoteconnection.h b/tools/qtestlib/wince/cetcpsync/remoteconnection.h new file mode 100644 index 0000000..fae6f7f --- /dev/null +++ b/tools/qtestlib/wince/cetcpsync/remoteconnection.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef REMOTECONNECTION_H +#define REMOTECONNECTION_H + +#include <QtCore/QString> +#include <QtCore/QVariant> +#include <windows.h> +class AbstractRemoteConnection +{ +public: + AbstractRemoteConnection(); + virtual ~AbstractRemoteConnection(); + + virtual bool connect(QVariantList&) = 0; + virtual void disconnect() = 0; + virtual bool isConnected() const = 0; + + // These functions are designed for transfer between desktop and device + // Caution: deviceDest path has to be device specific (eg. no drive letters for CE) + virtual bool copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists = false) = 0; + virtual bool copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive = true) = 0; + virtual bool copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists = false) = 0; + virtual bool copyDirectoryFromDevice(const QString &deviceSource, const QString &localDest, bool recursive = true) = 0; + + // For "intelligent deployment" we need to investigate on filetimes on the device + virtual bool timeStampForLocalFileTime(FILETIME*) const = 0; + virtual bool fileCreationTime(const QString &fileName, FILETIME*) const = 0; + + // These functions only work on files existing on the device + virtual bool copyFile(const QString&, const QString&, bool failIfExists = false) = 0; + virtual bool copyDirectory(const QString&, const QString&, bool recursive = true) = 0; + virtual bool deleteFile(const QString&) = 0; + virtual bool deleteDirectory(const QString&, bool recursive = true, bool failIfContentExists = false) = 0; + bool moveFile(const QString&, const QString&, bool FailIfExists = false); + bool moveDirectory(const QString&, const QString&, bool recursive = true); + + virtual bool createDirectory(const QString&, bool deleteBefore=false) = 0; + + virtual bool execute(QString program, QString arguments = QString(), int timeout = -1, int *returnValue = NULL) = 0; +}; + +#endif diff --git a/tools/qtestlib/wince/cetcpsyncserver/cetcpsyncserver.pro b/tools/qtestlib/wince/cetcpsyncserver/cetcpsyncserver.pro new file mode 100644 index 0000000..bd01d2d --- /dev/null +++ b/tools/qtestlib/wince/cetcpsyncserver/cetcpsyncserver.pro @@ -0,0 +1,17 @@ +TEMPLATE = app +TARGET = cetcpsyncsvr +DEPENDPATH += . +QT -= gui +QT += network + +CONFIG += console + +HEADERS += \ + connectionmanager.h \ + commands.h \ + transfer_global.h + +SOURCES += \ + connectionmanager.cpp \ + commands.cpp \ + main.cpp diff --git a/tools/qtestlib/wince/cetcpsyncserver/commands.cpp b/tools/qtestlib/wince/cetcpsyncserver/commands.cpp new file mode 100644 index 0000000..0c4d3bc --- /dev/null +++ b/tools/qtestlib/wince/cetcpsyncserver/commands.cpp @@ -0,0 +1,686 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commands.h" +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QProcess> + +#ifdef Q_OS_WINCE +#include <windows.h> +#endif + +///////////////////////////////////////////////////// +// Abstract Command Implementation // +///////////////////////////////////////////////////// +AbstractCommand::AbstractCommand() +: m_socket(0) +{ +} + +AbstractCommand::~AbstractCommand() +{ +} + +void AbstractCommand::reportSuccess() +{ + m_socket->write(COMMAND_SUCCESS, strlen(COMMAND_SUCCESS)); + m_socket->waitForBytesWritten(); +} + +void AbstractCommand::reportError() +{ + m_socket->write(COMMAND_ERROR, strlen(COMMAND_ERROR)); + m_socket->waitForBytesWritten(); +} + +void AbstractCommand::dataReceived(QByteArray&) +{ + debugOutput(1, "AbstractCommand::dataReceived NOT SUPPOSED TO BE HERE"); +} + +void AbstractCommand::commandFinished() +{ + debugOutput(1, "AbstractCommand::commandFinished()NOT SUPPOSED TO BE HERE"); +} + +void AbstractCommand::setSocket(QTcpSocket* socket) +{ + debugOutput(0, "AbstractCommand::setSocket()"); + Q_ASSERT(socket); + m_socket = socket; + connect(m_socket, SIGNAL(readyRead()), this, SLOT(_readData())); + reportSuccess(); +} + +QTcpSocket* AbstractCommand::socket() +{ + return m_socket; +} + +void AbstractCommand::_readData() +{ + QByteArray arr = m_socket->readAll(); + dataReceived(arr); +} + +void AbstractCommand::_disconnect() +{ +} + +///////////////////////////////////////////////////// +// Create File Command Implementation // +///////////////////////////////////////////////////// +CreateFileCommand::CreateFileCommand() +: m_dataCount(0) +{ + debugOutput(0, "CreateFileCommand::CreateFileCommand"); + m_options.fileSize= -1; +} + +CreateFileCommand::~CreateFileCommand() +{ + debugOutput(0, "CreateFileCommand::~CreateFileCommand"); + if (m_file.isOpen()) { + fprintf(stderr, "****************FILE IS STILL OPENED AND HAVENT FINISHED WRITING**********************\n"); + fprintf(stderr, "Current: %d Expected: %d\n", m_dataCount , m_options.fileSize); + m_file.close(); + } +} + +void CreateFileCommand::dataReceived(QByteArray &data) +{ + bool successful = true; + // If we haven't received the options yet + if (m_options.fileSize == -1) { + CreateFileOptions* opt = (CreateFileOptions*) data.data(); + memcpy(&m_options , opt , sizeof(CreateFileOptions)); + + if (QFileInfo(QString::fromLatin1(m_options.fileName)).exists()) { + if (m_options.overwriteExisting) { +#ifdef Q_OS_WINCE + SetFileAttributes(QFileInfo(m_options.fileName).absoluteFilePath().utf16(), FILE_ATTRIBUTE_NORMAL); +#endif + QFile::remove(m_options.fileName); + } else + successful = false; + } + m_file.setFileName(QString::fromLatin1(m_options.fileName)); + if (!m_file.open(QIODevice::WriteOnly)) + successful = false; + else + debugOutput(3, QString::fromLatin1("Creating file: %1").arg(m_options.fileName)); + } else { // write buffer on disc + if (!m_file.isOpen()) + return; + m_file.write(data); + m_dataCount += data.size(); + if (m_dataCount >= m_options.fileSize) { + // We do not care about more data than announced + m_file.close(); + } + } + + if (successful) + reportSuccess(); + else + reportError(); +} + +void CreateFileCommand::commandFinished() +{ + debugOutput(0, "CreateFileCommand::commandFinished"); +#ifdef Q_OS_WIN + // We need to set the file attributes for intelligent time comparisons + QString tmpFile = QString::fromLatin1(m_options.fileName); + HANDLE handle = CreateFile(tmpFile.utf16(), GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (handle != INVALID_HANDLE_VALUE) { + SetFileTime(handle, &(m_options.fileTime), NULL, NULL); + CloseHandle(handle); + } + SetFileAttributes(tmpFile.utf16(), m_options.fileAttributes); +#endif +} + +///////////////////////////////////////////////////// +// Create Directory Command Implementation // +///////////////////////////////////////////////////// +CreateDirectoryCommand::CreateDirectoryCommand() + : AbstractCommand() +{ + debugOutput(0, "CreateDirectoryCommand::CreateDirectoryCommand"); +} + +CreateDirectoryCommand::~CreateDirectoryCommand() +{ + debugOutput(0, "CreateDirectoryCommand::~CreateDirectoryCommand()"); +} + +void CreateDirectoryCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "CreateDirectoryCommand::dataReceived()"); + CreateDirectoryOptions* options = (CreateDirectoryOptions*) data.data(); + debugOutput(3, QString::fromLatin1("Creating directory: %1").arg(options->dirName)); + bool success = true; + QDir dir; + if (options->recursively) + success = dir.mkpath(options->dirName); + else + success = dir.mkdir(options->dirName); + + if (success) + reportSuccess(); + else + reportError(); +} + +void CreateDirectoryCommand::commandFinished() +{ + debugOutput(0, "CreateDirectoryCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Copy File Command Implementation // +///////////////////////////////////////////////////// +CopyFileCommand::CopyFileCommand() + : AbstractCommand() +{ + debugOutput(0, "CopyFileCommand::CopyFileCommand()"); +} + +CopyFileCommand::~CopyFileCommand() +{ + debugOutput(0, "CopyFileCommand::~CopyFileCommand()"); +} + +void CopyFileCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "CopyFileCommand::dataReceived()"); + CopyFileOptions* options = (CopyFileOptions*) data.data(); + debugOutput(3, QString::fromLatin1("Copy File: %1 -> %2").arg(options->from).arg(options->to)); + bool success = true; + if (QFileInfo(options->to).exists()) { + if (options->overwriteExisting) + QFile::remove(options->to); + else + success = false; + } + if (success) + if (!QFile::copy(options->from , options->to)) + success = false; + + if (success) + reportSuccess(); + else + reportError(); +} + +void CopyFileCommand::commandFinished() +{ + debugOutput(0, "CopyFileCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Copy Directory Command Implementation // +///////////////////////////////////////////////////// +CopyDirectoryCommand::CopyDirectoryCommand() + : AbstractCommand() +{ + debugOutput(0, "CopyDirectoryCommand::CopyDirectoryCommand()"); +} + +CopyDirectoryCommand::~CopyDirectoryCommand() +{ + debugOutput(0, "CopyDirectoryCommand::~CopyDirectoryCommand()"); +} + +void CopyDirectoryCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "CopyDirectoryCommand::dataReceived()"); + CopyDirectoryOptions* options = (CopyDirectoryOptions*) data.data(); + debugOutput(3, QString::fromLatin1("Copy Directory: %1 %2").arg(options->from).arg(options->to)); + if (copyDir(QLatin1String(options->from) , QLatin1String(options->to) , options->recursive)) + reportSuccess(); + else + reportError(); +} + +void CopyDirectoryCommand::commandFinished() +{ + debugOutput(0, "CopyDirectoryCommand::commandFinished()"); +} + +bool CopyDirectoryCommand::copyDir(const QString &from, const QString &to, bool recursive) +{ + QDir().mkpath(to); + QDir sourceDir(from); + QDir destDir(to); + QStringList entries = sourceDir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + foreach (QString item , entries) { + QString itemFrom = sourceDir.absoluteFilePath(item); + QString itemTo = destDir.absoluteFilePath(item); + if (QFileInfo(item).isDir()) { + if (recursive && !copyDir(itemFrom, itemTo, recursive)) + return false; + } else { + if (!QFile::copy(itemFrom, itemTo)) + return false; + } + } + return true; +} + +///////////////////////////////////////////////////// +// Delete File Command Implementation // +///////////////////////////////////////////////////// +DeleteFileCommand::DeleteFileCommand() + : AbstractCommand() +{ + debugOutput(0, "DeleteFileCommand::DeleteFileCommand()"); +} + +DeleteFileCommand::~DeleteFileCommand() +{ + debugOutput(0, "DeleteFileCommand::~DeleteFileCommand()"); +} + +void DeleteFileCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "DeleteFileCommand::dataReceived()"); + DeleteFileOptions* options = (DeleteFileOptions*) data.data(); + debugOutput(3, QString::fromLatin1("Delete File: %1").arg(options->fileName)); + bool success = true; + QFile file(options->fileName); + if (file.exists()) { +#ifdef Q_OS_WINCE + SetFileAttributes(QFileInfo(options->fileName).absoluteFilePath().utf16(), FILE_ATTRIBUTE_NORMAL); +#endif + success = file.remove(); + } else + success = false; + + if (success) + reportSuccess(); + else + reportError(); +} + +void DeleteFileCommand::commandFinished() +{ + debugOutput(0, "DeleteFileCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Delete Directory Command Implementation // +///////////////////////////////////////////////////// +DeleteDirectoryCommand::DeleteDirectoryCommand() + : AbstractCommand() +{ + debugOutput(0, "DeleteDirectoryCommand::DeleteDirectoryCommand()"); +} + +DeleteDirectoryCommand::~DeleteDirectoryCommand() +{ + debugOutput(0, "DeleteDirectoryCommand::~DeleteDirectoryCommand()"); +} + +void DeleteDirectoryCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "DeleteDirectoryCommand::dataReceived()"); + DeleteDirectoryOptions* options = (DeleteDirectoryOptions*) data.data(); + debugOutput(3, QString::fromLatin1("Delete directory: %1").arg(options->dirName)); + if (deleteDirectory(QLatin1String(options->dirName), options->recursive, options->failIfContentExists)) + reportSuccess(); + else + reportError(); +} + +void DeleteDirectoryCommand::commandFinished() +{ + debugOutput(0, "DeleteDirectoryCommand::commandFinished()"); +} + +bool DeleteDirectoryCommand::deleteDirectory(const QString &dirName, bool recursive, bool failIfContentExists) +{ + QDir dir(dirName); + if (!dir.exists()) + return false; + + QStringList itemList = dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); + if (itemList.size() > 0 && failIfContentExists) + return false; + + foreach (QString item, itemList) { + QString itemName = dir.absoluteFilePath(item); + if (QFileInfo(itemName).isDir()) { + if (recursive && !deleteDirectory(itemName, recursive, failIfContentExists)) + return false; + } else { + if (!dir.remove(item)) + return false; + } + } + QString lastName = dir.dirName(); + dir.cdUp(); + dir.rmpath(lastName); + return true; +} + +///////////////////////////////////////////////////// +// Execute Command Implementation // +///////////////////////////////////////////////////// +ExecuteCommand::ExecuteCommand() + : AbstractCommand() + , m_argumentCount(0) + , m_timeout(-1) +{ + debugOutput(0, "ExecuteCommand::ExecuteCommand()"); +} + +ExecuteCommand::~ExecuteCommand() +{ + debugOutput(0, "ExecuteCommand::~ExecuteCommand()"); +} + +void ExecuteCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "ExecuteCommand::dataReceived()"); + + if (m_argumentCount == 0) { + ExecuteOptions* options = (ExecuteOptions*) data.data(); + if (!QFileInfo(options->appName).exists()) { + debugOutput(1, "Error execute: application does not exist"); + reportError(); + return; + } + + m_program = QLatin1String(options->appName); + m_argumentCount = options->argumentsCount; + m_waitFinished = options->waitForFinished; + m_timeout = options->timeout; + if (m_argumentCount == 0) + m_argumentCount = -1; // to trigger startup on next receive + reportSuccess(); + } else if (m_arguments.size() < m_argumentCount) { + m_arguments += data; + reportSuccess(); + } else { // do the execution + if (data == COMMAND_SUCCESS) + _doExecute(); + } +} + +void ExecuteCommand::_doExecute() +{ + debugOutput(0, "ExecuteCommand::_doExecute()"); + debugOutput(3, QString::fromLatin1("Execute: %1 %2").arg(m_program).arg(m_arguments.join(" "))); + if (m_waitFinished) { + QProcess process; + process.start(m_program, m_arguments); + if (process.waitForFinished(m_timeout) == false || process.exitCode() < 0) + reportError(); + else + reportSuccess(); + } else { + if (QProcess::startDetached(m_program, m_arguments)) + reportSuccess(); + else + reportError(); + } +} +void ExecuteCommand::commandFinished() +{ + debugOutput(0,"ExecuteCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Read File Implementation // +///////////////////////////////////////////////////// +ReadFileCommand::ReadFileCommand() + : AbstractCommand() + , m_currentPos(0) +{ + debugOutput(0, "ReadFileCommand::ReadFileCommand()"); + m_fileName.clear(); +} + +ReadFileCommand::~ReadFileCommand() +{ + debugOutput(0, "ReadFileCommand::~ReadFileCommand()"); + if (m_file.isOpen()) + m_file.close(); +} + +void ReadFileCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "ReadFileCommand::dataReceived()"); + if (m_fileName.isEmpty()) { + ReadFileOptions* option = (ReadFileOptions*) data.data(); + m_fileName = QLatin1String(option->fileName); + QFileInfo info(m_fileName); + m_file.setFileName(m_fileName); + ReadFileReply reply; + if (!info.exists() || !info.isFile() || !m_file.open(QIODevice::ReadOnly)) + reply.fileValid = false; + else + reply.fileValid = true; + reply.fileSize = info.size(); + m_fileSize = reply.fileSize; + socket()->write((char*) &reply, sizeof(reply)); + debugOutput(3, QString::fromLatin1("Reading file: %1").arg(m_fileName)); + } else { + QTcpSocket* sock = socket(); // design failure??? + if (data != COMMAND_SUCCESS || m_currentPos >= m_fileSize) { + sock->disconnectFromHost(); + return; + } + const int bufferSize = 1024; + QByteArray buffer = m_file.read(bufferSize); + m_currentPos += buffer.size(); + sock->write(buffer); + sock->waitForBytesWritten(); + } +} + +void ReadFileCommand::commandFinished() +{ + debugOutput(0, "ReadFileCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Read Directory Implementation // +///////////////////////////////////////////////////// +ReadDirectoryCommand::ReadDirectoryCommand() + : AbstractCommand() + , m_iterator(0) +{ + debugOutput(0, "ReadDirectoryCommand::ReadDirectoryCommand"); + m_dirName.clear(); +} + +ReadDirectoryCommand::~ReadDirectoryCommand() +{ + debugOutput(0, "ReadDirectoryCommand::~ReadDirectoryCommand()"); + delete m_iterator; +} + +void ReadDirectoryCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "ReadDirectoryCommand::dataReceived()"); + QTcpSocket* sock = socket(); + if (m_dirName.isEmpty()) { + ReadDirectoryOptions* option = (ReadDirectoryOptions*) data.data(); + QFileInfo info(QLatin1String(option->dirName)); + debugOutput(3, QString::fromLatin1("Reading Directory entries: %1").arg(option->dirName)); + ReadDirectoryReply reply; + if (!info.exists() || !info.isDir()) { + reply.itemCount = -1; + reply.entryValid = false; + } else { + m_dirName = QLatin1String(option->dirName); + m_dir.setPath(m_dirName); + m_iterator = new QDirIterator(m_dir); + reply.itemCount = m_dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).size(); + reply.entryValid = true; + } + sock->write((char*) &reply, sizeof(reply)); + sock->waitForBytesWritten(); + } else { + if (data != COMMAND_SUCCESS) { + qDebug() << "Something went wrong in the meantime"; + return; + } + ReadDirectoryItem reply; + if (m_iterator->hasNext()) { + m_iterator->next(); + QFileInfo info = m_iterator->fileInfo(); + strcpy(reply.name, qPrintable(info.absoluteFilePath())); + reply.isDirectory = info.isDir(); + if (!reply.isDirectory) + reply.size = info.size(); + } + reply.hasMore = m_iterator->hasNext(); + sock->write((char*) &reply, sizeof(reply)); + sock->waitForBytesWritten(); + } +} + +void ReadDirectoryCommand::commandFinished() +{ + debugOutput(0, "ReadDirectoryCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// File Time Implementation // +///////////////////////////////////////////////////// +FileTimeCommand::FileTimeCommand() + : AbstractCommand() +{ + debugOutput(0, "FileTimeCommand::FileTimeCommand()"); +} + +FileTimeCommand::~FileTimeCommand() +{ + debugOutput(0, "FileTimeCommand::~FileTimeCommand()"); +} + +void FileTimeCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "FileTimeCommand::dataReceived()"); + FileTimeOptions* option = (FileTimeOptions*) data.data(); + + FILETIME resultTime; + resultTime.dwLowDateTime = -1; + resultTime.dwHighDateTime = -1; + +#ifdef Q_OS_WIN + QString fileName = QLatin1String(option->fileName); + HANDLE deviceHandle = CreateFile(fileName.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + debugOutput(3, QString::fromLatin1("Asking FileTime: %1").arg(fileName)); + if (deviceHandle != INVALID_HANDLE_VALUE) { + FILETIME deviceCreationTime; + if (GetFileTime(deviceHandle, &deviceCreationTime, NULL, NULL)) { + resultTime = deviceCreationTime; + } + CloseHandle(deviceHandle); + } +#endif + QTcpSocket* sock = socket(); + sock->write((char*) &resultTime, sizeof(resultTime)); + sock->waitForBytesWritten(); +} + +void FileTimeCommand::commandFinished() +{ + debugOutput(0, "FileTimeCommand::commandFinished()"); +} + +///////////////////////////////////////////////////// +// Time Stamp Implementation // +///////////////////////////////////////////////////// +TimeStampCommand::TimeStampCommand() + : AbstractCommand() +{ + debugOutput(0, "TimeStampCommand::TimeStampCommand()"); +} + +TimeStampCommand::~TimeStampCommand() +{ + debugOutput(0, "TimeStampCommand::~TimeStampCommand()"); +} + +void TimeStampCommand::dataReceived(QByteArray &data) +{ + debugOutput(0, "TimeStampCommand::dataReceived()"); + FILETIME resultTime; + resultTime.dwLowDateTime = -1; + resultTime.dwHighDateTime = -1; + +#ifdef Q_OS_WIN + FILETIME stampTime = *((FILETIME*)data.data()); + + QString tmpFile = QString::fromLatin1("\\qt_tmp_ftime_convert"); + HANDLE remoteHandle = CreateFile(tmpFile.utf16(), GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (remoteHandle != INVALID_HANDLE_VALUE) { + if (!SetFileTime(remoteHandle, &stampTime, NULL, NULL)) { + CloseHandle(remoteHandle); + } else { + CloseHandle(remoteHandle); + remoteHandle = CreateFile(tmpFile.utf16(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (remoteHandle != INVALID_HANDLE_VALUE) { + if (GetFileTime(remoteHandle, &stampTime, NULL, NULL)) + resultTime = stampTime; + CloseHandle(remoteHandle); + DeleteFile(tmpFile.utf16()); + } + } + } + debugOutput(3, QString::fromLatin1("Asking TimeStamp")); +#endif + QTcpSocket* sock = socket(); + sock->write((char*) &resultTime, sizeof(resultTime)); + sock->waitForBytesWritten(); +} + +void TimeStampCommand::commandFinished() +{ + debugOutput(0, "TimeStampCommand::commandFinished()"); +} diff --git a/tools/qtestlib/wince/cetcpsyncserver/commands.h b/tools/qtestlib/wince/cetcpsyncserver/commands.h new file mode 100644 index 0000000..356c1aa --- /dev/null +++ b/tools/qtestlib/wince/cetcpsyncserver/commands.h @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef COMMANDS_INCL +#define COMMANDS_INCL + +#include "transfer_global.h" + +#include <QtNetwork/QTcpSocket> +#include <QtCore/QString> +#include <QtCore/QFile> +#include <QtCore/QDir> +#include <QtCore/QDirIterator> +#include <windows.h> + +// debug output +#define DEBUG_LEVEL 2 +inline void debugOutput(int level, const char* text) +{ + if (level >= DEBUG_LEVEL) + qDebug() << text; +} + +inline void debugOutput(int level, const QString &text) +{ + if (level >= DEBUG_LEVEL) + qDebug() << text; +} +// Basic abtract command class +class AbstractCommand : public QObject +{ + Q_OBJECT +public: + AbstractCommand(); + virtual ~AbstractCommand(); + + void setSocket(QTcpSocket*); + QTcpSocket* socket(); + + void reportSuccess(); + void reportError(); + +public slots: + virtual void dataReceived(QByteArray&); + virtual void commandFinished(); + +private slots: + void _readData(); + void _disconnect(); + +private: + QTcpSocket* m_socket; +}; + +// File Creation class +class CreateFileCommand : public AbstractCommand +{ + Q_OBJECT +public: + CreateFileCommand(); + ~CreateFileCommand(); + +public slots: + void dataReceived(QByteArray&); + void commandFinished(); + +private: + CreateFileOptions m_options; + QFile m_file; + int m_dataCount; +}; + +inline AbstractCommand* instCreateFile() { return new CreateFileCommand(); } + +// Directory Creation class +class CreateDirectoryCommand : public AbstractCommand +{ + Q_OBJECT +public: + CreateDirectoryCommand(); + ~CreateDirectoryCommand(); + +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +}; +inline AbstractCommand* instCreateDirectory() { return new CreateDirectoryCommand(); } + +// File copy class +class CopyFileCommand : public AbstractCommand +{ + Q_OBJECT +public: + CopyFileCommand(); + ~CopyFileCommand(); + +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +}; +inline AbstractCommand* instCopyFile() { return new CopyFileCommand(); } + +// Copy directory class +class CopyDirectoryCommand : public AbstractCommand +{ + Q_OBJECT +public: + CopyDirectoryCommand(); + ~CopyDirectoryCommand(); + +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +private: + bool copyDir(const QString &from, const QString &to, bool recursive); +}; +inline AbstractCommand* instCopyDirectory() { return new CopyDirectoryCommand(); } + +// Delete File class +class DeleteFileCommand : public AbstractCommand +{ + Q_OBJECT +public: + DeleteFileCommand(); + ~DeleteFileCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +}; +inline AbstractCommand* instDeleteFile() { return new DeleteFileCommand(); } + +// Delete Directory class +class DeleteDirectoryCommand : public AbstractCommand +{ + Q_OBJECT +public: + DeleteDirectoryCommand(); + ~DeleteDirectoryCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +private: + bool deleteDirectory(const QString &dirName, bool recursive, bool failIfContentExists); +}; +inline AbstractCommand* instDeleteDirectory() { return new DeleteDirectoryCommand(); } + +// Execute application class +class ExecuteCommand : public AbstractCommand +{ + Q_OBJECT +public: + ExecuteCommand(); + ~ExecuteCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +private: + void _doExecute(); + QString m_program; + QStringList m_arguments; + int m_argumentCount; + bool m_waitFinished; + int m_timeout; +}; +inline AbstractCommand* instExecution() { return new ExecuteCommand(); } + +// Read File class +class ReadFileCommand : public AbstractCommand +{ + Q_OBJECT +public: + ReadFileCommand(); + ~ReadFileCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +private: + QString m_fileName; + QFile m_file; + qint64 m_currentPos; + qint64 m_fileSize; +}; +inline AbstractCommand* instReadFile() { return new ReadFileCommand(); } + +// Read Directory class +class ReadDirectoryCommand : public AbstractCommand +{ + Q_OBJECT +public: + ReadDirectoryCommand(); + ~ReadDirectoryCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +private: + QString m_dirName; + QDir m_dir; + QDirIterator* m_iterator; +}; +inline AbstractCommand* instReadDirectory() { return new ReadDirectoryCommand(); } + +// Read File Time class +class FileTimeCommand : public AbstractCommand +{ + Q_OBJECT +public: + FileTimeCommand(); + ~FileTimeCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +}; +inline AbstractCommand* instFileTime() { return new FileTimeCommand(); } + +// Time stamp class +class TimeStampCommand : public AbstractCommand +{ + Q_OBJECT +public: + TimeStampCommand(); + ~TimeStampCommand(); +public slots: + void dataReceived(QByteArray&); + void commandFinished(); +}; +inline AbstractCommand* instTimeStamp() { return new TimeStampCommand(); } + +// Access part +typedef AbstractCommand* (*instantiator)(); + +struct CommandInfo +{ + CommandInfo(const QString &name, instantiator func) : commandName(name) , commandFunc(func) { } + QString commandName; + instantiator commandFunc; +}; + +inline QList<CommandInfo> availableCommands() +{ + QList<CommandInfo> list; + list.append(CommandInfo(QLatin1String(COMMAND_CREATE_FILE), instCreateFile)); + list.append(CommandInfo(QLatin1String(COMMAND_CREATE_DIRECTORY), instCreateDirectory)); + list.append(CommandInfo(QLatin1String(COMMAND_COPY_FILE), instCopyFile)); + list.append(CommandInfo(QLatin1String(COMMAND_COPY_DIRECTORY), instCopyDirectory)); + list.append(CommandInfo(QLatin1String(COMMAND_DELETE_FILE), instDeleteFile)); + list.append(CommandInfo(QLatin1String(COMMAND_DELETE_DIRECTORY), instDeleteDirectory)); + list.append(CommandInfo(QLatin1String(COMMAND_EXECUTE), instExecution)); + list.append(CommandInfo(QLatin1String(COMMAND_READ_FILE), instReadFile)); + list.append(CommandInfo(QLatin1String(COMMAND_READ_DIRECTORY), instReadDirectory)); + list.append(CommandInfo(QLatin1String(COMMAND_FILE_TIME), instFileTime)); + list.append(CommandInfo(QLatin1String(COMMAND_TIME_STAMP), instTimeStamp)); + return list; +} + +#endif diff --git a/tools/qtestlib/wince/cetcpsyncserver/connectionmanager.cpp b/tools/qtestlib/wince/cetcpsyncserver/connectionmanager.cpp new file mode 100644 index 0000000..901cd12 --- /dev/null +++ b/tools/qtestlib/wince/cetcpsyncserver/connectionmanager.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "connectionmanager.h" +#include "commands.h" +#include <QtCore/QDebug> + +ConnectionManager::ConnectionManager() + : QObject() + , m_server(0) +{ + debugOutput(0, "ConnectionManager::ConnectionManager()"); +} + +ConnectionManager::~ConnectionManager() +{ + debugOutput(0, "ConnectionManager::~ConnectionManager()"); + cleanUp(); +} + +bool ConnectionManager::init() +{ + debugOutput(0, "ConnectionManager::init()"); + debugOutput(3, "Initializing server..."); + cleanUp(); + m_server = new QTcpServer(this); + connect(m_server, SIGNAL(newConnection()), this, SLOT(newConnection())); + bool result = m_server->listen(QHostAddress::Any, SERVER_PORT); + if (!result) + debugOutput(3, QString::fromLatin1(" Error: Server start failed:") + m_server->errorString()); + debugOutput(3, " Waiting for action"); + return result; +} + +void ConnectionManager::cleanUp() +{ + debugOutput(0, "ConnectionManager::cleanUp()"); + + if (m_server) { + debugOutput(1, "Removing server instance..."); + disconnect(m_server, SIGNAL(newConnection()), this, SLOT(newConnection())); + delete m_server; + m_server = 0; + } +} + +void ConnectionManager::newConnection() +{ + debugOutput(0, "ConnectionManager::newConnection()"); + + QTcpSocket* connection = m_server->nextPendingConnection(); + if (!connection) { + debugOutput(3, "Received connection has empty socket"); + return; + } + debugOutput(0, QString::fromLatin1(" received a connection: %1").arg((int) connection)); + new Connection(connection); +} + +Connection::Connection(QTcpSocket *socket) + : QObject() + , m_connection(socket) + , m_command(0) +{ + connect(m_connection, SIGNAL(readyRead()), this, SLOT(receiveCommand())); + connect(m_connection, SIGNAL(disconnected()), this, SLOT(closedConnection())); +} + +Connection::~Connection() +{ + if (m_command) { + m_command->commandFinished(); + delete m_command; + m_command = 0; + } + delete m_connection; +} + +void Connection::receiveCommand() +{ + QByteArray arr = m_connection->readAll(); + debugOutput(1, QString::fromLatin1("Command received: ") + (arr)); + QList<CommandInfo> commands = availableCommands(); + for(QList<CommandInfo>::iterator it = commands.begin(); it != commands.end(); ++it) { + if (it->commandName == QString::fromLatin1(arr)) { + debugOutput(1, "Found command in list"); + disconnect(m_connection, SIGNAL(readyRead()), this, SLOT(receiveCommand())); + AbstractCommand* command = (*it).commandFunc(); + command->setSocket(m_connection); + m_command = command; + return; + } + } + debugOutput(2, QString::fromLatin1("Unknown command received: ") + (arr)); +} + +void Connection::closedConnection() +{ + debugOutput(0, "connection being closed..."); + this->deleteLater(); +} diff --git a/doc/src/functions.qdoc b/tools/qtestlib/wince/cetcpsyncserver/connectionmanager.h index e4c8c57..21183ac 100644 --- a/doc/src/functions.qdoc +++ b/tools/qtestlib/wince/cetcpsyncserver/connectionmanager.h @@ -3,7 +3,7 @@ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (qt-info@nokia.com) ** -** This file is part of the documentation of the Qt Toolkit. +** This file is part of the tools applications of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage @@ -38,26 +38,46 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ +#ifndef CONNECTION_MANAGER_INCL +#define CONNECTION_MANAGER_INCL -/**************************************************************************** -** -** Documentation for class overview. -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the Qt GUI Toolkit. -** EDITIONS: FREE, PROFESSIONAL, ENTERPRISE -** -****************************************************************************/ +#include "transfer_global.h" +#include "commands.h" + +#include <QtNetwork/QTcpServer> +#include <QtNetwork/QTcpSocket> + +class Connection : public QObject +{ + Q_OBJECT +public: + Connection(QTcpSocket* socket); + ~Connection(); + +public slots: + void receiveCommand(); + void closedConnection(); + +private: + QTcpSocket* m_connection; + AbstractCommand* m_command; +}; + +class ConnectionManager : public QObject +{ + Q_OBJECT +public: + ConnectionManager(); + ~ConnectionManager(); + + bool init(); -/*! - \page functions.html - \title Member Function Index - \ingroup classlists +public slots: + void cleanUp(); + void newConnection(); - Here is the list of all the documented member functions in the Qt - API with links to the class documentation for each function. +private: + QTcpServer* m_server; +}; - \generatelist functionindex -*/ +#endif diff --git a/tools/qtestlib/wince/cetcpsyncserver/main.cpp b/tools/qtestlib/wince/cetcpsyncserver/main.cpp new file mode 100644 index 0000000..19d38ea --- /dev/null +++ b/tools/qtestlib/wince/cetcpsyncserver/main.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "connectionmanager.h" + +#include <QtCore> +#include <QtNetwork> + +void messageOutput(QtMsgType type, const char *msg) +{ + switch(type) { + case QtDebugMsg: fprintf(stderr, "Debug: %s\n", msg); break; + case QtWarningMsg: fprintf(stderr, "Warning: %s\n", msg); break; + default: fprintf(stderr, "Some Msg: %s\n", msg); break; + } +} + +int main(int argc, char **argv) +{ + qInstallMsgHandler(messageOutput); + + QCoreApplication app(argc, argv); + ConnectionManager manager; + manager.init(); + return app.exec(); +} diff --git a/tools/qtestlib/wince/cetcpsyncserver/transfer_global.h b/tools/qtestlib/wince/cetcpsyncserver/transfer_global.h new file mode 100644 index 0000000..5b6ff23 --- /dev/null +++ b/tools/qtestlib/wince/cetcpsyncserver/transfer_global.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef TRANSFER_GLOBAL_H +#define TRANSFER_GLOBAL_H + +#include <QtCore/qglobal.h> +#ifdef Q_OS_WIN +#include <windows.h> +#endif + +#define SERVER_PORT 12145 + +#define MAX_NAME_LENGTH 512 +#define MAX_ARGUMENTS 10 + +// Defines for commands sent/received +#define COMMAND_CREATE_FILE "CREATEFILE" +#define COMMAND_CREATE_DIRECTORY "CREATEDIR" +#define COMMAND_COPY_FILE "COPYFILE" +#define COMMAND_COPY_DIRECTORY "COPYDIR" +#define COMMAND_DELETE_FILE "DELETEFILE" +#define COMMAND_DELETE_DIRECTORY "DELETEDIR" +#define COMMAND_EXECUTE "EXECUTE" +#define COMMAND_QUIT_SERVER "QUIT" +#define COMMAND_FILE_TIME "FILETIME" +#define COMMAND_TIME_STAMP "TIMESTAMP" + +// Report back commands +#define COMMAND_SUCCESS "SUCCESS" +#define COMMAND_ERROR "ERROR" + +// Defines for commands that send data back to requester +#define COMMAND_READ_FILE "READFILE" +#define COMMAND_READ_DIRECTORY "READDIR" + +#include <QtCore/qglobal.h> +// Option-Structures for commands + +struct CreateFileOptions +{ + char fileName[MAX_NAME_LENGTH]; +#ifdef Q_OS_WIN + FILETIME fileTime; + DWORD fileAttributes; +#endif + int fileSize; + bool overwriteExisting; +}; + +struct CreateDirectoryOptions +{ + char dirName[MAX_NAME_LENGTH]; + bool recursively; // in case of \foo\bar create \foo if it does not exist +}; + +struct CopyFileOptions +{ + char from[MAX_NAME_LENGTH]; + char to[MAX_NAME_LENGTH]; + bool overwriteExisting; +}; + +struct CopyDirectoryOptions +{ + char from[MAX_NAME_LENGTH]; + char to[MAX_NAME_LENGTH]; + bool recursive; +}; + +struct DeleteFileOptions +{ + char fileName[MAX_NAME_LENGTH]; +}; + +struct DeleteDirectoryOptions +{ + char dirName[MAX_NAME_LENGTH]; + bool recursive; + bool failIfContentExists; +}; + +struct ExecuteOptions +{ + char appName[MAX_NAME_LENGTH]; + int argumentsCount; + bool waitForFinished; + int timeout; +}; + +struct ReadFileOptions +{ + char fileName[MAX_NAME_LENGTH]; +}; + +struct ReadFileReply +{ + qint64 fileSize; + bool fileValid; +}; + +struct ReadDirectoryOptions +{ + char dirName[MAX_NAME_LENGTH]; +}; + +struct ReadDirectoryItem +{ + char name[MAX_NAME_LENGTH]; + qint64 size; + bool isDirectory; + bool hasMore; +}; + +#define FileTimeOptions ReadFileOptions + +struct ReadDirectoryReply +{ + bool entryValid; + int itemCount; // might change during iteration +}; +#endif diff --git a/tools/qtestlib/wince/cetest/cetcpsyncconnection.cpp b/tools/qtestlib/wince/cetest/cetcpsyncconnection.cpp new file mode 100644 index 0000000..621a6ac --- /dev/null +++ b/tools/qtestlib/wince/cetest/cetcpsyncconnection.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "CeTcpSyncConnection.h" +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo> + +static const QString ceTcpSyncProgram = "cetcpsync"; +extern void debugOutput(const QString& text, int level); + +CeTcpSyncConnection::CeTcpSyncConnection() + : AbstractRemoteConnection() + , connected(false) +{ +} + +CeTcpSyncConnection::~CeTcpSyncConnection() +{ + if (isConnected()) + disconnect(); +} + +bool CeTcpSyncConnection::connect(QVariantList&) +{ + // We connect with each command, so this is always true + // The command itself will fail then + const QString cmd = ceTcpSyncProgram + " noop"; + if (system(qPrintable(cmd)) != 0) + return false; + connected = true; + return true; +} + +void CeTcpSyncConnection::disconnect() +{ + connected = false; +} + +bool CeTcpSyncConnection::isConnected() const +{ + return connected; +} + +inline QString boolToString(bool b) +{ + return b ? "true" : "false"; +} + +static bool fileTimeFromString(FILETIME& ft, const QString& str) +{ + int idx = str.indexOf("*"); + if (idx <= 0) + return false; + bool ok; + ft.dwLowDateTime = str.left(idx).toULong(&ok); + if (!ok) + return false; + ft.dwHighDateTime = str.mid(idx+1).toULong(&ok); + return ok; +} + +static QString fileTimeToString(FILETIME& ft) +{ + return QString::number(ft.dwLowDateTime) + "*" + QString::number(ft.dwHighDateTime); +} + +bool CeTcpSyncConnection::copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists) +{ + QString cmd = ceTcpSyncProgram + " copyFileToDevice \"" + localSource + "\" \"" + deviceDest + "\" " + boolToString(failIfExists); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive) +{ + QString cmd = ceTcpSyncProgram + " copyDirectoryToDevice \"" + localSource + "\" \"" + deviceDest + "\" " + boolToString(recursive); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists) +{ + QString cmd = ceTcpSyncProgram + " copyFileFromDevice \"" + deviceSource + "\" \"" + localDest + "\" " + boolToString(failIfExists); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::copyDirectoryFromDevice(const QString &deviceSource, const QString &localDest, bool recursive) +{ + QString cmd = ceTcpSyncProgram + " copyDirectoryFromDevice \"" + deviceSource + "\" \"" + localDest + "\" " + boolToString(recursive); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::copyFile(const QString &srcFile, const QString &destFile, bool failIfExists) +{ + QString cmd = ceTcpSyncProgram + " copyFile \"" + srcFile + "\" \"" + destFile + "\" " + boolToString(failIfExists); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::copyDirectory(const QString &srcDirectory, const QString &destDirectory, + bool recursive) +{ + QString cmd = ceTcpSyncProgram + " copyDirectory \"" + srcDirectory + "\" \"" + destDirectory + "\" " + boolToString(recursive); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::deleteFile(const QString &fileName) +{ + QString cmd = ceTcpSyncProgram + " deleteFile \"" + fileName + "\""; + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::deleteDirectory(const QString &directory, bool recursive, bool failIfContentExists) +{ + QString cmd = ceTcpSyncProgram + " deleteDirectory \"" + directory + "\" " + boolToString(recursive) + " " + boolToString(failIfContentExists); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::execute(QString program, QString arguments, int timeout, int *returnValue) +{ + QString cmd = ceTcpSyncProgram + " execute \"" + program + "\" \"" + arguments + "\" " + QString::number(timeout); + int exitCode = system(qPrintable(cmd)); + if (returnValue) + *returnValue = exitCode; + return true; +} + +bool CeTcpSyncConnection::createDirectory(const QString &path, bool deleteBefore) +{ + QString cmd = ceTcpSyncProgram + " createDirectory \"" + path + "\" " + boolToString(deleteBefore); + return system(qPrintable(cmd)) == 0; +} + +bool CeTcpSyncConnection::timeStampForLocalFileTime(FILETIME* fTime) const +{ + QString cmd = ceTcpSyncProgram + " timeStampForLocalFileTime " + fileTimeToString(*fTime) + " >qt_cetcpsyncdata.txt"; + if (system(qPrintable(cmd)) != 0) + return false; + + QFile file("qt_cetcpsyncdata.txt"); + if (!file.open(QIODevice::ReadOnly)) + return false; + + bool result = fileTimeFromString(*fTime, file.readLine()); + file.close(); + file.remove(); + return result; +} + +bool CeTcpSyncConnection::fileCreationTime(const QString &fileName, FILETIME* deviceCreationTime) const +{ + QString cmd = ceTcpSyncProgram + " fileCreationTime \"" + fileName + "\" >qt_cetcpsyncdata.txt"; + if (system(qPrintable(cmd)) != 0) + return false; + + QFile file("qt_cetcpsyncdata.txt"); + if (!file.open(QIODevice::ReadOnly)) + return false; + + bool result = fileTimeFromString(*deviceCreationTime, file.readLine()); + file.close(); + file.remove(); + return result; +} diff --git a/tools/qtestlib/wince/cetest/cetcpsyncconnection.h b/tools/qtestlib/wince/cetest/cetcpsyncconnection.h new file mode 100644 index 0000000..1ef8423 --- /dev/null +++ b/tools/qtestlib/wince/cetest/cetcpsyncconnection.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://www.qtsoftware.com/contact. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CETCPSYNC_REMOTECONNECTION_H +#define CETCPSYNC_REMOTECONNECTION_H + +#include "remoteconnection.h" + +class CeTcpSyncConnection : public AbstractRemoteConnection +{ +public: + CeTcpSyncConnection(); + virtual ~CeTcpSyncConnection(); + + bool connect(QVariantList &list = QVariantList()); + void disconnect(); + bool isConnected() const; + + // These functions are designed for transfer between desktop and device + // Caution: deviceDest path has to be device specific (eg. no drive letters for CE) + bool copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists = false); + bool copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive = true); + bool copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists = false); + bool copyDirectoryFromDevice(const QString &deviceSource, const QString &localDest, bool recursive = true); + + bool timeStampForLocalFileTime(FILETIME*) const; + bool fileCreationTime(const QString &fileName, FILETIME*) const; + + // These functions only work on files existing on the device + bool copyFile(const QString&, const QString&, bool failIfExists = false); + bool copyDirectory(const QString&, const QString&, bool recursive = true); + bool deleteFile(const QString&); + bool deleteDirectory(const QString&, bool recursive = true, bool failIfContentExists = false); + bool moveFile(const QString&, const QString&, bool FailIfExists = false); + bool moveDirectory(const QString&, const QString&, bool recursive = true); + + bool createDirectory(const QString&, bool deleteBefore=false); + + bool execute(QString program, QString arguments = QString(), int timeout = -1, int *returnValue = NULL); +private: + bool connected; +}; + +#endif diff --git a/tools/qtestlib/wince/cetest/cetest.pro b/tools/qtestlib/wince/cetest/cetest.pro index d66fa33..a6b79da 100644 --- a/tools/qtestlib/wince/cetest/cetest.pro +++ b/tools/qtestlib/wince/cetest/cetest.pro @@ -28,20 +28,27 @@ DEPENDPATH += $$QT_BUILD_TREE/src/corelib/tools $$QT_BUILD_TREE/src/corelib/io # Input HEADERS += \ remoteconnection.h \ - activesyncconnection.h \ deployment.h SOURCES += \ remoteconnection.cpp \ - activesyncconnection.cpp \ deployment.cpp \ main.cpp -win32-msvc*:LIBS += ole32.lib advapi32.lib rapi.lib +LIBS += ole32.lib advapi32.lib + +isEmpty(QT_CE_RAPI_INC) { + DEFINES += QT_CETEST_NO_ACTIVESYNC + HEADERS += cetcpsyncconnection.h + SOURCES += cetcpsyncconnection.cpp +} else { + HEADERS += activesyncconnection.h + SOURCES += activesyncconnection.cpp + LIBS += rapi.lib + INCLUDEPATH += $$QT_CE_RAPI_INC + LIBS += -L$$QT_CE_RAPI_LIB +} include(qmake_include.pri) include(bootstrapped.pri) include($$QT_SOURCE_TREE/src/script/script.pri) - -INCLUDEPATH += $$QT_CE_RAPI_INC -LIBS += -L$$QT_CE_RAPI_LIB diff --git a/tools/qtestlib/wince/cetest/deployment.cpp b/tools/qtestlib/wince/cetest/deployment.cpp index 68f0197..c64ae26 100644 --- a/tools/qtestlib/wince/cetest/deployment.cpp +++ b/tools/qtestlib/wince/cetest/deployment.cpp @@ -168,6 +168,7 @@ void DeploymentHandler::initQtDeploy(QMakeProject *project, DeploymentList &depl } } +#ifndef QT_CETEST_NO_ACTIVESYNC // QtRemote deployment. We always deploy to \Windows if (!project->values("QMAKE_LIBDIR").isEmpty()) { QString remoteLibName = QLatin1String("QtRemote.dll"); @@ -177,6 +178,7 @@ void DeploymentHandler::initQtDeploy(QMakeProject *project, DeploymentList &depl else debugOutput(QString::fromLatin1("Could not find QtRemote. Might not be able to launch target executable"),0); } +#endif // C-runtime deployment QString runtime = project->values("QT_CE_C_RUNTIME").join(QLatin1String(" ")); diff --git a/tools/qtestlib/wince/cetest/main.cpp b/tools/qtestlib/wince/cetest/main.cpp index 782f6d9..e0e475c 100644 --- a/tools/qtestlib/wince/cetest/main.cpp +++ b/tools/qtestlib/wince/cetest/main.cpp @@ -39,7 +39,12 @@ ** ****************************************************************************/ -#include "activesyncconnection.h" +#ifdef QT_CETEST_NO_ACTIVESYNC +# include "cetcpsyncconnection.h" +#else +# include "activesyncconnection.h" +#endif + #include "deployment.h" #include <option.h> #include <project.h> @@ -290,7 +295,11 @@ int main(int argc, char **argv) projectDeploymentList.append(CopyItem(TestConfiguration::localExecutable , TestConfiguration::remoteExecutable)); // deploy +#ifdef QT_CETEST_NO_ACTIVESYNC + CeTcpSyncConnection connection; +#else ActiveSyncConnection connection; +#endif if (!connection.connect()) { cout << "Error: Could not connect to device!" << endl; return -1; |