diff options
author | Qt Continuous Integration System <qt-info@nokia.com> | 2009-12-07 08:44:33 (GMT) |
---|---|---|
committer | Qt Continuous Integration System <qt-info@nokia.com> | 2009-12-07 08:44:33 (GMT) |
commit | 3d4f904601490d6de83120a8716f67779f562403 (patch) | |
tree | b647f62ccabdf566cedc339784f594c4e1b59626 | |
parent | 319e6ca83d82a765e499d796b78c3edf27a92bbe (diff) | |
parent | 8fea31ca0ab98ef6fed7bb2d87d97f4f425b2078 (diff) | |
download | Qt-3d4f904601490d6de83120a8716f67779f562403.zip Qt-3d4f904601490d6de83120a8716f67779f562403.tar.gz Qt-3d4f904601490d6de83120a8716f67779f562403.tar.bz2 |
Merge branch '4.6' of scm.dev.nokia.troll.no:qt/oslo-staging-2 into qt-integration
* '4.6' of scm.dev.nokia.troll.no:qt/oslo-staging-2: (35 commits)
Detect GL2 paint engine based on fragment shaders, not programs
Automatically destroy VG pixmaps when the last window surface goes away
Change file update for compressed texture support.
Compressed texture binding for QtOpenGL: ETC1 and PVRTC
Fix WA_TranslucentBackground for QGLWidgets on X11/EGL
Add EGL_BUFFER_SIZE to QEglProperties::reduceConfiguration()
Fixes problem with QMenu when it's populated on the aboutToShow
Fixed OpenGL graphicssystem issues for OpenGL ES 2.0 platforms.
Made qobject.h "syncqt compliant".
Fix toolbar animation when restoring from expanded state
Make sure that QtGui links with QtCore for the win32-icc mkspec.
Fixes clipping bug in the embedded dialogs demo.
Made sure that we didn't reference dangling parallel anchors.
Add QMake configuration for Scratchbox's host-gcc compilera
Remove the linux-g++-gles2-experimental mkspec
Fix crash when QGraphicsItem destructor deletes other QGraphicsItem
Make lrelease link again with the win32-icc and win32-g++ mkspecs
Rename the global limit to g_offset.
Revert "Compile fix for win32-icc."
QGAL: Remove deprecated assert clauses
...
37 files changed, 1585 insertions, 678 deletions
diff --git a/dist/changes-4.6.1 b/dist/changes-4.6.1 index 6896358..5efa0e2 100644 --- a/dist/changes-4.6.1 +++ b/dist/changes-4.6.1 @@ -46,8 +46,11 @@ QtCore QtGui ----- - - foo - * bar + - QPixmap + * load() and loadFromData() can now support compressed GL textures + in the DDS, ETC1, PVRTC2, and PVRTC4 formats if the OpenGL graphics + system is active and the appropriate extensions are present in the + GL implementation. QtDBus ------ @@ -64,8 +67,10 @@ QtNetwork QtOpenGL -------- - - foo - * bar + - QGLContext + * bindTexture(QString) now supports DDS, ETC1, PVRTC2, and PVRTC4 + compressed textures if the appropriate extensions are present + in the GL implementation. QtScript -------- diff --git a/examples/graphicsview/anchorlayout/main.cpp b/examples/graphicsview/anchorlayout/main.cpp index f898d1d..389bc9e 100644 --- a/examples/graphicsview/anchorlayout/main.cpp +++ b/examples/graphicsview/anchorlayout/main.cpp @@ -122,8 +122,8 @@ int main(int argc, char **argv) scene.addItem(w); scene.setBackgroundBrush(Qt::darkGreen); - QGraphicsView *view = new QGraphicsView(&scene); - view->show(); + QGraphicsView view(&scene); + view.show(); return app.exec(); } diff --git a/mkspecs/linux-g++-gles2-experimental/qmake.conf b/mkspecs/linux-g++-gles2-experimental/qmake.conf deleted file mode 100644 index 9c28d17..0000000 --- a/mkspecs/linux-g++-gles2-experimental/qmake.conf +++ /dev/null @@ -1,22 +0,0 @@ -# -# Experimental qmake configuration for GLES2 -# - -MAKEFILE_GENERATOR = UNIX -TEMPLATE = app -CONFIG += qt warn_on release incremental link_prl -QT += core gui -QMAKE_INCREMENTAL_STYLE = sublib - -include(../common/g++.conf) - -QMAKE_LFLAGS += -Wl,-rpath-link=/usr/lib - -include(../common/linux.conf) - -QMAKE_LIBS_EGL = -lEGL -QMAKE_LIBS_OPENGL = -lGLESv2 -QMAKE_LIBS_OPENGL_QT = -lGLESv2 -lEGL - - -load(qt_config) diff --git a/mkspecs/linux-g++-gles2-experimental/qplatformdefs.h b/mkspecs/linux-g++-gles2-experimental/qplatformdefs.h deleted file mode 100644 index ecfbc73..0000000 --- a/mkspecs/linux-g++-gles2-experimental/qplatformdefs.h +++ /dev/null @@ -1,166 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the qmake spec of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPLATFORMDEFS_H -#define QPLATFORMDEFS_H - -// Get Qt defines/settings - -#include "qglobal.h" - -// Set any POSIX/XOPEN defines at the top of this file to turn on specific APIs - -// 1) need to reset default environment if _BSD_SOURCE is defined -// 2) need to specify POSIX thread interfaces explicitly in glibc 2.0 -// 3) it seems older glibc need this to include the X/Open stuff -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif - -#include <unistd.h> - - -// We are hot - unistd.h should have turned on the specific APIs we requested - -#include <features.h> -#include <pthread.h> -#include <dirent.h> -#include <fcntl.h> -#include <grp.h> -#include <pwd.h> -#include <signal.h> -#include <dlfcn.h> - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/ipc.h> -#include <sys/time.h> -#include <sys/shm.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <netinet/in.h> -#ifndef QT_NO_IPV6IFNAME -#include <net/if.h> -#endif - -#ifdef QT_LARGEFILE_SUPPORT -#define QT_STATBUF struct stat64 -#define QT_STATBUF4TSTAT struct stat64 -#define QT_STAT ::stat64 -#define QT_FSTAT ::fstat64 -#define QT_LSTAT ::lstat64 -#define QT_OPEN ::open64 -#define QT_TRUNCATE ::truncate64 -#define QT_FTRUNCATE ::ftruncate64 -#define QT_LSEEK ::lseek64 -#else -#define QT_STATBUF struct stat -#define QT_STATBUF4TSTAT struct stat -#define QT_STAT ::stat -#define QT_FSTAT ::fstat -#define QT_LSTAT ::lstat -#define QT_OPEN ::open -#define QT_TRUNCATE ::truncate -#define QT_FTRUNCATE ::ftruncate -#define QT_LSEEK ::lseek -#endif - -#ifdef QT_LARGEFILE_SUPPORT -#define QT_FOPEN ::fopen64 -#define QT_FSEEK ::fseeko64 -#define QT_FTELL ::ftello64 -#define QT_FGETPOS ::fgetpos64 -#define QT_FSETPOS ::fsetpos64 -#define QT_MMAP ::mmap64 -#define QT_FPOS_T fpos64_t -#define QT_OFF_T off64_t -#else -#define QT_FOPEN ::fopen -#define QT_FSEEK ::fseek -#define QT_FTELL ::ftell -#define QT_FGETPOS ::fgetpos -#define QT_FSETPOS ::fsetpos -#define QT_MMAP ::mmap -#define QT_FPOS_T fpos_t -#define QT_OFF_T long -#endif - -#define QT_STAT_REG S_IFREG -#define QT_STAT_DIR S_IFDIR -#define QT_STAT_MASK S_IFMT -#define QT_STAT_LNK S_IFLNK -#define QT_SOCKET_CONNECT ::connect -#define QT_SOCKET_BIND ::bind -#define QT_FILENO fileno -#define QT_CLOSE ::close -#define QT_READ ::read -#define QT_WRITE ::write -#define QT_ACCESS ::access -#define QT_GETCWD ::getcwd -#define QT_CHDIR ::chdir -#define QT_MKDIR ::mkdir -#define QT_RMDIR ::rmdir -#define QT_OPEN_LARGEFILE O_LARGEFILE -#define QT_OPEN_RDONLY O_RDONLY -#define QT_OPEN_WRONLY O_WRONLY -#define QT_OPEN_RDWR O_RDWR -#define QT_OPEN_CREAT O_CREAT -#define QT_OPEN_TRUNC O_TRUNC -#define QT_OPEN_APPEND O_APPEND - -#define QT_SIGNAL_RETTYPE void -#define QT_SIGNAL_ARGS int -#define QT_SIGNAL_IGNORE SIG_IGN - -#if defined(__GLIBC__) && (__GLIBC__ >= 2) -#define QT_SOCKLEN_T socklen_t -#else -#define QT_SOCKLEN_T int -#endif - -#if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500) -#define QT_SNPRINTF ::snprintf -#define QT_VSNPRINTF ::vsnprintf -#endif - - -#endif // QPLATFORMDEFS_H diff --git a/mkspecs/unsupported/linux-host-g++/qmake.conf b/mkspecs/unsupported/linux-host-g++/qmake.conf new file mode 100644 index 0000000..237477c --- /dev/null +++ b/mkspecs/unsupported/linux-host-g++/qmake.conf @@ -0,0 +1,138 @@ +# +# QMake configuration for Scratchbox's host-gcc compiler. +# +# This mkspec can be used as the platform mkspec when building +# Qt in scratchbox. If used as such, qmake and similar host +# tools will be compiled for the host architecture (E.g. x86) +# and thus not run in the emulator. This results in a +# significant improvement in build times. +# +# Note: The mkspec copied & pasted parts from common/gcc.conf +# and common/linux.conf as setBootstrapVariable in +# configure has a bug which stops re-assignments working +# for QMake variables (I.e. "QMAKE_foo = bar" is broken). + +MAKEFILE_GENERATOR = UNIX +TEMPLATE = app +CONFIG += qt warn_on release incremental link_prl +QT += core gui +QMAKE_INCREMENTAL_STYLE = sublib + +# +# qmake configuration for common gcc +# + +QMAKE_CC = host-gcc +QMAKE_CFLAGS += -pipe +QMAKE_CFLAGS_DEPS += -M +QMAKE_CFLAGS_WARN_ON += -Wall -W +QMAKE_CFLAGS_WARN_OFF += -w +QMAKE_CFLAGS_RELEASE += -O2 +QMAKE_CFLAGS_DEBUG += -g +QMAKE_CFLAGS_SHLIB += -fPIC +QMAKE_CFLAGS_STATIC_LIB += -fPIC +QMAKE_CFLAGS_YACC += -Wno-unused -Wno-parentheses +QMAKE_CFLAGS_HIDESYMS += -fvisibility=hidden +QMAKE_CFLAGS_PRECOMPILE += -x c-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} +QMAKE_CFLAGS_USE_PRECOMPILE += -include ${QMAKE_PCH_OUTPUT_BASE} + +QMAKE_CXX = host-g++ +QMAKE_CXXFLAGS += $$QMAKE_CFLAGS +QMAKE_CXXFLAGS_DEPS += $$QMAKE_CFLAGS_DEPS +QMAKE_CXXFLAGS_WARN_ON += $$QMAKE_CFLAGS_WARN_ON +QMAKE_CXXFLAGS_WARN_OFF += $$QMAKE_CFLAGS_WARN_OFF +QMAKE_CXXFLAGS_RELEASE += $$QMAKE_CFLAGS_RELEASE +QMAKE_CXXFLAGS_DEBUG += $$QMAKE_CFLAGS_DEBUG +QMAKE_CXXFLAGS_SHLIB += $$QMAKE_CFLAGS_SHLIB +QMAKE_CXXFLAGS_STATIC_LIB += $$QMAKE_CFLAGS_STATIC_LIB +QMAKE_CXXFLAGS_YACC += $$QMAKE_CFLAGS_YACC +QMAKE_CXXFLAGS_HIDESYMS += $$QMAKE_CFLAGS_HIDESYMS -fvisibility-inlines-hidden +QMAKE_CXXFLAGS_PRECOMPILE += -x c++-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} +QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE + +QMAKE_LINK = host-g++ +QMAKE_LINK_SHLIB = host-g++ +QMAKE_LINK_C = host-gcc +QMAKE_LINK_C_SHLIB = host-gcc +QMAKE_LFLAGS += +QMAKE_LFLAGS_RELEASE += -Wl,-O1 +QMAKE_LFLAGS_DEBUG += +QMAKE_LFLAGS_APP += +QMAKE_LFLAGS_SHLIB += -shared +QMAKE_LFLAGS_PLUGIN += $$QMAKE_LFLAGS_SHLIB +QMAKE_LFLAGS_SONAME += -Wl,-soname, +QMAKE_LFLAGS_THREAD += +QMAKE_LFLAGS_NOUNDEF += -Wl,--no-undefined +QMAKE_RPATH = -Wl,-rpath, + +QMAKE_PCH_OUTPUT_EXT = .gch + +# -Bsymbolic-functions (ld) support +QMAKE_LFLAGS_BSYMBOLIC_FUNC = -Wl,-Bsymbolic-functions +QMAKE_LFLAGS_DYNAMIC_LIST = -Wl,--dynamic-list, + +# +# qmake configuration for common linux +# + +QMAKE_CFLAGS_THREAD += -D_REENTRANT +QMAKE_CXXFLAGS_THREAD += $$QMAKE_CFLAGS_THREAD + +QMAKE_INCDIR = +QMAKE_LIBDIR = +QMAKE_INCDIR_X11 = /usr/X11R6/include +QMAKE_LIBDIR_X11 = /usr/X11R6/lib +QMAKE_INCDIR_QT = $$[QT_INSTALL_HEADERS] +QMAKE_LIBDIR_QT = $$[QT_INSTALL_LIBS] +QMAKE_INCDIR_OPENGL = /usr/X11R6/include +QMAKE_LIBDIR_OPENGL = /usr/X11R6/lib +QMAKE_INCDIR_OPENGL_ES1 = $$QMAKE_INCDIR_OPENGL +QMAKE_LIBDIR_OPENGL_ES1 = $$QMAKE_LIBDIR_OPENGL +QMAKE_INCDIR_OPENGL_ES1CL = $$QMAKE_INCDIR_OPENGL +QMAKE_LIBDIR_OPENGL_ES1CL = $$QMAKE_LIBDIR_OPENGL +QMAKE_INCDIR_OPENGL_ES2 = $$QMAKE_INCDIR_OPENGL +QMAKE_LIBDIR_OPENGL_ES2 = $$QMAKE_LIBDIR_OPENGL +QMAKE_INCDIR_EGL = +QMAKE_LIBDIR_EGL = +QMAKE_INCDIR_OPENVG = +QMAKE_LIBDIR_OPENVG = + +QMAKE_LIBS = +QMAKE_LIBS_DYNLOAD = -ldl +QMAKE_LIBS_X11 = -lXext -lX11 -lm +QMAKE_LIBS_X11SM = -lSM -lICE +QMAKE_LIBS_NIS = -lnsl +QMAKE_LIBS_EGL = -lEGL +QMAKE_LIBS_OPENGL = -lGLU -lGL +QMAKE_LIBS_OPENGL_QT = -lGL +QMAKE_LIBS_OPENGL_ES1 = -lGLES_CM +QMAKE_LIBS_OPENGL_ES1CL = -lGLES_CL +QMAKE_LIBS_OPENGL_ES2 = -lGLESv2 +QMAKE_LIBS_OPENVG = -lOpenVG +QMAKE_LIBS_THREAD = -lpthread + +QMAKE_MOC = $$[QT_INSTALL_BINS]/moc +QMAKE_UIC = $$[QT_INSTALL_BINS]/uic + +QMAKE_AR = host-ar cqs +QMAKE_OBJCOPY = host-objcopy +QMAKE_RANLIB = + +QMAKE_TAR = tar -cf +QMAKE_GZIP = gzip -9f + +QMAKE_COPY = cp -f +QMAKE_COPY_FILE = $(COPY) +QMAKE_COPY_DIR = $(COPY) -r +QMAKE_MOVE = mv -f +QMAKE_DEL_FILE = rm -f +QMAKE_DEL_DIR = rmdir +QMAKE_STRIP = host-strip +QMAKE_STRIPFLAGS_LIB += --strip-unneeded +QMAKE_CHK_DIR_EXISTS = test -d +QMAKE_MKDIR = mkdir -p +QMAKE_INSTALL_FILE = install -m 644 -p +QMAKE_INSTALL_PROGRAM = install -m 755 -p + +include(../common/unix.conf) +load(qt_config) diff --git a/mkspecs/unsupported/linux-host-g++/qplatformdefs.h b/mkspecs/unsupported/linux-host-g++/qplatformdefs.h new file mode 100644 index 0000000..60e0f5e --- /dev/null +++ b/mkspecs/unsupported/linux-host-g++/qplatformdefs.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the qmake spec of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "../../linux-g++/qplatformdefs.h" diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 4321c59..30cd011 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -153,15 +153,6 @@ QObjectPrivate::QObjectPrivate(int version) hasGuards = false; } -#ifdef Q_CC_INTEL -/* Workaround for a bug in win32-icc where it seems to inline ~QObjectPrivate too aggressive. - When icc compiles QtGui, it inlines ~QObjectPrivate so that it would generate a call to - ~QObjectData. However, ~QObjectData is not exported from QtCore, so it does not link. - See also QTBUG-5145 for info on how this manifested itself. - */ -# pragma auto_inline(off) -#endif - QObjectPrivate::~QObjectPrivate() { delete static_cast<QAbstractDynamicMetaObject*>(metaObject); @@ -173,9 +164,6 @@ QObjectPrivate::~QObjectPrivate() delete extraData; #endif } -#ifdef Q_CC_INTEL -# pragma auto_inline(on) -#endif int *QObjectPrivate::setDeleteWatch(QObjectPrivate *d, int *w) { diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 340498f..1a178e2 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -91,7 +91,11 @@ template<typename T> inline QList<T> qFindChildren(const QObject *, const QRegEx # endif #endif -class QObjectData { +class +#if defined(__INTEL_COMPILER) && defined(Q_OS_WIN) +Q_CORE_EXPORT +#endif +QObjectData { public: virtual ~QObjectData() = 0; QObject *q_ptr; diff --git a/src/gui/egl/qeglproperties.cpp b/src/gui/egl/qeglproperties.cpp index 2d37edb..4d4410a 100644 --- a/src/gui/egl/qeglproperties.cpp +++ b/src/gui/egl/qeglproperties.cpp @@ -229,6 +229,15 @@ void QEglProperties::setRenderableType(QEgl::API api) // reductions in complexity are possible. bool QEglProperties::reduceConfiguration() { + // EGL chooses configs with the highest color depth over + // those with smaller (but faster) lower color depths. One + // way around this is to set EGL_BUFFER_SIZE to 16, which + // trumps the others. Of course, there may not be a 16-bit + // config avaliable, so it's the first restraint we remove. + if (value(EGL_BUFFER_SIZE) == 16) { + removeValue(EGL_BUFFER_SIZE); + return true; + } if (removeValue(EGL_SAMPLE_BUFFERS)) { removeValue(EGL_SAMPLES); return true; diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index 0a2bf27..076b8fa 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -236,11 +236,13 @@ public: EdgeData *data = edgeData(v, v1); bool forward = data->from == v; if (forward) { - edges += QString::fromAscii("\"%1\"->\"%2\" [label=\"[%3,%4,%5]\" dir=both color=\"#000000:#a0a0a0\"] \n") + edges += QString::fromAscii("\"%1\"->\"%2\" [label=\"[%3,%4,%5,%6,%7]\" color=\"#000000\"] \n") .arg(v->toString()) .arg(v1->toString()) .arg(data->minSize) + .arg(data->minPrefSize) .arg(data->prefSize) + .arg(data->maxPrefSize) .arg(data->maxSize) ; } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index a6f5992..03ed63d 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -49,20 +49,34 @@ #endif #include "qgraphicsanchorlayout_p.h" + #ifndef QT_NO_GRAPHICSVIEW QT_BEGIN_NAMESPACE +// To ensure that all variables inside the simplex solver are non-negative, +// we limit the size of anchors in the interval [-limit, limit]. Then before +// sending them to the simplex solver we add "limit" as an offset, so that +// they are actually calculated in the interval [0, 2 * limit] +// To avoid numerical errors in platforms where we use single precision, +// we use a tighter limit for the variables range. +const qreal g_offset = (sizeof(qreal) == sizeof(double)) ? QWIDGETSIZE_MAX : QWIDGETSIZE_MAX / 32; QGraphicsAnchorPrivate::QGraphicsAnchorPrivate(int version) : QObjectPrivate(version), layoutPrivate(0), data(0), sizePolicy(QSizePolicy::Fixed), preferredSize(0), - hasSize(true), reversed(false) + hasSize(true) { } QGraphicsAnchorPrivate::~QGraphicsAnchorPrivate() { - layoutPrivate->removeAnchor(data->from, data->to); + if (data) { + // The QGraphicsAnchor was already deleted at this moment. We must clean + // the dangling pointer to avoid double deletion in the AnchorData dtor. + data->graphicsAnchor = 0; + + layoutPrivate->removeAnchor(data->from, data->to); + } } void QGraphicsAnchorPrivate::setSizePolicy(QSizePolicy::Policy policy) @@ -80,27 +94,12 @@ void QGraphicsAnchorPrivate::setSpacing(qreal value) return; } - const qreal rawValue = reversed ? -preferredSize : preferredSize; - if (hasSize && (rawValue == value)) + if (hasSize && (preferredSize == value)) return; // The anchor has an user-defined size hasSize = true; - - // The simplex solver cannot handle negative sizes. To workaround that, - // if value is less than zero, we reverse the anchor and set the absolute - // value; - if (value >= 0) { - preferredSize = value; - if (reversed) - qSwap(data->from, data->to); - reversed = false; - } else { - preferredSize = -value; - if (!reversed) - qSwap(data->from, data->to); - reversed = true; - } + preferredSize = value; layoutPrivate->q_func()->invalidate(); } @@ -114,9 +113,6 @@ void QGraphicsAnchorPrivate::unsetSpacing() // Return to standard direction hasSize = false; - if (reversed) - qSwap(data->from, data->to); - reversed = false; layoutPrivate->q_func()->invalidate(); } @@ -128,14 +124,14 @@ qreal QGraphicsAnchorPrivate::spacing() const return 0; } - return reversed ? -preferredSize : preferredSize; + return preferredSize; } -static void internalSizeHints(QSizePolicy::Policy policy, - qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint, - qreal *minSize, qreal *prefSize, - qreal *maxSize) +static void applySizePolicy(QSizePolicy::Policy policy, + qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint, + qreal *minSize, qreal *prefSize, + qreal *maxSize) { // minSize, prefSize and maxSize are initialized // with item's preferred Size: this is QSizePolicy::Fixed. @@ -167,6 +163,18 @@ static void internalSizeHints(QSizePolicy::Policy policy, *prefSize = prefSizeHint; } +AnchorData::~AnchorData() +{ + if (graphicsAnchor) { + // Remove reference to ourself to avoid double removal in + // QGraphicsAnchorPrivate dtor. + graphicsAnchor->d_func()->data = 0; + + delete graphicsAnchor; + } +} + + void AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) { QSizePolicy::Policy policy; @@ -182,6 +190,9 @@ void AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) maxSize = QWIDGETSIZE_MAX; if (isCenterAnchor) maxSize /= 2; + + minPrefSize = prefSize; + maxPrefSize = maxSize; return; } else { if (orientation == QGraphicsAnchorLayoutPrivate::Horizontal) { @@ -206,14 +217,18 @@ void AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) // It is a user-created anchor, fetch size information from the associated QGraphicsAnchor Q_ASSERT(graphicsAnchor); QGraphicsAnchorPrivate *anchorPrivate = graphicsAnchor->d_func(); + + // Policy, min and max sizes are straightforward policy = anchorPrivate->sizePolicy; minSizeHint = 0; + maxSizeHint = QWIDGETSIZE_MAX; + + // Preferred Size if (anchorPrivate->hasSize) { - // One can only configure the preferred size of a normal anchor. Their minimum and - // maximum "size hints" are always 0 and QWIDGETSIZE_MAX, correspondingly. However, - // their effective size hints might be narrowed down due to their size policies. + // Anchor has user-defined size prefSizeHint = anchorPrivate->preferredSize; } else { + // Fetch size information from style const Qt::Orientation orient = Qt::Orientation(QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge) + 1); qreal s = styleInfo->defaultSpacing(orient); if (s < 0) { @@ -229,10 +244,14 @@ void AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) } prefSizeHint = s; } - maxSizeHint = QWIDGETSIZE_MAX; } - internalSizeHints(policy, minSizeHint, prefSizeHint, maxSizeHint, - &minSize, &prefSize, &maxSize); + + // Fill minSize, prefSize and maxSize based on policy and sizeHints + applySizePolicy(policy, minSizeHint, prefSizeHint, maxSizeHint, + &minSize, &prefSize, &maxSize); + + minPrefSize = prefSize; + maxPrefSize = maxSize; // Set the anchor effective sizes to preferred. // @@ -252,13 +271,7 @@ void ParallelAnchorData::updateChildrenSizes() firstEdge->sizeAtPreferred = sizeAtPreferred; firstEdge->sizeAtMaximum = sizeAtMaximum; - // We have the convention that the first children will define the direction of the - // pararell group. So we can check whether the second edge is "forward" in relation - // to the group if it have the same direction as the first edge. Note that we don't - // use 'this->from' because it might be changed by vertex simplification. - const bool secondForward = (firstEdge->from == secondEdge->from); - - if (secondForward) { + if (secondForward()) { secondEdge->sizeAtMinimum = sizeAtMinimum; secondEdge->sizeAtPreferred = sizeAtPreferred; secondEdge->sizeAtMaximum = sizeAtMaximum; @@ -272,21 +285,40 @@ void ParallelAnchorData::updateChildrenSizes() secondEdge->updateChildrenSizes(); } -bool ParallelAnchorData::calculateSizeHints() -{ - // Note that parallel groups can lead to unfeasibility, so during calculation, we can - // find out one unfeasibility. Because of that this method return boolean. This can't - // happen in sequential, so there the method is void. +/* + \internal - // Account for parallel anchors where the second edge is backwards. - // We rely on the fact that a forward anchor of sizes min, pref, max is equivalent - // to a backwards anchor of size (-max, -pref, -min) + Initialize the parallel anchor size hints using the sizeHint information from + its children. - // Also see comments in updateChildrenSizes(). - const bool secondForward = (firstEdge->from == secondEdge->from); - const qreal secondMin = secondForward ? secondEdge->minSize : -secondEdge->maxSize; - const qreal secondPref = secondForward ? secondEdge->prefSize : -secondEdge->prefSize; - const qreal secondMax = secondForward ? secondEdge->maxSize : -secondEdge->minSize; + Note that parallel groups can lead to unfeasibility, so during calculation, we can + find out one unfeasibility. Because of that this method return boolean. This can't + happen in sequential, so there the method is void. + */ +bool ParallelAnchorData::calculateSizeHints() +{ + // Normalize second child sizes. + // A negative anchor of sizes min, minPref, pref, maxPref and max, is equivalent + // to a forward anchor of sizes -max, -maxPref, -pref, -minPref, -min + qreal secondMin; + qreal secondMinPref; + qreal secondPref; + qreal secondMaxPref; + qreal secondMax; + + if (secondForward()) { + secondMin = secondEdge->minSize; + secondMinPref = secondEdge->minPrefSize; + secondPref = secondEdge->prefSize; + secondMaxPref = secondEdge->maxPrefSize; + secondMax = secondEdge->maxSize; + } else { + secondMin = -secondEdge->maxSize; + secondMinPref = -secondEdge->maxPrefSize; + secondPref = -secondEdge->prefSize; + secondMaxPref = -secondEdge->minPrefSize; + secondMax = -secondEdge->minSize; + } minSize = qMax(firstEdge->minSize, secondMin); maxSize = qMin(firstEdge->maxSize, secondMax); @@ -298,23 +330,72 @@ bool ParallelAnchorData::calculateSizeHints() return false; } - // The equivalent preferred Size of a parallel anchor is calculated as to - // reduce the deviation from the original preferred sizes _and_ to avoid shrinking - // items below their preferred sizes, unless strictly needed. - - // ### This logic only holds if all anchors in the layout are "well-behaved" in the - // following terms: + // Preferred size calculation + // The calculation of preferred size is done as follows: + // + // 1) Check whether one of the child anchors is the layout structural anchor + // If so, we can simply copy the preferred information from the other child, + // after bounding it to our minimum and maximum sizes. + // If not, then we proceed with the actual calculations. // - // - There are no negative-sized anchors - // - All sequential anchors are composed of children in the same direction as the - // sequential anchor itself + // 2) The whole algorithm for preferred size calculation is based on the fact + // that, if a given anchor cannot remain at its preferred size, it'd rather + // grow than shrink. // - // With these assumptions we can grow a child knowing that no hidden items will - // have to shrink as the result of that. - // If any of these does not hold, we have a situation where the ParallelAnchor - // does not have enough information to calculate its equivalent prefSize. - prefSize = qMax(firstEdge->prefSize, secondPref); - prefSize = qMin(prefSize, maxSize); + // What happens though is that while this affirmative is true for simple + // anchors, it may not be true for sequential anchors that have one or more + // reversed anchors inside it. That happens because when a sequential anchor + // grows, any reversed anchors inside it may be required to shrink, something + // we try to avoid, as said above. + // + // To overcome this, besides their actual preferred size "prefSize", each anchor + // exports what we call "minPrefSize" and "maxPrefSize". These two values define + // a surrounding interval where, if required to move, the anchor would rather + // remain inside. + // + // For standard anchors, this area simply represents the region between + // prefSize and maxSize, which makes sense since our first affirmation. + // For composed anchors, these values are calculated as to reduce the global + // "damage", that is, to reduce the total deviation and the total amount of + // anchors that had to shrink. + + if (firstEdge->isLayoutAnchor) { + prefSize = qBound(minSize, secondPref, maxSize); + minPrefSize = qBound(minSize, secondMinPref, maxSize); + maxPrefSize = qBound(minSize, secondMaxPref, maxSize); + } else if (secondEdge->isLayoutAnchor) { + prefSize = qBound(minSize, firstEdge->prefSize, maxSize); + minPrefSize = qBound(minSize, firstEdge->minPrefSize, maxSize); + maxPrefSize = qBound(minSize, firstEdge->maxPrefSize, maxSize); + } else { + // Calculate the intersection between the "preferred" regions of each child + const qreal lowerBoundary = + qBound(minSize, qMax(firstEdge->minPrefSize, secondMinPref), maxSize); + const qreal upperBoundary = + qBound(minSize, qMin(firstEdge->maxPrefSize, secondMaxPref), maxSize); + const qreal prefMean = + qBound(minSize, (firstEdge->prefSize + secondPref) / 2, maxSize); + + if (lowerBoundary < upperBoundary) { + // If there is an intersection between the two regions, this intersection + // will be used as the preferred region of the parallel anchor itself. + // The preferred size will be the bounded average between the two preferred + // sizes. + prefSize = qBound(lowerBoundary, prefMean, upperBoundary); + minPrefSize = lowerBoundary; + maxPrefSize = upperBoundary; + } else { + // If there is no intersection, we have to attribute "damage" to at least + // one of the children. The minimum total damage is achieved in points + // inside the region that extends from (1) the upper boundary of the lower + // region to (2) the lower boundary of the upper region. + // Then, we expose this region as _our_ preferred region and once again, + // use the bounded average as our preferred size. + prefSize = qBound(upperBoundary, prefMean, lowerBoundary); + minPrefSize = upperBoundary; + maxPrefSize = lowerBoundary; + } + } // See comment in AnchorData::refreshSizeHints() about sizeAt* values sizeAtMinimum = prefSize; @@ -332,19 +413,28 @@ bool ParallelAnchorData::calculateSizeHints() 1 is at Maximum */ static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal value, qreal min, - qreal pref, qreal max) + qreal minPref, qreal pref, + qreal maxPref, qreal max) { QGraphicsAnchorLayoutPrivate::Interval interval; qreal lower; qreal upper; - if (value < pref) { - interval = QGraphicsAnchorLayoutPrivate::MinToPreferred; + if (value < minPref) { + interval = QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred; lower = min; + upper = minPref; + } else if (value < pref) { + interval = QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred; + lower = minPref; upper = pref; - } else { - interval = QGraphicsAnchorLayoutPrivate::PreferredToMax; + } else if (value < maxPref) { + interval = QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred; lower = pref; + upper = maxPref; + } else { + interval = QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum; + lower = maxPref; upper = max; } @@ -359,19 +449,26 @@ static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal valu } static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> &factor, - qreal min, qreal pref, - qreal max) + qreal min, qreal minPref, qreal pref, qreal maxPref, qreal max) { qreal lower; qreal upper; switch (factor.first) { - case QGraphicsAnchorLayoutPrivate::MinToPreferred: + case QGraphicsAnchorLayoutPrivate::MinimumToMinPreferred: lower = min; + upper = minPref; + break; + case QGraphicsAnchorLayoutPrivate::MinPreferredToPreferred: + lower = minPref; upper = pref; break; - case QGraphicsAnchorLayoutPrivate::PreferredToMax: + case QGraphicsAnchorLayoutPrivate::PreferredToMaxPreferred: lower = pref; + upper = maxPref; + break; + case QGraphicsAnchorLayoutPrivate::MaxPreferredToMaximum: + lower = maxPref; upper = max; break; } @@ -381,34 +478,43 @@ static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qre void SequentialAnchorData::updateChildrenSizes() { - // ### REMOVE ME - // ### check whether we are guarantee to get those or we need to warn stuff at this - // point. - Q_ASSERT(sizeAtMinimum > minSize || qAbs(sizeAtMinimum - minSize) < 0.00000001); - Q_ASSERT(sizeAtPreferred > minSize || qAbs(sizeAtPreferred - minSize) < 0.00000001); - Q_ASSERT(sizeAtMaximum > minSize || qAbs(sizeAtMaximum - minSize) < 0.00000001); - - // These may be false if this anchor was in parallel with the layout stucture - // Q_ASSERT(sizeAtMinimum < maxSize || qAbs(sizeAtMinimum - maxSize) < 0.00000001); - // Q_ASSERT(sizeAtPreferred < maxSize || qAbs(sizeAtPreferred - maxSize) < 0.00000001); - // Q_ASSERT(sizeAtMaximum < maxSize || qAbs(sizeAtMaximum - maxSize) < 0.00000001); - // Band here refers if the value is in the Minimum To Preferred // band (the lower band) or the Preferred To Maximum (the upper band). const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> minFactor = - getFactor(sizeAtMinimum, minSize, prefSize, maxSize); + getFactor(sizeAtMinimum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize); const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> prefFactor = - getFactor(sizeAtPreferred, minSize, prefSize, maxSize); + getFactor(sizeAtPreferred, minSize, minPrefSize, prefSize, maxPrefSize, maxSize); const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> maxFactor = - getFactor(sizeAtMaximum, minSize, prefSize, maxSize); + getFactor(sizeAtMaximum, minSize, minPrefSize, prefSize, maxPrefSize, maxSize); + + // XXX This is not safe if Vertex simplification takes place after the sequential + // anchor is created. In that case, "prev" will be a group-vertex, different from + // "from" or "to", that _contains_ one of them. + AnchorVertex *prev = from; for (int i = 0; i < m_edges.count(); ++i) { AnchorData *e = m_edges.at(i); - e->sizeAtMinimum = interpolate(minFactor, e->minSize, e->prefSize, e->maxSize); - e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->prefSize, e->maxSize); - e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->prefSize, e->maxSize); + const bool edgeIsForward = (e->from == prev); + if (edgeIsForward) { + e->sizeAtMinimum = interpolate(minFactor, e->minSize, e->minPrefSize, + e->prefSize, e->maxPrefSize, e->maxSize); + e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->minPrefSize, + e->prefSize, e->maxPrefSize, e->maxSize); + e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->minPrefSize, + e->prefSize, e->maxPrefSize, e->maxSize); + prev = e->to; + } else { + Q_ASSERT(prev == e->to); + e->sizeAtMinimum = interpolate(minFactor, e->maxSize, e->maxPrefSize, + e->prefSize, e->minPrefSize, e->minSize); + e->sizeAtPreferred = interpolate(prefFactor, e->maxSize, e->maxPrefSize, + e->prefSize, e->minPrefSize, e->minSize); + e->sizeAtMaximum = interpolate(maxFactor, e->maxSize, e->maxPrefSize, + e->prefSize, e->minPrefSize, e->minSize); + prev = e->from; + } e->updateChildrenSizes(); } @@ -419,12 +525,31 @@ void SequentialAnchorData::calculateSizeHints() minSize = 0; prefSize = 0; maxSize = 0; + minPrefSize = 0; + maxPrefSize = 0; + + AnchorVertex *prev = from; for (int i = 0; i < m_edges.count(); ++i) { AnchorData *edge = m_edges.at(i); - minSize += edge->minSize; - prefSize += edge->prefSize; - maxSize += edge->maxSize; + + const bool edgeIsForward = (edge->from == prev); + if (edgeIsForward) { + minSize += edge->minSize; + prefSize += edge->prefSize; + maxSize += edge->maxSize; + minPrefSize += edge->minPrefSize; + maxPrefSize += edge->maxPrefSize; + prev = edge->to; + } else { + Q_ASSERT(prev == edge->to); + minSize -= edge->maxSize; + prefSize -= edge->prefSize; + maxSize -= edge->minSize; + minPrefSize -= edge->maxPrefSize; + maxPrefSize -= edge->minPrefSize; + prev = edge->from; + } } // See comment in AnchorData::refreshSizeHints() about sizeAt* values @@ -588,16 +713,25 @@ AnchorData *QGraphicsAnchorLayoutPrivate::addAnchorMaybeParallel(AnchorData *new AnchorData *child = children[i]; QList<QSimplexConstraint *> *childConstraints = childrenConstraints[i]; + // We need to fix the second child constraints if the parallel group will have the + // opposite direction of the second child anchor. For the point of view of external + // entities, this anchor was reversed. So if at some point we say that the parallel + // has a value of 20, this mean that the second child (when reversed) will be + // assigned -20. + const bool needsReverse = i == 1 && !parallel->secondForward(); + if (!child->isCenterAnchor) continue; parallel->isCenterAnchor = true; - for (int i = 0; i < constraints.count(); ++i) { - QSimplexConstraint *c = constraints[i]; + for (int j = 0; j < constraints.count(); ++j) { + QSimplexConstraint *c = constraints[j]; if (c->variables.contains(child)) { childConstraints->append(c); qreal v = c->variables.take(child); + if (needsReverse) + v *= -1; c->variables.insert(parallel, v); } } @@ -628,24 +762,10 @@ static AnchorData *createSequence(Graph<AnchorVertex, AnchorData> *graph, const QVector<AnchorVertex*> &vertices, AnchorVertex *after) { - AnchorData *data = graph->edgeData(before, vertices.first()); - Q_ASSERT(data); - - const bool forward = (before == data->from); - QVector<AnchorVertex *> orderedVertices; - - if (forward) { - orderedVertices = vertices; - } else { - qSwap(before, after); - for (int i = vertices.count() - 1; i >= 0; --i) - orderedVertices.append(vertices.at(i)); - } - #if defined(QT_DEBUG) && 0 QString strVertices; - for (int i = 0; i < orderedVertices.count(); ++i) { - strVertices += QString::fromAscii("%1 - ").arg(orderedVertices.at(i)->toString()); + for (int i = 0; i < vertices.count(); ++i) { + strVertices += QString::fromAscii("%1 - ").arg(vertices.at(i)->toString()); } QString strPath = QString::fromAscii("%1 - %2%3").arg(before->toString(), strVertices, after->toString()); qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); @@ -654,15 +774,22 @@ static AnchorData *createSequence(Graph<AnchorVertex, AnchorData> *graph, AnchorVertex *prev = before; QVector<AnchorData *> edges; - for (int i = 0; i <= orderedVertices.count(); ++i) { - AnchorVertex *next = (i < orderedVertices.count()) ? orderedVertices.at(i) : after; + // Take from the graph, the edges that will be simplificated + for (int i = 0; i < vertices.count(); ++i) { + AnchorVertex *next = vertices.at(i); AnchorData *ad = graph->takeEdge(prev, next); Q_ASSERT(ad); edges.append(ad); prev = next; } - SequentialAnchorData *sequence = new SequentialAnchorData(orderedVertices, edges); + // Take the last edge (not covered in the loop above) + AnchorData *ad = graph->takeEdge(vertices.last(), after); + Q_ASSERT(ad); + edges.append(ad); + + // Create sequence + SequentialAnchorData *sequence = new SequentialAnchorData(vertices, edges); sequence->from = before; sequence->to = after; @@ -922,7 +1049,6 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP QStack<QPair<AnchorVertex *, AnchorVertex *> > stack; stack.push(qMakePair(static_cast<AnchorVertex *>(0), layoutFirstVertex[orientation])); QVector<AnchorVertex*> candidates; - bool candidatesForward = true; // Walk depth-first, in the stack we store start of the candidate sequence (beforeSequence) // and the vertex to be visited. @@ -938,9 +1064,8 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP // A vertex can trigger an end of sequence if // (a) it is a layout vertex, we don't simplify away the layout vertices; // (b) it does not have exactly 2 adjacents; - // (c) it will change the direction of the sequence; - // (d) its next adjacent is already visited (a cycle in the graph); - // (e) the next anchor is a center anchor. + // (c) its next adjacent is already visited (a cycle in the graph). + // (d) the next anchor is a center anchor. const QList<AnchorVertex *> &adjacents = g.adjacentVertices(v); const bool isLayoutVertex = v->m_item == q; @@ -955,19 +1080,10 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP endOfSequence = isLayoutVertex || adjacents.count() != 2; if (!endOfSequence) { - // If this is the first vertice, determine what is the direction to use for this - // sequence. - if (candidates.isEmpty()) { - const AnchorData *data = g.edgeData(beforeSequence, v); - Q_ASSERT(data); - candidatesForward = (beforeSequence == data->from); - } - // This is a tricky part. We peek at the next vertex to find out whether // - // - the edge from this vertex to the next vertex has the same direction; - // - we already visited the next vertex; - // - the next anchor is a center. + // - we already visited the next vertex (c); + // - the next anchor is a center (d). // // Those are needed to identify the remaining end of sequence cases. Note that unlike // (a) and (b), we preempt the end of sequence by looking into the next vertex. @@ -985,22 +1101,17 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP const AnchorData *data = g.edgeData(v, after); Q_ASSERT(data); - const bool willChangeDirection = (candidatesForward != (v == data->from)); const bool cycleFound = visited.contains(after); - // Now cases (c), (d) and (e)... - endOfSequence = willChangeDirection || cycleFound || data->isCenterAnchor; + // Now cases (c) and (d)... + endOfSequence = cycleFound || data->isCenterAnchor; - if (endOfSequence) { - if (!willChangeDirection) { - // If the direction will not change, we can add the current vertex to the - // candidates list and we know that 'after' can be used as afterSequence. - candidates.append(v); - afterSequence = after; - } - } else { + if (!endOfSequence) { // If it's not an end of sequence, then the vertex didn't trigger neither of the - // previously four cases, so it can be added to the candidates list. + // previously three cases, so it can be added to the candidates list. + candidates.append(v); + } else if (cycleFound && (beforeSequence != after)) { + afterSequence = after; candidates.append(v); } } @@ -1143,9 +1254,15 @@ void QGraphicsAnchorLayoutPrivate::restoreSimplifiedConstraints(ParallelAnchorDa c->variables.insert(parallel->firstEdge, v); } + // When restoring, we might have to revert constraints back. See comments on + // addAnchorMaybeParallel(). + const bool needsReverse = !parallel->secondForward(); + for (int i = 0; i < parallel->m_secondConstraints.count(); ++i) { QSimplexConstraint *c = parallel->m_secondConstraints.at(i); qreal v = c->variables[parallel]; + if (needsReverse) + v *= -1; c->variables.remove(parallel); c->variables.insert(parallel->secondEdge, v); } @@ -1187,7 +1304,22 @@ void QGraphicsAnchorLayoutPrivate::restoreVertices(Orientation orientation) Graph<AnchorVertex, AnchorData> &g = graph[orientation]; QList<AnchorVertexPair *> &toRestore = simplifiedVertices[orientation]; - // We will restore the vertices in the inverse order of creation, this way we ensure that + // Since we keep a list of parallel anchors and vertices that were created during vertex + // simplification, we can now iterate on those lists instead of traversing the graph + // recursively. + + // First, restore the constraints changed when we created parallel anchors. Note that this + // works at this point because the constraints doesn't depend on vertex information and at + // this point it's always safe to identify whether the second child is forward or backwards. + // In the next step, we'll change the anchors vertices so that would not be possible anymore. + QList<AnchorData *> ¶llelAnchors = anchorsFromSimplifiedVertices[orientation]; + + for (int i = parallelAnchors.count() - 1; i >= 0; --i) { + ParallelAnchorData *parallel = static_cast<ParallelAnchorData *>(parallelAnchors.at(i)); + restoreSimplifiedConstraints(parallel); + } + + // Then, we will restore the vertices in the inverse order of creation, this way we ensure that // the vertex being restored was not wrapped by another simplification. for (int i = toRestore.count() - 1; i >= 0; --i) { AnchorVertexPair *pair = toRestore.at(i); @@ -1231,20 +1363,9 @@ void QGraphicsAnchorLayoutPrivate::restoreVertices(Orientation orientation) delete pair; } - toRestore.clear(); - - // The restoration process for vertex simplification also restored the effect of the - // parallel anchors created during vertex simplification, so we just need to restore - // the constraints in case of parallels that contain center anchors. For the same - // reason as above, order matters here. - QList<AnchorData *> ¶llelAnchors = anchorsFromSimplifiedVertices[orientation]; - - for (int i = parallelAnchors.count() - 1; i >= 0; --i) { - ParallelAnchorData *parallel = static_cast<ParallelAnchorData *>(parallelAnchors.at(i)); - restoreSimplifiedConstraints(parallel); - delete parallel; - } + qDeleteAll(parallelAnchors); parallelAnchors.clear(); + toRestore.clear(); } QGraphicsAnchorLayoutPrivate::Orientation @@ -1652,6 +1773,10 @@ QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *fi QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) { + // Do not expose internal anchors + if (firstItem == secondItem) + return 0; + const Orientation orientation = edgeOrientation(firstEdge); AnchorVertex *v1 = internalVertex(firstItem, firstEdge); AnchorVertex *v2 = internalVertex(secondItem, secondEdge); @@ -1659,8 +1784,16 @@ QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *fi QGraphicsAnchor *graphicsAnchor = 0; AnchorData *data = graph[orientation].edgeData(v1, v2); - if (data) - graphicsAnchor = acquireGraphicsAnchor(data); + if (data) { + // We could use "acquireGraphicsAnchor" here, but to avoid a regression where + // an internal anchor was wrongly exposed, I want to ensure no new + // QGraphicsAnchor instances are created by this call. + // This assumption must hold because anchors are either user-created (and already + // have their public object created), or they are internal (and must not reach + // this point). + Q_ASSERT(data->graphicsAnchor); + graphicsAnchor = data->graphicsAnchor; + } return graphicsAnchor; } @@ -1675,12 +1808,16 @@ void QGraphicsAnchorLayoutPrivate::removeAnchor(AnchorVertex *firstVertex, { Q_Q(QGraphicsAnchorLayout); - // Actually delete the anchor - removeAnchor_helper(firstVertex, secondVertex); - + // Save references to items while it's safe to assume the vertices exist QGraphicsLayoutItem *firstItem = firstVertex->m_item; QGraphicsLayoutItem *secondItem = secondVertex->m_item; + // Delete the anchor (may trigger deletion of center vertices) + removeAnchor_helper(firstVertex, secondVertex); + + // Ensure no dangling pointer is left behind + firstVertex = secondVertex = 0; + // Checking if the item stays in the layout or not bool keepFirstItem = false; bool keepSecondItem = false; @@ -2047,6 +2184,25 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( /*! \internal + Shift all the constraints by a certain amount. This allows us to deal with negative values in + the linear program if they are bounded by a certain limit. Functions should be careful to + call it again with a negative amount, to shift the constraints back. +*/ +static void shiftConstraints(const QList<QSimplexConstraint *> &constraints, qreal amount) +{ + for (int i = 0; i < constraints.count(); ++i) { + QSimplexConstraint *c = constraints.at(i); + qreal multiplier = 0; + foreach (qreal v, c->variables.values()) { + multiplier += v; + } + c->constant += multiplier * amount; + } +} + +/*! + \internal + Calculate the sizes for all anchors which are part of the trunk. This works on top of a (possibly) simplified graph. */ @@ -2067,12 +2223,14 @@ bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables); QList<QSimplexConstraint *> allConstraints = constraints + sizeHintConstraints; + shiftConstraints(allConstraints, g_offset); + // Solve min and max size hints qreal min, max; feasible = solveMinMax(allConstraints, path, &min, &max); if (feasible) { - solvePreferred(allConstraints, variables); + solvePreferred(constraints, variables); // Calculate and set the preferred size for the layout, // from the edge sizes that were calculated above. @@ -2090,6 +2248,7 @@ bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const } qDeleteAll(sizeHintConstraints); + shiftConstraints(constraints, -g_offset); } else { // No Simplex is necessary because the path was simplified all the way to a single @@ -2120,8 +2279,8 @@ bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstraint *> &constraints, const QList<AnchorData *> &variables) { - QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables); - bool feasible = solvePreferred(constraints + sizeHintConstraints, variables); + shiftConstraints(constraints, g_offset); + bool feasible = solvePreferred(constraints, variables); if (feasible) { // Propagate size at preferred to other sizes. Semi-floats always will be @@ -2134,7 +2293,7 @@ bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstra } } - qDeleteAll(sizeHintConstraints); + shiftConstraints(constraints, -g_offset); return feasible; } @@ -2298,17 +2457,23 @@ QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHin if (ad->dependency == AnchorData::Slave) continue; - if ((ad->minSize == ad->maxSize) || qFuzzyCompare(ad->minSize, ad->maxSize)) { + // To use negative variables inside simplex, we shift them so the minimum negative value is + // mapped to zero before solving. To make sure that it works, we need to guarantee that the + // variables are all inside a certain boundary. + qreal boundedMin = qBound(-g_offset, ad->minSize, g_offset); + qreal boundedMax = qBound(-g_offset, ad->maxSize, g_offset); + + if ((boundedMin == boundedMax) || qFuzzyCompare(boundedMin, boundedMax)) { QSimplexConstraint *c = new QSimplexConstraint; c->variables.insert(ad, 1.0); - c->constant = ad->minSize; + c->constant = boundedMin; c->ratio = QSimplexConstraint::Equal; anchorConstraints += c; unboundedProblem = false; } else { QSimplexConstraint *c = new QSimplexConstraint; c->variables.insert(ad, 1.0); - c->constant = ad->minSize; + c->constant = boundedMin; c->ratio = QSimplexConstraint::MoreOrEqual; anchorConstraints += c; @@ -2320,7 +2485,7 @@ QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHin c = new QSimplexConstraint; c->variables.insert(ad, 1.0); - c->constant = ad->maxSize; + c->constant = boundedMax; c->ratio = QSimplexConstraint::LessOrEqual; anchorConstraints += c; unboundedProblem = false; @@ -2331,7 +2496,8 @@ QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHin if (unboundedProblem) { QSimplexConstraint *c = new QSimplexConstraint; c->variables.insert(layoutEdge, 1.0); - c->constant = QWIDGETSIZE_MAX; + // The maximum size that the layout can take + c->constant = g_offset; c->ratio = QSimplexConstraint::LessOrEqual; anchorConstraints += c; } @@ -2597,6 +2763,8 @@ void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( result = getFactor(current, sizeHints[orientation][Qt::MinimumSize], sizeHints[orientation][Qt::PreferredSize], + sizeHints[orientation][Qt::PreferredSize], + sizeHints[orientation][Qt::PreferredSize], sizeHints[orientation][Qt::MaximumSize]); interpolationInterval[orientation] = result.first; @@ -2625,6 +2793,7 @@ void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, AnchorDat interpolationProgress[orientation]); qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred, + edge->sizeAtPreferred, edge->sizeAtPreferred, edge->sizeAtMaximum); Q_ASSERT(edge->from == base || edge->to == base); @@ -2652,34 +2821,46 @@ bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> for (iter = path.negatives.constBegin(); iter != path.negatives.constEnd(); ++iter) objective.variables.insert(*iter, -1.0); + const qreal objectiveOffset = (path.positives.count() - path.negatives.count()) * g_offset; simplex.setObjective(&objective); // Calculate minimum values - *min = simplex.solveMin(); + *min = simplex.solveMin() - objectiveOffset; // Save sizeAtMinimum results QList<AnchorData *> variables = getVariables(constraints); for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = static_cast<AnchorData *>(variables.at(i)); - ad->sizeAtMinimum = ad->result; - Q_ASSERT(ad->sizeAtMinimum >= ad->minSize || - qAbs(ad->sizeAtMinimum - ad->minSize) < 0.00000001); + ad->sizeAtMinimum = ad->result - g_offset; } // Calculate maximum values - *max = simplex.solveMax(); + *max = simplex.solveMax() - objectiveOffset; // Save sizeAtMaximum results for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = static_cast<AnchorData *>(variables.at(i)); - ad->sizeAtMaximum = ad->result; - // Q_ASSERT(ad->sizeAtMaximum <= ad->maxSize || - // qAbs(ad->sizeAtMaximum - ad->maxSize) < 0.00000001); + ad->sizeAtMaximum = ad->result - g_offset; } } return feasible; } +enum slackType { Grower = -1, Shrinker = 1 }; +static QPair<QSimplexVariable *, QSimplexConstraint *> createSlack(QSimplexConstraint *sizeConstraint, + qreal interval, slackType type) +{ + QSimplexVariable *slack = new QSimplexVariable; + sizeConstraint->variables.insert(slack, type); + + QSimplexConstraint *limit = new QSimplexConstraint; + limit->variables.insert(slack, 1.0); + limit->ratio = QSimplexConstraint::LessOrEqual; + limit->constant = interval; + + return qMakePair(slack, limit); +} + bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint *> &constraints, const QList<AnchorData *> &variables) { @@ -2690,7 +2871,8 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint // Fill the objective coefficients for this variable. In the // end the objective function will be // - // z = n * (A_shrink + B_shrink + ...) + (A_grower + B_grower + ...) + // z = n * (A_shrinker_hard + A_grower_hard + B_shrinker_hard + B_grower_hard + ...) + + // (A_shrinker_soft + A_grower_soft + B_shrinker_soft + B_grower_soft + ...) // // where n is the number of variables that have // slacks. Note that here we use the number of variables @@ -2702,7 +2884,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint // and we now fill the values for the slack constraints (one per variable), // which have this form (the constant A_pref was set when creating the slacks): // - // A + A_shrinker - A_grower = A_pref + // A + A_shrinker_hard + A_shrinker_soft - A_grower_hard - A_grower_soft = A_pref // for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = variables.at(i); @@ -2711,22 +2893,58 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint if (ad->isLayoutAnchor) continue; - QSimplexVariable *grower = new QSimplexVariable; - QSimplexVariable *shrinker = new QSimplexVariable; - QSimplexConstraint *c = new QSimplexConstraint; - c->variables.insert(ad, 1.0); - c->variables.insert(shrinker, 1.0); - c->variables.insert(grower, -1.0); - c->constant = ad->prefSize; + // By default, all variables are equal to their preferred size. If they have room to + // grow or shrink, such flexibility will be added by the additional variables below. + QSimplexConstraint *sizeConstraint = new QSimplexConstraint; + preferredConstraints += sizeConstraint; + sizeConstraint->variables.insert(ad, 1.0); + sizeConstraint->constant = ad->prefSize + g_offset; + + // Can easily shrink + QPair<QSimplexVariable *, QSimplexConstraint *> slack; + const qreal softShrinkInterval = ad->prefSize - ad->minPrefSize; + if (softShrinkInterval) { + slack = createSlack(sizeConstraint, softShrinkInterval, Shrinker); + preferredVariables += slack.first; + preferredConstraints += slack.second; + + // Add to objective with ratio == 1 (soft) + objective.variables.insert(slack.first, 1.0); + } - preferredConstraints += c; - preferredVariables += grower; - preferredVariables += shrinker; + // Can easily grow + const qreal softGrowInterval = ad->maxPrefSize - ad->prefSize; + if (softGrowInterval) { + slack = createSlack(sizeConstraint, softGrowInterval, Grower); + preferredVariables += slack.first; + preferredConstraints += slack.second; - objective.variables.insert(grower, 1.0); - objective.variables.insert(shrinker, variables.size()); - } + // Add to objective with ratio == 1 (soft) + objective.variables.insert(slack.first, 1.0); + } + + // Can shrink if really necessary + const qreal hardShrinkInterval = ad->minPrefSize - ad->minSize; + if (hardShrinkInterval) { + slack = createSlack(sizeConstraint, hardShrinkInterval, Shrinker); + preferredVariables += slack.first; + preferredConstraints += slack.second; + + // Add to objective with ratio == N (hard) + objective.variables.insert(slack.first, variables.size()); + } + // Can grow if really necessary + const qreal hardGrowInterval = ad->maxSize - ad->maxPrefSize; + if (hardGrowInterval) { + slack = createSlack(sizeConstraint, hardGrowInterval, Grower); + preferredVariables += slack.first; + preferredConstraints += slack.second; + + // Add to objective with ratio == N (hard) + objective.variables.insert(slack.first, variables.size()); + } + } QSimplex *simplex = new QSimplex; bool feasible = simplex->setConstraints(constraints + preferredConstraints); @@ -2739,7 +2957,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint // Save sizeAtPreferred results for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = variables.at(i); - ad->sizeAtPreferred = ad->result; + ad->sizeAtPreferred = ad->result - g_offset; } // Make sure we delete the simplex solver -before- we delete the diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 8529e2e..3be9d41 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -123,17 +123,17 @@ struct AnchorData : public QSimplexVariable { AnchorData() : QSimplexVariable(), from(0), to(0), minSize(0), prefSize(0), maxSize(0), + minPrefSize(0), maxPrefSize(0), sizeAtMinimum(0), sizeAtPreferred(0), sizeAtMaximum(0), item(0), graphicsAnchor(0), type(Normal), isLayoutAnchor(false), isCenterAnchor(false), orientation(0), dependency(Independent) {} + virtual ~AnchorData(); virtual void updateChildrenSizes() {} void refreshSizeHints(const QLayoutStyleInfo *styleInfo = 0); - virtual ~AnchorData() {} - #ifdef QT_DEBUG void dump(int indent = 2); inline QString toString() const; @@ -154,6 +154,9 @@ struct AnchorData : public QSimplexVariable { qreal prefSize; qreal maxSize; + qreal minPrefSize; + qreal maxPrefSize; + // Calculated sizes // These attributes define which sizes should that anchor be in when the // layout is at its minimum, preferred or maximum sizes. Values are @@ -213,7 +216,8 @@ struct ParallelAnchorData : public AnchorData Q_ASSERT(((first->from == second->from) && (first->to == second->to)) || ((first->from == second->to) && (first->to == second->from))); - // We arbitrarily choose the direction of the first child as "our" direction + // Our convention will be that the parallel group anchor will have the same + // direction as the first anchor. from = first->from; to = first->to; #ifdef QT_DEBUG @@ -224,6 +228,13 @@ struct ParallelAnchorData : public AnchorData virtual void updateChildrenSizes(); bool calculateSizeHints(); + bool secondForward() const { + // We have the convention that the first children will define the direction of the + // pararell group. Note that we can't rely on 'this->from' or 'this->to' because they + // might be changed by vertex simplification. + return firstEdge->from == secondEdge->from; + } + AnchorData* firstEdge; AnchorData* secondEdge; @@ -343,7 +354,6 @@ public: qreal preferredSize; uint hasSize : 1; // if false, get size from style. - uint reversed : 1; // if true, the anchor was inverted to keep its value positive }; @@ -365,8 +375,10 @@ public: // // Interval represents which interpolation interval are we operating in. enum Interval { - MinToPreferred = 0, - PreferredToMax + MinimumToMinPreferred = 0, + MinPreferredToPreferred, + PreferredToMaxPreferred, + MaxPreferredToMaximum }; // Several structures internal to the layout are duplicated to handle diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index bc47872..f93ffdf 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -1341,8 +1341,8 @@ QGraphicsItem::~QGraphicsItem() } if (!d_ptr->children.isEmpty()) { - QList<QGraphicsItem *> oldChildren = d_ptr->children; - qDeleteAll(oldChildren); + while (!d_ptr->children.isEmpty()) + delete d_ptr->children.first(); Q_ASSERT(d_ptr->children.isEmpty()); } diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 8777cdc..27ebb79 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -4233,7 +4233,6 @@ static void _q_paintIntoCache(QPixmap *pix, QGraphicsItem *item, const QRegion & if (!subPix.isNull()) { // Blit the subpixmap into the main pixmap. pixmapPainter.begin(pix); - pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source); pixmapPainter.setClipRegion(pixmapExposed); pixmapPainter.drawPixmap(br.topLeft(), subPix); pixmapPainter.end(); diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h index a5816d1..2004471 100644 --- a/src/gui/graphicsview/qsimplex_p.h +++ b/src/gui/graphicsview/qsimplex_p.h @@ -107,7 +107,7 @@ struct QSimplexConstraint Q_ASSERT(constant > 0 || qFuzzyCompare(1, 1 + constant)); - if ((leftHandSide == constant) || qAbs(leftHandSide - constant) < 0.00000001) + if ((leftHandSide == constant) || qAbs(leftHandSide - constant) < 0.0000001) return true; switch (ratio) { diff --git a/src/gui/widgets/qmenu.cpp b/src/gui/widgets/qmenu.cpp index 2e27226..ec9683d 100644 --- a/src/gui/widgets/qmenu.cpp +++ b/src/gui/widgets/qmenu.cpp @@ -65,6 +65,8 @@ #include "qmenubar_p.h" #include "qwidgetaction.h" #include "qtoolbutton.h" +#include "qpushbutton.h" +#include <private/qpushbutton_p.h> #include <private/qaction_p.h> #include <private/qsoftkeymanager_p.h> #ifdef QT3_SUPPORT @@ -417,12 +419,7 @@ void QMenuPrivate::hideUpToMenuBar() hideMenu(m, fadeMenus); if (!fadeMenus) // Mac doesn't clear the action until after hidden. m->d_func()->setCurrentAction(0); - } else { -#ifndef QT_NO_TOOLBUTTON - if (qobject_cast<QToolButton*>(caused) == 0) -#endif - qWarning("QMenu: Internal error"); - caused = 0; + } else { caused = 0; } } #if defined(Q_WS_MAC) @@ -1825,8 +1822,15 @@ void QMenu::popup(const QPoint &p, QAction *atAction) ensurePolished(); // Get the right font emit aboutToShow(); + const bool actionListChanged = d->itemsDirty; d->updateActionRects(); - QPoint pos = p; + QPoint pos; + QPushButton *causedButton = qobject_cast<QPushButton*>(d->causedPopup.widget); + if (actionListChanged && causedButton) + pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition(); + else + pos = p; + QSize size = sizeHint(); QRect screen; #ifndef QT_NO_GRAPHICSVIEW @@ -2302,22 +2306,9 @@ void QMenu::mouseReleaseEvent(QMouseEvent *e) if (action->menu()) action->menu()->d_func()->setFirstActionActive(); else { -#if defined(Q_WS_WIN) && !defined(QT_NO_MENUBAR) +#if defined(Q_WS_WIN) //On Windows only context menus can be activated with the right button - bool isContextMenu = true; - const QWidget *cause = d->causedPopup.widget; - while (cause) { - //if the popup was caused by either QMenuBar or a QToolButton, it is not a context menu - if (qobject_cast<const QMenuBar *>(cause) || qobject_cast<const QToolButton *>(cause)) { - isContextMenu = false; - break; - } else if (const QMenu *menu = qobject_cast<const QMenu *>(cause)) { - cause = menu->d_func()->causedPopup.widget; - } else { - break; - } - } - if (e->button() == Qt::LeftButton || isContextMenu) + if (e->button() == Qt::LeftButton || d->topCausedWidget() == 0) #endif d->activateAction(action, QAction::Trigger); } diff --git a/src/gui/widgets/qmenu_p.h b/src/gui/widgets/qmenu_p.h index 5757885..93017f5 100644 --- a/src/gui/widgets/qmenu_p.h +++ b/src/gui/widgets/qmenu_p.h @@ -182,6 +182,7 @@ public: } void init(); + static QMenuPrivate *get(QMenu *m) { return m->d_func(); } int scrollerHeight() const; //item calculations diff --git a/src/gui/widgets/qpushbutton.cpp b/src/gui/widgets/qpushbutton.cpp index eb34336..849bc43 100644 --- a/src/gui/widgets/qpushbutton.cpp +++ b/src/gui/widgets/qpushbutton.cpp @@ -63,6 +63,7 @@ #include "qaccessible.h" #endif +#include "private/qmenu_p.h" #include "private/qpushbutton_p.h" QT_BEGIN_NAMESPACE @@ -575,12 +576,33 @@ void QPushButtonPrivate::_q_popupPressed() return; menu->setNoReplayFor(q); + + QPoint menuPos = adjustedMenuPosition(); + + QPointer<QPushButton> guard(q); + QMenuPrivate::get(menu)->causedPopup.widget = guard; + + //Because of a delay in menu effects, we must keep track of the + //menu visibility to avoid flicker on button release + menuOpen = true; + menu->exec(menuPos); + if (guard) { + menuOpen = false; + q->setDown(false); + } +} + +QPoint QPushButtonPrivate::adjustedMenuPosition() +{ + Q_Q(QPushButton); + bool horizontal = true; #if !defined(QT_NO_TOOLBAR) QToolBar *tb = qobject_cast<QToolBar*>(parent); if (tb && tb->orientation() == Qt::Vertical) horizontal = false; #endif + QWidgetItem item(q); QRect rect = item.geometry(); rect.setRect(rect.x() - q->x(), rect.y() - q->y(), rect.width(), rect.height()); @@ -603,17 +625,10 @@ void QPushButtonPrivate::_q_popupPressed() else x -= menuSize.width(); } - QPointer<QPushButton> guard(q); - //Because of a delay in menu effects, we must keep track of the - //menu visibility to avoid flicker on button release - menuOpen = true; - menu->exec(QPoint(x, y)); - if (guard) { - menuOpen = false; - q->setDown(false); - } + return QPoint(x,y); } + #endif // QT_NO_MENU void QPushButtonPrivate::resetLayoutItemMargins() diff --git a/src/gui/widgets/qpushbutton_p.h b/src/gui/widgets/qpushbutton_p.h index f448027..2510e05 100644 --- a/src/gui/widgets/qpushbutton_p.h +++ b/src/gui/widgets/qpushbutton_p.h @@ -68,6 +68,10 @@ public: defaultButton(false), flat(false), menuOpen(false), lastAutoDefault(false) {} inline void init() { resetLayoutItemMargins(); } + static QPushButtonPrivate* get(QPushButton *b) { return b->d_func(); } +#ifndef QT_NO_MENU + QPoint adjustedMenuPosition(); +#endif void resetLayoutItemMargins(); void _q_popupPressed(); QDialog *dialogParent() const; diff --git a/src/gui/widgets/qtoolbar.cpp b/src/gui/widgets/qtoolbar.cpp index 58a3d28..c0ca015 100644 --- a/src/gui/widgets/qtoolbar.cpp +++ b/src/gui/widgets/qtoolbar.cpp @@ -396,10 +396,10 @@ bool QToolBarPrivate::mouseMoveEvent(QMouseEvent *event) void QToolBarPrivate::unplug(const QRect &_r) { Q_Q(QToolBar); - layout->setExpanded(false); QRect r = _r; r.moveTopLeft(q->mapToGlobal(QPoint(0, 0))); setWindowState(true, true, r); + layout->setExpanded(false); } void QToolBarPrivate::plug(const QRect &r) diff --git a/src/gui/widgets/qtoolbarlayout.cpp b/src/gui/widgets/qtoolbarlayout.cpp index 0afe5d8..93429e4 100644 --- a/src/gui/widgets/qtoolbarlayout.cpp +++ b/src/gui/widgets/qtoolbarlayout.cpp @@ -654,6 +654,7 @@ void QToolBarLayout::setExpanded(bool exp) if (!tb) return; if (QMainWindow *win = qobject_cast<QMainWindow*>(tb->parentWidget())) { + animating = !tb->isWindow() && win->isAnimated(); QMainWindowLayout *layout = qobject_cast<QMainWindowLayout*>(win->layout()); if (expanded) { tb->raise(); @@ -664,7 +665,7 @@ void QToolBarLayout::setExpanded(bool exp) layoutActions(rect.size()); } } - layout->layoutState.toolBarAreaLayout.apply(win->isAnimated()); + layout->layoutState.toolBarAreaLayout.apply(animating); } } diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 94b8aa5..1ff102f 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -127,18 +127,6 @@ Q_GLOBAL_STATIC(QGLDefaultOverlayFormat, defaultOverlayFormatInstance) QGLExtensions::Extensions QGLExtensions::glExtensions = 0; bool QGLExtensions::nvidiaFboNeedsFinish = false; -#ifndef APIENTRY -# define APIENTRY -#endif -typedef void (APIENTRY *pfn_glCompressedTexImage2DARB) (GLenum, GLint, GLenum, GLsizei, - GLsizei, GLint, GLsizei, const GLvoid *); -static pfn_glCompressedTexImage2DARB qt_glCompressedTexImage2DARB = 0; - - -#ifndef APIENTRY -#define APIENTRY -#endif - Q_GLOBAL_STATIC(QGLSignalProxy, theSignalProxy) QGLSignalProxy *QGLSignalProxy::instance() { @@ -185,12 +173,12 @@ public: #else // We can't do this in the constructor for this object because it // needs to be called *before* the QApplication constructor. - // Also check for the FragmentProgram extension in conjunction with + // Also check for the FragmentShader extension in conjunction with // the 2.0 version flag, to cover the case where we export the display // from an old GL 1.1 server to a GL 2.x client. In that case we can't // use GL 2.0. if ((QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0) - && (QGLExtensions::glExtensions & QGLExtensions::FragmentProgram) + && (QGLExtensions::glExtensions & QGLExtensions::FragmentShader) && qgetenv("QT_GL_USE_OPENGL1ENGINE").isEmpty()) engineType = QPaintEngine::OpenGL2; else @@ -1887,118 +1875,42 @@ void QGLContextPrivate::cleanup() { } -typedef QHash<QString, GLuint> QGLDDSCache; -Q_GLOBAL_STATIC(QGLDDSCache, qgl_dds_cache) - /*! \overload - Reads the DirectDrawSurface (DDS) compressed file \a fileName and - generates a 2D GL texture from it. + Reads the compressed texture file \a fileName and generates a 2D GL + texture from it. - Only the DXT1, DXT3 and DXT5 DDS formats are supported. + This function can load DirectDrawSurface (DDS) textures in the + DXT1, DXT3 and DXT5 DDS formats if the \c GL_ARB_texture_compression + and \c GL_EXT_texture_compression_s3tc extensions are supported. - Note that this will only work if the implementation supports the - \c GL_ARB_texture_compression and \c GL_EXT_texture_compression_s3tc - extensions. + Since 4.6.1, textures in the ETC1 format can be loaded if the + \c GL_OES_compressed_ETC1_RGB8_texture extension is supported + and the ETC1 texture has been encapsulated in the PVR container format. + Also, textures in the PVRTC2 and PVRTC4 formats can be loaded + if the \c GL_IMG_texture_compression_pvrtc extension is supported. \sa deleteTexture() */ GLuint QGLContext::bindTexture(const QString &fileName) { - if (!qt_glCompressedTexImage2DARB) { - qWarning("QGLContext::bindTexture(): The GL implementation does not support texture" - "compression extensions."); - return 0; - } - - QGLDDSCache::const_iterator it = qgl_dds_cache()->constFind(fileName); - if (it != qgl_dds_cache()->constEnd()) { + Q_D(QGLContext); + QGLDDSCache *dds_cache = &(d->group->m_dds_cache); + QGLDDSCache::const_iterator it = dds_cache->constFind(fileName); + if (it != dds_cache->constEnd()) { glBindTexture(GL_TEXTURE_2D, it.value()); return it.value(); } - QFile f(fileName); - f.open(QIODevice::ReadOnly); - - char tag[4]; - f.read(&tag[0], 4); - if (strncmp(tag,"DDS ", 4) != 0) { - qWarning("QGLContext::bindTexture(): not a DDS image file."); - return 0; - } - - DDSFormat ddsHeader; - f.read((char *) &ddsHeader, sizeof(DDSFormat)); - - if (!ddsHeader.dwLinearSize) { - qWarning("QGLContext::bindTexture() DDS image size is not valid."); - return 0; - } - - int factor = 4; - int bufferSize = 0; - int blockSize = 16; - GLenum format; - - switch(ddsHeader.ddsPixelFormat.dwFourCC) { - case FOURCC_DXT1: - format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - factor = 2; - blockSize = 8; - break; - case FOURCC_DXT3: - format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - break; - case FOURCC_DXT5: - format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - break; - default: - qWarning("QGLContext::bindTexture() DDS image format not supported."); + QGLTexture texture(this); + QSize size = texture.bindCompressedTexture(fileName); + if (!size.isValid()) return 0; - } - - if (ddsHeader.dwMipMapCount > 1) - bufferSize = ddsHeader.dwLinearSize * factor; - else - bufferSize = ddsHeader.dwLinearSize; - - GLubyte *pixels = (GLubyte *) malloc(bufferSize*sizeof(GLubyte)); - f.seek(ddsHeader.dwSize + 4); - f.read((char *) pixels, bufferSize); - f.close(); - - GLuint tx_id; - glGenTextures(1, &tx_id); - glBindTexture(GL_TEXTURE_2D, tx_id); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - int size; - int offset = 0; - int w = ddsHeader.dwWidth; - int h = ddsHeader.dwHeight; - // load mip-maps - for(int i = 0; i < (int) ddsHeader.dwMipMapCount; ++i) { - if (w == 0) w = 1; - if (h == 0) h = 1; - - size = ((w+3)/4) * ((h+3)/4) * blockSize; - qt_glCompressedTexImage2DARB(GL_TEXTURE_2D, i, format, w, h, 0, - size, pixels + offset); - offset += size; - - // half size for each mip-map level - w = w/2; - h = h/2; - } - - free(pixels); - - qgl_dds_cache()->insert(fileName, tx_id); - return tx_id; + dds_cache->insert(fileName, texture.id); + return texture.id; } static inline QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format) @@ -2593,17 +2505,20 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, Q */ void QGLContext::deleteTexture(GLuint id) { + Q_D(QGLContext); + if (QGLTextureCache::instance()->remove(this, id)) return; // check the DDS cache if the texture wasn't found in the pixmap/image // cache - QList<QString> ddsKeys = qgl_dds_cache()->keys(); + QGLDDSCache *dds_cache = &(d->group->m_dds_cache); + QList<QString> ddsKeys = dds_cache->keys(); for (int i = 0; i < ddsKeys.size(); ++i) { - GLuint texture = qgl_dds_cache()->value(ddsKeys.at(i)); + GLuint texture = dds_cache->value(ddsKeys.at(i)); if (id == texture) { glDeleteTextures(1, &texture); - qgl_dds_cache()->remove(ddsKeys.at(i)); + dds_cache->remove(ddsKeys.at(i)); return; } } @@ -4907,10 +4822,18 @@ void QGLExtensions::init_extensions() glExtensions |= SampleBuffers; if (extensions.contains("GL_SGIS_generate_mipmap")) glExtensions |= GenerateMipmap; - if (extensions.contains("GL_EXT_texture_compression_s3tc")) + if (extensions.contains("GL_ARB_texture_compression")) glExtensions |= TextureCompression; + if (extensions.contains("GL_EXT_texture_compression_s3tc")) + glExtensions |= DDSTextureCompression; + if (extensions.contains("GL_OES_compressed_ETC1_RGB8_texture")) + glExtensions |= ETC1TextureCompression; + if (extensions.contains("GL_IMG_texture_compression_pvrtc")) + glExtensions |= PVRTCTextureCompression; if (extensions.contains("GL_ARB_fragment_program")) glExtensions |= FragmentProgram; + if (extensions.contains("GL_ARB_fragment_shader")) + glExtensions |= FragmentShader; if (extensions.contains("GL_ARB_texture_mirrored_repeat")) glExtensions |= MirroredRepeat; if (extensions.contains("GL_EXT_framebuffer_object")) @@ -4928,6 +4851,7 @@ void QGLExtensions::init_extensions() #if defined(QT_OPENGL_ES_2) glExtensions |= FramebufferObject; glExtensions |= GenerateMipmap; + glExtensions |= FragmentShader; #endif #if defined(QT_OPENGL_ES_1) || defined(QT_OPENGL_ES_1_CL) if (extensions.contains("GL_OES_framebuffer_object")) @@ -4951,12 +4875,6 @@ void QGLExtensions::init_extensions() if (extensions.contains("GL_EXT_bgra")) glExtensions |= BGRATextureFormat; - - - QGLContext cx(QGLFormat::defaultFormat()); - if (glExtensions & TextureCompression) { - qt_glCompressedTexImage2DARB = (pfn_glCompressedTexImage2DARB) cx.getProcAddress(QLatin1String("glCompressedTexImage2DARB")); - } } /* @@ -5112,4 +5030,340 @@ void QGLSharedResourceGuard::setContext(const QGLContext *context) } } +QSize QGLTexture::bindCompressedTexture + (const QString& fileName, const char *format) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) + return QSize(); + QByteArray contents = file.readAll(); + file.close(); + return bindCompressedTexture + (contents.constData(), contents.size(), format); +} + +// PVR header format for container files that store textures compressed +// with the ETC1, PVRTC2, and PVRTC4 encodings. Format information from the +// PowerVR SDK at http://www.imgtec.com/powervr/insider/powervr-sdk.asp +// "PVRTexTool Reference Manual, version 1.11f". +struct PvrHeader +{ + quint32 headerSize; + quint32 height; + quint32 width; + quint32 mipMapCount; + quint32 flags; + quint32 dataSize; + quint32 bitsPerPixel; + quint32 redMask; + quint32 greenMask; + quint32 blueMask; + quint32 alphaMask; + quint32 magic; + quint32 surfaceCount; +}; + +#define PVR_MAGIC 0x21525650 // "PVR!" in little-endian + +#define PVR_FORMAT_MASK 0x000000FF +#define PVR_FORMAT_PVRTC2 0x00000018 +#define PVR_FORMAT_PVRTC4 0x00000019 +#define PVR_FORMAT_ETC1 0x00000036 + +#define PVR_HAS_MIPMAPS 0x00000100 +#define PVR_TWIDDLED 0x00000200 +#define PVR_NORMAL_MAP 0x00000400 +#define PVR_BORDER_ADDED 0x00000800 +#define PVR_CUBE_MAP 0x00001000 +#define PVR_FALSE_COLOR_MIPMAPS 0x00002000 +#define PVR_VOLUME_TEXTURE 0x00004000 +#define PVR_ALPHA_IN_TEXTURE 0x00008000 +#define PVR_VERTICAL_FLIP 0x00010000 + +#ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG +#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 +#endif + +#ifndef GL_ETC1_RGB8_OES +#define GL_ETC1_RGB8_OES 0x8D64 +#endif + +bool QGLTexture::canBindCompressedTexture + (const char *buf, int len, const char *format, bool *hasAlpha) +{ + if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) { + // Compressed texture loading only supported on little-endian + // systems such as x86 and ARM at the moment. + return false; + } + if (!format) { + // Auto-detect the format from the header. + if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) { + *hasAlpha = true; + return true; + } else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) { + const PvrHeader *pvrHeader = + reinterpret_cast<const PvrHeader *>(buf); + *hasAlpha = (pvrHeader->alphaMask != 0); + return true; + } + } else { + // Validate the format against the header. + if (!qstricmp(format, "DDS")) { + if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) { + *hasAlpha = true; + return true; + } + } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) { + if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) { + const PvrHeader *pvrHeader = + reinterpret_cast<const PvrHeader *>(buf); + *hasAlpha = (pvrHeader->alphaMask != 0); + return true; + } + } + } + return false; +} + +#define ctx QGLContext::currentContext() + +QSize QGLTexture::bindCompressedTexture + (const char *buf, int len, const char *format) +{ + if (QSysInfo::ByteOrder != QSysInfo::LittleEndian) { + // Compressed texture loading only supported on little-endian + // systems such as x86 and ARM at the moment. + return QSize(); + } +#if !defined(QT_OPENGL_ES) + if (!glCompressedTexImage2D) { + if (!(QGLExtensions::glExtensions & QGLExtensions::TextureCompression)) { + qWarning("QGLContext::bindTexture(): The GL implementation does " + "not support texture compression extensions."); + return QSize(); + } + glCompressedTexImage2D = (_glCompressedTexImage2DARB) ctx->getProcAddress(QLatin1String("glCompressedTexImage2DARB")); + if (!glCompressedTexImage2D) { + qWarning("QGLContext::bindTexture(): could not resolve " + "glCompressedTexImage2DARB."); + return QSize(); + } + } +#endif + if (!format) { + // Auto-detect the format from the header. + if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) + return bindCompressedTextureDDS(buf, len); + else if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) + return bindCompressedTexturePVR(buf, len); + } else { + // Validate the format against the header. + if (!qstricmp(format, "DDS")) { + if (len >= 4 && !qstrncmp(buf, "DDS ", 4)) + return bindCompressedTextureDDS(buf, len); + } else if (!qstricmp(format, "PVR") || !qstricmp(format, "ETC1")) { + if (len >= 52 && !qstrncmp(buf + 44, "PVR!", 4)) + return bindCompressedTexturePVR(buf, len); + } + } + return QSize(); +} + +QSize QGLTexture::bindCompressedTextureDDS(const char *buf, int len) +{ + // We only support 2D texture loading at present. + if (target != GL_TEXTURE_2D) + return QSize(); + + // Bail out if the necessary extension is not present. + if (!(QGLExtensions::glExtensions & QGLExtensions::DDSTextureCompression)) { + qWarning("QGLContext::bindTexture(): DDS texture compression is not supported."); + return QSize(); + } + + const DDSFormat *ddsHeader = reinterpret_cast<const DDSFormat *>(buf + 4); + if (!ddsHeader->dwLinearSize) { + qWarning("QGLContext::bindTexture(): DDS image size is not valid."); + return QSize(); + } + + int blockSize = 16; + GLenum format; + + switch(ddsHeader->ddsPixelFormat.dwFourCC) { + case FOURCC_DXT1: + format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + blockSize = 8; + break; + case FOURCC_DXT3: + format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + break; + case FOURCC_DXT5: + format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + default: + qWarning("QGLContext::bindTexture(): DDS image format not supported."); + return QSize(); + } + + const GLubyte *pixels = + reinterpret_cast<const GLubyte *>(buf + ddsHeader->dwSize + 4); + + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + int size; + int offset = 0; + int available = len - int(ddsHeader->dwSize + 4); + int w = ddsHeader->dwWidth; + int h = ddsHeader->dwHeight; + + // load mip-maps + for(int i = 0; i < (int) ddsHeader->dwMipMapCount; ++i) { + if (w == 0) w = 1; + if (h == 0) h = 1; + + size = ((w+3)/4) * ((h+3)/4) * blockSize; + if (size > available) + break; + glCompressedTexImage2D(GL_TEXTURE_2D, i, format, w, h, 0, + size, pixels + offset); + offset += size; + available -= size; + + // half size for each mip-map level + w = w/2; + h = h/2; + } + + // DDS images are not inverted. + options &= ~QGLContext::InvertedYBindOption; + + return QSize(ddsHeader->dwWidth, ddsHeader->dwHeight); +} + +QSize QGLTexture::bindCompressedTexturePVR(const char *buf, int len) +{ + // We only support 2D texture loading at present. Cube maps later. + if (target != GL_TEXTURE_2D) + return QSize(); + + // Determine which texture format we will be loading. + const PvrHeader *pvrHeader = reinterpret_cast<const PvrHeader *>(buf); + GLenum textureFormat; + quint32 minWidth, minHeight; + switch (pvrHeader->flags & PVR_FORMAT_MASK) { + case PVR_FORMAT_PVRTC2: + if (pvrHeader->alphaMask) + textureFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + else + textureFormat = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + minWidth = 16; + minHeight = 8; + break; + + case PVR_FORMAT_PVRTC4: + if (pvrHeader->alphaMask) + textureFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + else + textureFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + minWidth = 8; + minHeight = 8; + break; + + case PVR_FORMAT_ETC1: + textureFormat = GL_ETC1_RGB8_OES; + minWidth = 4; + minHeight = 4; + break; + + default: + qWarning("QGLContext::bindTexture(): PVR image format 0x%x not supported.", int(pvrHeader->flags & PVR_FORMAT_MASK)); + return QSize(); + } + + // Bail out if the necessary extension is not present. + if (textureFormat == GL_ETC1_RGB8_OES) { + if (!(QGLExtensions::glExtensions & + QGLExtensions::ETC1TextureCompression)) { + qWarning("QGLContext::bindTexture(): ETC1 texture compression is not supported."); + return QSize(); + } + } else { + if (!(QGLExtensions::glExtensions & + QGLExtensions::PVRTCTextureCompression)) { + qWarning("QGLContext::bindTexture(): PVRTC texture compression is not supported."); + return QSize(); + } + } + + // Boundary check on the buffer size. + quint32 bufferSize = pvrHeader->headerSize + pvrHeader->dataSize; + if (bufferSize > quint32(len)) { + qWarning("QGLContext::bindTexture(): PVR image size is not valid."); + return QSize(); + } + + // Create the texture. + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + if (pvrHeader->mipMapCount) { + if ((options & QGLContext::LinearFilteringBindOption) != 0) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } else { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + } + } else if ((options & QGLContext::LinearFilteringBindOption) != 0) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + // Load the compressed mipmap levels. + const GLubyte *buffer = + reinterpret_cast<const GLubyte *>(buf + pvrHeader->headerSize); + bufferSize = pvrHeader->dataSize; + quint32 level = 0; + quint32 width = pvrHeader->width; + quint32 height = pvrHeader->height; + while (bufferSize > 0 && level < pvrHeader->mipMapCount) { + quint32 size = + (qMax(width, minWidth) * qMax(height, minHeight) * + pvrHeader->bitsPerPixel) / 8; + if (size > bufferSize) + break; + glCompressedTexImage2D(GL_TEXTURE_2D, GLint(level), textureFormat, + GLsizei(width), GLsizei(height), 0, + GLsizei(size), buffer); + width /= 2; + height /= 2; + buffer += size; + ++level; + } + + // Restore the default pixel alignment for later texture uploads. + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + // Set the invert flag for the texture. + if ((pvrHeader->flags & PVR_VERTICAL_FLIP) != 0) + options |= QGLContext::InvertedYBindOption; + else + options &= ~QGLContext::InvertedYBindOption; + + return QSize(pvrHeader->width, pvrHeader->height); +} + +#undef ctx + QT_END_NAMESPACE diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index ab72c9c..179d69a 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -222,6 +222,8 @@ public: class QGLContextResource; class QGLSharedResourceGuard; +typedef QHash<QString, GLuint> QGLDDSCache; + // QGLContextPrivate has the responsibility of creating context groups. // QGLContextPrivate and QGLShareRegister will both maintain the reference counter and destroy // context groups when needed. @@ -246,6 +248,7 @@ private: QHash<QGLContextResource *, void *> m_resources; QGLSharedResourceGuard *m_guards; // double-linked list of active guards. QAtomicInt m_refs; + QGLDDSCache m_dds_cache; void cleanupResources(const QGLContext *ctx); @@ -377,7 +380,11 @@ public: PixelBufferObject = 0x00000800, FramebufferBlit = 0x00001000, NPOTTextures = 0x00002000, - BGRATextureFormat = 0x00004000 + BGRATextureFormat = 0x00004000, + DDSTextureCompression = 0x00008000, + ETC1TextureCompression = 0x00010000, + PVRTCTextureCompression = 0x00020000, + FragmentShader = 0x00040000 }; Q_DECLARE_FLAGS(Extensions, Extension) @@ -482,6 +489,14 @@ public: QPixmapData* boundPixmap; #endif + bool canBindCompressedTexture + (const char *buf, int len, const char *format, bool *hasAlpha); + QSize bindCompressedTexture + (const QString& fileName, const char *format = 0); + QSize bindCompressedTexture + (const char *buf, int len, const char *format = 0); + QSize bindCompressedTextureDDS(const char *buf, int len); + QSize bindCompressedTexturePVR(const char *buf, int len); }; class QGLTextureCache { @@ -518,7 +533,8 @@ bool qt_gl_preferGL2Engine(); inline GLenum qt_gl_preferredTextureFormat() { - return QSysInfo::ByteOrder == QSysInfo::BigEndian ? GL_RGBA : GL_BGRA; + return (QGLExtensions::glExtensions & QGLExtensions::BGRATextureFormat) && QSysInfo::ByteOrder == QSysInfo::LittleEndian + ? GL_BGRA : GL_RGBA; } inline GLenum qt_gl_preferredTextureTarget() diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index b51c239..a868e83 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -63,6 +63,7 @@ void qt_egl_add_platform_config(QEglProperties& props, QPaintDevice *device) props.setPixelFormat(static_cast<QImage *>(device)->format()); } +// Chooses the EGL config and creates the EGL context bool QGLContext::chooseContext(const QGLContext* shareContext) { Q_D(QGLContext); @@ -73,56 +74,74 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) int devType = device()->devType(); // Get the display and initialize it. - d->eglContext = new QEglContext(); - d->eglContext->setApi(QEgl::OpenGL); - if (!d->eglContext->openDisplay(device())) { - delete d->eglContext; - d->eglContext = 0; - return false; - } + if (d->eglContext == 0) { + d->eglContext = new QEglContext(); + d->eglContext->setApi(QEgl::OpenGL); + if (!d->eglContext->openDisplay(device())) { + delete d->eglContext; + d->eglContext = 0; + return false; + } - // Construct the configuration we need for this surface. - QEglProperties configProps; - qt_egl_set_format(configProps, devType, d->glFormat); - qt_egl_add_platform_config(configProps, device()); - configProps.setRenderableType(QEgl::OpenGL); - - QEgl::PixelFormatMatch matchType = QEgl::BestPixelFormat; - if (device()->depth() == 16) { - configProps.setValue(EGL_RED_SIZE, 5); - configProps.setValue(EGL_GREEN_SIZE, 6); - configProps.setValue(EGL_BLUE_SIZE, 5); - configProps.setValue(EGL_ALPHA_SIZE, 0); - matchType = QEgl::ExactPixelFormat; - } - configProps.setRenderableType(QEgl::OpenGL); + // Construct the configuration we need for this surface. + QEglProperties configProps; + qt_egl_set_format(configProps, devType, d->glFormat); + qt_egl_add_platform_config(configProps, device()); + configProps.setRenderableType(QEgl::OpenGL); + +#if We_have_an_EGL_library_which_bothers_to_check_EGL_BUFFER_SIZE + if (device()->depth() == 16 && configProps.value(EGL_ALPHA_SIZE) <= 0) { + qDebug("Setting EGL_BUFFER_SIZE to 16"); + configProps.setValue(EGL_BUFFER_SIZE, 16); + configProps.setValue(EGL_ALPHA_SIZE, 0); + } - // Search for a matching configuration, reducing the complexity - // each time until we get something that matches. - if (!d->eglContext->chooseConfig(configProps, matchType)) { - delete d->eglContext; - d->eglContext = 0; - return false; - } + if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) { + delete d->eglContext; + d->eglContext = 0; + return false; + } +#else + QEgl::PixelFormatMatch matchType = QEgl::BestPixelFormat; + if ((device()->depth() == 16) && configProps.value(EGL_ALPHA_SIZE) == 0) { + configProps.setValue(EGL_RED_SIZE, 5); + configProps.setValue(EGL_GREEN_SIZE, 6); + configProps.setValue(EGL_BLUE_SIZE, 5); + configProps.setValue(EGL_ALPHA_SIZE, 0); + matchType = QEgl::ExactPixelFormat; + } - // Inform the higher layers about the actual format properties. - qt_egl_update_format(*(d->eglContext), d->glFormat); + // Search for a matching configuration, reducing the complexity + // each time until we get something that matches. + if (!d->eglContext->chooseConfig(configProps, matchType)) { + delete d->eglContext; + d->eglContext = 0; + return false; + } +#endif - // Create a new context for the configuration. - if (!d->eglContext->createContext - (shareContext ? shareContext->d_func()->eglContext : 0)) { - delete d->eglContext; - d->eglContext = 0; - return false; - } - d->sharing = d->eglContext->isSharing(); - if (d->sharing && shareContext) - const_cast<QGLContext *>(shareContext)->d_func()->sharing = true; +// qDebug("QGLContext::chooseContext() - using EGL config %d:", d->eglContext->config()); +// qDebug() << QEglProperties(d->eglContext->config()).toString(); + + // Create a new context for the configuration. + if (!d->eglContext->createContext + (shareContext ? shareContext->d_func()->eglContext : 0)) { + delete d->eglContext; + d->eglContext = 0; + return false; + } + d->sharing = d->eglContext->isSharing(); + if (d->sharing && shareContext) + const_cast<QGLContext *>(shareContext)->d_func()->sharing = true; #if defined(EGL_VERSION_1_1) - if (d->glFormat.swapInterval() != -1 && devType == QInternal::Widget) - eglSwapInterval(d->eglContext->display(), d->glFormat.swapInterval()); + if (d->glFormat.swapInterval() != -1 && devType == QInternal::Widget) + eglSwapInterval(d->eglContext->display(), d->glFormat.swapInterval()); #endif + } + + // Inform the higher layers about the actual format properties. + qt_egl_update_format(*(d->eglContext), d->glFormat); return true; } @@ -160,6 +179,9 @@ bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig conf memset(&vi, 0, sizeof(XVisualInfo)); + EGLint eglConfigColorSize; + eglGetConfigAttrib(display, config, EGL_BUFFER_SIZE, &eglConfigColorSize); + // Check to see if EGL is suggesting an appropriate visual id: EGLint nativeVisualId; eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &nativeVisualId); @@ -189,8 +211,12 @@ bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig conf } else #endif { -// qDebug("Using opaque X Visual ID (%d) provided by EGL", (int)vi.visualid); - vi = *chosenVisualInfo; + if (eglConfigColorSize == chosenVisualInfo->depth) { +// qDebug("Using opaque X Visual ID (%d) provided by EGL", (int)vi.visualid); + vi = *chosenVisualInfo; + } else + qWarning("Warning: EGL suggested using X visual ID %d (%d bpp) for config %d (%d bpp), but the depths do not match!", + nativeVisualId, chosenVisualInfo->depth, (int)config, eglConfigColorSize); } XFree(chosenVisualInfo); } @@ -300,6 +326,8 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool createFailed = false; if (!d->glcx->isValid()) { + // Create the QGLContext here, which in turn chooses the EGL config + // and creates the EGL context: if (!d->glcx->create(shareContext ? shareContext : oldcx)) createFailed = true; } diff --git a/src/opengl/qglextensions_p.h b/src/opengl/qglextensions_p.h index 3510765..62e216c 100644 --- a/src/opengl/qglextensions_p.h +++ b/src/opengl/qglextensions_p.h @@ -184,6 +184,10 @@ typedef void (APIENTRY *_glBlitFramebufferEXT) (int srcX0, int srcY0, int srcX1, typedef void (APIENTRY *_glRenderbufferStorageMultisampleEXT) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +// ARB_texture_compression +typedef void (APIENTRY *_glCompressedTexImage2DARB) (GLenum, GLint, GLenum, GLsizei, + GLsizei, GLint, GLsizei, const GLvoid *); + QT_BEGIN_NAMESPACE struct QGLExtensionFuncs @@ -289,6 +293,11 @@ struct QGLExtensionFuncs #endif qt_glMapBufferARB = 0; qt_glUnmapBufferARB = 0; + +#if !defined(QT_OPENGL_ES) + // Texture compression + qt_glCompressedTexImage2DARB = 0; +#endif } @@ -397,6 +406,10 @@ struct QGLExtensionFuncs _glMapBufferARB qt_glMapBufferARB; _glUnmapBufferARB qt_glUnmapBufferARB; +#if !defined(QT_OPENGL_ES) + // Texture compression + _glCompressedTexImage2DARB qt_glCompressedTexImage2DARB; +#endif }; @@ -732,6 +745,10 @@ struct QGLExtensionFuncs #define glClearDepth glClearDepthf #endif +#if !defined(QT_OPENGL_ES) +#define glCompressedTexImage2D QGLContextPrivate::extensionFuncs(ctx).qt_glCompressedTexImage2DARB +#endif + extern bool qt_resolve_framebufferobject_extensions(QGLContext *ctx); bool qt_resolve_buffer_extensions(QGLContext *ctx); diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp index fb55097..0299cea 100644 --- a/src/opengl/qpixmapdata_gl.cpp +++ b/src/opengl/qpixmapdata_gl.cpp @@ -53,6 +53,8 @@ #include <private/qpaintengineex_opengl2_p.h> #include <qdesktopwidget.h> +#include <qfile.h> +#include <qimagereader.h> QT_BEGIN_NAMESPACE @@ -321,25 +323,47 @@ void QGLPixmapData::ensureCreated() const QGLShareContextScope ctx(qt_gl_share_widget()->context()); m_ctx = ctx; - const GLenum format = qt_gl_preferredTextureFormat(); + const GLenum internal_format = m_hasAlpha ? GL_RGBA : GL_RGB; +#ifdef QT_OPENGL_ES_2 + const GLenum external_format = internal_format; +#else + const GLenum external_format = qt_gl_preferredTextureFormat(); +#endif const GLenum target = GL_TEXTURE_2D; if (!m_texture.id) { glGenTextures(1, &m_texture.id); glBindTexture(target, m_texture.id); - GLenum format = m_hasAlpha ? GL_RGBA : GL_RGB; - glTexImage2D(target, 0, format, w, h, 0, - GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D(target, 0, internal_format, w, h, 0, external_format, GL_UNSIGNED_BYTE, 0); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } if (!m_source.isNull()) { - const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, format); + if (external_format == GL_RGB) { + QImage tx = m_source.convertToFormat(QImage::Format_RGB32); + + QVector<uchar> pixelData(w * h * 3); + uchar *p = &pixelData[0]; + QRgb *src = (QRgb *)tx.bits(); + + for (int i = 0; i < w * h; ++i) { + *p++ = qRed(*src); + *p++ = qGreen(*src); + *p++ = qBlue(*src); + ++src; + } - glBindTexture(target, m_texture.id); - glTexSubImage2D(target, 0, 0, 0, w, h, format, - GL_UNSIGNED_BYTE, tx.bits()); + glBindTexture(target, m_texture.id); + glTexSubImage2D(target, 0, 0, 0, w, h, external_format, + GL_UNSIGNED_BYTE, &pixelData[0]); + } else { + const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, external_format); + + glBindTexture(target, m_texture.id); + glTexSubImage2D(target, 0, 0, 0, w, h, external_format, + GL_UNSIGNED_BYTE, tx.bits()); + } if (useFramebufferObjects()) m_source = QImage(); @@ -385,6 +409,61 @@ void QGLPixmapData::fromImage(const QImage &image, } } +bool QGLPixmapData::fromFile(const QString &filename, const char *format, + Qt::ImageConversionFlags flags) +{ + if (pixelType() == QPixmapData::BitmapType) + return QPixmapData::fromFile(filename, format, flags); + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) + return false; + QByteArray data = file.peek(64); + bool alpha; + if (m_texture.canBindCompressedTexture + (data.constData(), data.size(), format, &alpha)) { + resize(0, 0); + data = file.readAll(); + file.close(); + QSize size = m_texture.bindCompressedTexture + (data.constData(), data.size(), format); + if (!size.isEmpty()) { + w = size.width(); + h = size.height(); + is_null = false; + d = 32; + m_hasAlpha = alpha; + m_source = QImage(); + m_dirty = isValid(); + return true; + } + return false; + } + fromImage(QImageReader(&file, format).read(), flags); + return !isNull(); +} + +bool QGLPixmapData::fromData(const uchar *buffer, uint len, const char *format, + Qt::ImageConversionFlags flags) +{ + bool alpha; + const char *buf = reinterpret_cast<const char *>(buffer); + if (m_texture.canBindCompressedTexture(buf, int(len), format, &alpha)) { + resize(0, 0); + QSize size = m_texture.bindCompressedTexture(buf, int(len), format); + if (!size.isEmpty()) { + w = size.width(); + h = size.height(); + is_null = false; + d = 32; + m_hasAlpha = alpha; + m_source = QImage(); + m_dirty = isValid(); + return true; + } + } + return QPixmapData::fromData(buffer, len, format, flags); +} + bool QGLPixmapData::scroll(int dx, int dy, const QRect &rect) { Q_UNUSED(dx); diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h index 8a13e03..007c52a 100644 --- a/src/opengl/qpixmapdata_gl_p.h +++ b/src/opengl/qpixmapdata_gl_p.h @@ -106,6 +106,10 @@ public: // Re-implemented from QPixmapData: void resize(int width, int height); void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + bool fromFile(const QString &filename, const char *format, + Qt::ImageConversionFlags flags); + bool fromData(const uchar *buffer, uint len, const char *format, + Qt::ImageConversionFlags flags); void copy(const QPixmapData *data, const QRect &rect); bool scroll(int dx, int dy, const QRect &rect); void fill(const QColor &color); diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp index e353f5d..7194f9d 100644 --- a/src/opengl/qwindowsurface_gl.cpp +++ b/src/opengl/qwindowsurface_gl.cpp @@ -493,7 +493,6 @@ void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint & } #endif d_ptr->paintedRegion = QRegion(); - context()->swapBuffers(); } else { glFlush(); @@ -688,11 +687,13 @@ void QGLWindowSurface::updateGeometry() { d_ptr->size = rect.size(); if (d_ptr->ctx) { +#ifndef QT_OPENGL_ES_2 if (d_ptr->destructive_swap_buffers) { glBindTexture(target, d_ptr->tex_id); glTexImage2D(target, 0, GL_RGBA, rect.width(), rect.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, 0); glBindTexture(target, 0); } +#endif return; } @@ -756,11 +757,7 @@ void QGLWindowSurface::updateGeometry() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); -#ifndef QT_OPENGL_ES glOrtho(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999); -#else - glOrthof(0, d_ptr->pb->width(), d_ptr->pb->height(), 0, -999999, 999999); -#endif d_ptr->pb->d_ptr->qctx->d_func()->internal_context = true; return; @@ -774,6 +771,7 @@ void QGLWindowSurface::updateGeometry() { ctx->makeCurrent(); +#ifndef QT_OPENGL_ES_2 if (d_ptr->destructive_swap_buffers) { glGenTextures(1, &d_ptr->tex_id); glBindTexture(target, d_ptr->tex_id); @@ -783,6 +781,7 @@ void QGLWindowSurface::updateGeometry() { glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(target, 0); } +#endif qDebug() << "QGLWindowSurface: Using plain widget as window surface" << this;; d_ptr->ctx = ctx; diff --git a/src/openvg/qpixmapdata_vg.cpp b/src/openvg/qpixmapdata_vg.cpp index 19c90ed..5d5fcbf 100644 --- a/src/openvg/qpixmapdata_vg.cpp +++ b/src/openvg/qpixmapdata_vg.cpp @@ -65,12 +65,21 @@ QVGPixmapData::QVGPixmapData(PixelType type) recreate = true; #if !defined(QT_NO_EGL) context = 0; + qt_vg_register_pixmap(this); #endif setSerialNumber(++qt_vg_pixmap_serial); } QVGPixmapData::~QVGPixmapData() { + destroyImageAndContext(); +#if !defined(QT_NO_EGL) + qt_vg_unregister_pixmap(this); +#endif +} + +void QVGPixmapData::destroyImageAndContext() +{ if (vgImage != VG_INVALID_HANDLE) { // We need to have a context current to destroy the image. #if !defined(QT_NO_EGL) @@ -93,11 +102,16 @@ QVGPixmapData::~QVGPixmapData() if (vgImageOpacity != VG_INVALID_HANDLE) vgDestroyImage(vgImageOpacity); #endif + vgImage = VG_INVALID_HANDLE; + vgImageOpacity = VG_INVALID_HANDLE; } #if !defined(QT_NO_EGL) - if (context) - qt_vg_destroy_context(context); + if (context) { + qt_vg_destroy_context(context, QInternal::Pixmap); + context = 0; + } #endif + recreate = true; } QPixmapData *QVGPixmapData::createCompatiblePixmapData() const @@ -217,7 +231,7 @@ VGImage QVGPixmapData::toVGImage() #if !defined(QT_NO_EGL) // Increase the reference count on the shared context. if (!context) - context = qt_vg_create_context(0); + context = qt_vg_create_context(0, QInternal::Pixmap); #endif if (recreate && prevSize != QSize(w, h)) { @@ -286,6 +300,17 @@ VGImage QVGPixmapData::toVGImage(qreal opacity) #endif } +void QVGPixmapData::hibernate() +{ + // If the image was imported (e.g, from an SgImage under Symbian), + // then we cannot copy it back to main memory for storage. + if (vgImage != VG_INVALID_HANDLE && source.isNull()) + return; + + forceToImage(); + destroyImageAndContext(); +} + extern int qt_defaultDpiX(); extern int qt_defaultDpiY(); @@ -376,7 +401,7 @@ void QVGPixmapData::fromNativeType(void* pixmap, NativeType type) // when "0" used as argument then // default display, context are used if (!context) - context = qt_vg_create_context(0); + context = qt_vg_create_context(0, QInternal::Pixmap); if (vgImage != VG_INVALID_HANDLE) { vgDestroyImage(vgImage); diff --git a/src/openvg/qpixmapdata_vg_p.h b/src/openvg/qpixmapdata_vg_p.h index fe19f35..c0bb098 100644 --- a/src/openvg/qpixmapdata_vg_p.h +++ b/src/openvg/qpixmapdata_vg_p.h @@ -55,8 +55,6 @@ #include <QtGui/private/qpixmap_raster_p.h> #include <private/qvg_p.h> -#if !defined(QT_NO_EGL) -#endif #if defined(Q_OS_SYMBIAN) class RSGImage; @@ -66,6 +64,15 @@ QT_BEGIN_NAMESPACE class QEglContext; +#if !defined(QT_NO_EGL) +class QVGPixmapData; +class QVGSharedContext; + +void qt_vg_register_pixmap(QVGPixmapData *pd); +void qt_vg_unregister_pixmap(QVGPixmapData *pd); +void qt_vg_hibernate_pixmaps(QVGSharedContext *context); +#endif + class Q_OPENVG_EXPORT QVGPixmapData : public QPixmapData { public: @@ -94,6 +101,14 @@ public: // Return the VGImage form for a specific opacity setting. virtual VGImage toVGImage(qreal opacity); + // Release the VG resources associated with this pixmap and copy + // the pixmap's contents out of the GPU back into main memory. + // The VG resource will be automatically recreated the next time + // toVGImage() is called. Does nothing if the pixmap cannot be + // hibernated for some reason (e.g. VGImage is shared with another + // process via a SgImage). + virtual void hibernate(); + QSize size() const { return QSize(w, h); } #if defined(Q_OS_SYMBIAN) @@ -108,6 +123,16 @@ protected: void cleanup(); #endif +#if !defined(QT_NO_EGL) +private: + QVGPixmapData *next; + QVGPixmapData *prev; + + friend void qt_vg_register_pixmap(QVGPixmapData *pd); + friend void qt_vg_unregister_pixmap(QVGPixmapData *pd); + friend void qt_vg_hibernate_pixmaps(QVGSharedContext *context); +#endif + protected: QSize prevSize; VGImage vgImage; @@ -121,6 +146,8 @@ protected: void forceToImage(); QImage::Format sourceFormat() const; + + void destroyImageAndContext(); }; QT_END_NAMESPACE diff --git a/src/openvg/qvg_p.h b/src/openvg/qvg_p.h index 04e2bab..3577013 100644 --- a/src/openvg/qvg_p.h +++ b/src/openvg/qvg_p.h @@ -71,11 +71,17 @@ class QEglContext; // Create an EGL context, but don't bind it to a surface. If single-context // mode is enabled, this will return the previously-created context. -Q_OPENVG_EXPORT QEglContext *qt_vg_create_context(QPaintDevice *device); +// "devType" indicates the type of device using the context, usually +// QInternal::Widget or QInternal::Pixmap. +Q_OPENVG_EXPORT QEglContext *qt_vg_create_context + (QPaintDevice *device, int devType); // Destroy an EGL context that was created by qt_vg_create_context(). // If single-context mode is enabled, this will decrease the reference count. -Q_OPENVG_EXPORT void qt_vg_destroy_context(QEglContext *context); +// "devType" indicates the type of device destroying the context, usually +// QInternal::Widget or QInternal::Pixmap. +Q_OPENVG_EXPORT void qt_vg_destroy_context + (QEglContext *context, int devType); // Return the shared pbuffer surface that can be made current to // destroy VGImage objects when there is no other surface available. diff --git a/src/openvg/qwindowsurface_vgegl.cpp b/src/openvg/qwindowsurface_vgegl.cpp index 62871cf..1571083 100644 --- a/src/openvg/qwindowsurface_vgegl.cpp +++ b/src/openvg/qwindowsurface_vgegl.cpp @@ -111,15 +111,19 @@ public: QEglContext *context; int refCount; + int widgetRefCount; QVGPaintEngine *engine; EGLSurface surface; + QVGPixmapData *firstPixmap; }; QVGSharedContext::QVGSharedContext() : context(0) , refCount(0) + , widgetRefCount(0) , engine(0) , surface(EGL_NO_SURFACE) + , firstPixmap(0) { } @@ -154,6 +158,28 @@ void qt_vg_destroy_paint_engine(QVGPaintEngine *engine) Q_UNUSED(engine); } +void qt_vg_register_pixmap(QVGPixmapData *pd) +{ + QVGSharedContext *shared = sharedContext(); + pd->next = shared->firstPixmap; + pd->prev = 0; + if (shared->firstPixmap) + shared->firstPixmap->prev = pd; + shared->firstPixmap = pd; +} + +void qt_vg_unregister_pixmap(QVGPixmapData *pd) +{ + if (pd->next) + pd->next->prev = pd->prev; + if (pd->prev) { + pd->prev->next = pd->next; + } else { + QVGSharedContext *shared = sharedContext(); + shared->firstPixmap = pd->next; + } +} + #else QVGPaintEngine *qt_vg_create_paint_engine(void) @@ -166,6 +192,16 @@ void qt_vg_destroy_paint_engine(QVGPaintEngine *engine) delete engine; } +void qt_vg_register_pixmap(QVGPixmapData *pd) +{ + Q_UNUSED(pd); +} + +void qt_vg_unregister_pixmap(QVGPixmapData *pd) +{ + Q_UNUSED(pd); +} + #endif #ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT @@ -278,9 +314,11 @@ static QEglContext *createContext(QPaintDevice *device) #if !defined(QVG_NO_SINGLE_CONTEXT) -QEglContext *qt_vg_create_context(QPaintDevice *device) +QEglContext *qt_vg_create_context(QPaintDevice *device, int devType) { QVGSharedContext *shared = sharedContext(); + if (devType == QInternal::Widget) + ++(shared->widgetRefCount); if (shared->context) { ++(shared->refCount); return shared->context; @@ -291,23 +329,65 @@ QEglContext *qt_vg_create_context(QPaintDevice *device) } } -void qt_vg_destroy_context(QEglContext *context) +static void qt_vg_destroy_shared_context(QVGSharedContext *shared) +{ + shared->context->makeCurrent(qt_vg_shared_surface()); + delete shared->engine; + shared->engine = 0; + shared->context->doneCurrent(); + if (shared->surface != EGL_NO_SURFACE) { + eglDestroySurface(shared->context->display(), shared->surface); + shared->surface = EGL_NO_SURFACE; + } + delete shared->context; + shared->context = 0; +} + +void qt_vg_hibernate_pixmaps(QVGSharedContext *shared) +{ + // Artificially increase the reference count to prevent the + // context from being destroyed until after we have finished + // the hibernation process. + ++(shared->refCount); + + // We need a context current to hibernate the VGImage objects. + shared->context->makeCurrent(qt_vg_shared_surface()); + + // Scan all QVGPixmapData objects in the system and hibernate them. + QVGPixmapData *pd = shared->firstPixmap; + while (pd != 0) { + pd->hibernate(); + pd = pd->next; + } + + // Don't need the current context any more. + shared->context->lazyDoneCurrent(); + + // Decrease the reference count and destroy the context if necessary. + if (--(shared->refCount) <= 0) + qt_vg_destroy_shared_context(shared); +} + +void qt_vg_destroy_context(QEglContext *context, int devType) { QVGSharedContext *shared = sharedContext(); if (shared->context != context) { // This is not the shared context. Shouldn't happen! delete context; - } else if (--(shared->refCount) <= 0) { - shared->context->makeCurrent(qt_vg_shared_surface()); - delete shared->engine; - shared->engine = 0; - shared->context->doneCurrent(); - if (shared->surface != EGL_NO_SURFACE) { - eglDestroySurface(shared->context->display(), shared->surface); - shared->surface = EGL_NO_SURFACE; - } - delete shared->context; - shared->context = 0; + return; + } + if (devType == QInternal::Widget) + --(shared->widgetRefCount); + if (--(shared->refCount) <= 0) { + qt_vg_destroy_shared_context(shared); + } else if (shared->widgetRefCount <= 0 && devType == QInternal::Widget) { + // All of the widget window surfaces have been destroyed + // but we still have VG pixmaps active. Ask them to hibernate + // to free up GPU resources until a widget is shown again. + // This may eventually cause the EGLContext to be destroyed + // because nothing in the system needs a context, which will + // free up even more GPU resources. + qt_vg_hibernate_pixmaps(shared); } } @@ -338,13 +418,15 @@ EGLSurface qt_vg_shared_surface(void) #else -QEglContext *qt_vg_create_context(QPaintDevice *device) +QEglContext *qt_vg_create_context(QPaintDevice *device, int devType) { + Q_UNUSED(devType); return createContext(device); } -void qt_vg_destroy_context(QEglContext *context) +void qt_vg_destroy_context(QEglContext *context, int devType) { + Q_UNUSED(devType); delete context; } @@ -434,7 +516,7 @@ QVGEGLWindowSurfaceVGImage::~QVGEGLWindowSurfaceVGImage() } if (windowSurface != EGL_NO_SURFACE) context->destroySurface(windowSurface); - qt_vg_destroy_context(context); + qt_vg_destroy_context(context, QInternal::Widget); } } @@ -453,7 +535,7 @@ QEglContext *QVGEGLWindowSurfaceVGImage::ensureContext(QWidget *widget) if (!context) { // Create a new EGL context. We create the surface in beginPaint(). size = newSize; - context = qt_vg_create_context(widget); + context = qt_vg_create_context(widget, QInternal::Widget); if (!context) return 0; isPaintingActive = false; @@ -548,7 +630,7 @@ QVGEGLWindowSurfaceDirect::~QVGEGLWindowSurfaceDirect() if (context) { if (windowSurface != EGL_NO_SURFACE) context->destroySurface(windowSurface); - qt_vg_destroy_context(context); + qt_vg_destroy_context(context, QInternal::Widget); } } @@ -587,7 +669,7 @@ QEglContext *QVGEGLWindowSurfaceDirect::ensureContext(QWidget *widget) qt_vg_destroy_paint_engine(engine); engine = 0; context->destroySurface(windowSurface); - qt_vg_destroy_context(context); + qt_vg_destroy_context(context, QInternal::Widget); context = 0; windowSurface = EGL_NO_SURFACE; } @@ -596,7 +678,7 @@ QEglContext *QVGEGLWindowSurfaceDirect::ensureContext(QWidget *widget) if (!context) { // Create a new EGL context and bind it to the widget surface. size = newSize; - context = qt_vg_create_context(widget); + context = qt_vg_create_context(widget, QInternal::Widget); if (!context) return 0; // We want a direct to window rendering surface if possible. @@ -613,7 +695,7 @@ QEglContext *QVGEGLWindowSurfaceDirect::ensureContext(QWidget *widget) #endif EGLSurface surface = context->createSurface(widget, &surfaceProps); if (surface == EGL_NO_SURFACE) { - qt_vg_destroy_context(context); + qt_vg_destroy_context(context, QInternal::Widget); context = 0; return 0; } diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index aa67ac5..6e78522 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -89,6 +89,7 @@ private slots: void snakeParallelWithLayout(); void parallelToHalfLayout(); void globalSpacing(); + void graphicsAnchorHandling(); }; class RectWidget : public QGraphicsWidget @@ -1196,7 +1197,7 @@ void tst_QGraphicsAnchorLayout::styleDefaults() QSizeF pref(20, 20); QSizeF max (50, 50); - /* + /* create this layout, where a,b have controlType QSizePolicy::RadioButton c,d have controlType QSizePolicy::PushButton: +-------+ @@ -1243,9 +1244,9 @@ void tst_QGraphicsAnchorLayout::styleDefaults() scene.addItem(window); window->show(); - QGraphicsView *view = new QGraphicsView(&scene); - view->resize(200, 200); - view->show(); + QGraphicsView view(&scene); + view.resize(200, 200); + view.show(); window->adjustSize(); QCOMPARE(a->geometry(), QRectF(0, 3, 20, 20)); //radio @@ -1264,10 +1265,13 @@ void tst_QGraphicsAnchorLayout::styleDefaults() window->setStyle(style); window->adjustSize(); QCOMPARE(a->geometry(), QRectF(0, 3, 20, 20)); - QCOMPARE(b->geometry(), QRectF(21, 25, 20, 20)); + QCOMPARE(b->geometry(), QRectF(21, 25, 20, 20)); QCOMPARE(c->geometry(), QRectF(42, 47, 20, 20)); QCOMPARE(d->geometry(), QRectF(63, 69, 20, 20)); QCOMPARE(l->geometry(), QRectF(0, 0, 89, 98)); + + window->setStyle(0); + delete style; } @@ -1776,7 +1780,8 @@ void tst_QGraphicsAnchorLayout::simplificationVsOrder() QGraphicsWidget *b = createItem(min, pref, max, "B"); QGraphicsWidget *c = createItem(min, pref, max, "C"); - QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + QGraphicsWidget frame; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&frame); // Bulk anchors l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); @@ -1801,7 +1806,6 @@ void tst_QGraphicsAnchorLayout::simplificationVsOrder() l->effectiveSizeHint(Qt::MinimumSize); if (hasSimplification) { - QEXPECT_FAIL("", "Sequential anchors cannot handle children of opposite directions", Continue); QCOMPARE(usedSimplex(l, Qt::Horizontal), false); QCOMPARE(usedSimplex(l, Qt::Vertical), false); } @@ -1846,7 +1850,8 @@ void tst_QGraphicsAnchorLayout::simplificationVsRedundance() QGraphicsWidget *b = createItem(min, pref, max, "B"); QGraphicsWidget *c = createItem(min, pref, max, "C"); - QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + QGraphicsWidget frame; + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(&frame); l->addCornerAnchors(a, Qt::TopLeftCorner, l, Qt::TopLeftCorner); l->addCornerAnchors(a, Qt::BottomLeftCorner, l, Qt::BottomLeftCorner); @@ -2016,6 +2021,42 @@ void tst_QGraphicsAnchorLayout::globalSpacing() QCOMPARE(newHSpacing, hSpacing); } +void tst_QGraphicsAnchorLayout::graphicsAnchorHandling() +{ + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout(); + QGraphicsWidget *a = createItem(); + + l->addAnchors(l, a); + + QGraphicsAnchor *layoutAnchor = l->anchor(l, Qt::AnchorTop, l, Qt::AnchorBottom); + QGraphicsAnchor *itemAnchor = l->anchor(a, Qt::AnchorTop, a, Qt::AnchorBottom); + QGraphicsAnchor *invalidAnchor = l->anchor(a, Qt::AnchorTop, l, Qt::AnchorBottom); + + // Ensure none of these anchors are accessible. + QVERIFY(layoutAnchor == 0); + QVERIFY(itemAnchor == 0); + QVERIFY(invalidAnchor == 0); + + // Hook the anchors to a QObject + QObject object; + QGraphicsAnchor *userAnchor = l->anchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + userAnchor->setParent(&object); + userAnchor = l->anchor(l, Qt::AnchorBottom, a, Qt::AnchorBottom); + userAnchor->setParent(&object); + userAnchor = l->anchor(l, Qt::AnchorRight, a, Qt::AnchorRight); + userAnchor->setParent(&object); + userAnchor = l->anchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + userAnchor->setParent(&object); + + QCOMPARE(object.children().size(), 4); + + // Delete layout, this will cause all anchors to be deleted internally. + // We expect the public QGraphicsAnchor instances to be deleted too. + delete l; + QCOMPARE(object.children().size(), 0); + + delete a; +} QTEST_MAIN(tst_QGraphicsAnchorLayout) #include "tst_qgraphicsanchorlayout.moc" diff --git a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp index baa1ba1..57dc90d 100644 --- a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp +++ b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp @@ -356,6 +356,8 @@ void tst_QGraphicsAnchorLayout1::testItemAt() QVERIFY( layout->itemAt(0) == widget2 ); + delete widget1; + widget->setLayout(layout); delete widget; } @@ -460,6 +462,12 @@ void tst_QGraphicsAnchorLayout1::testAddAndRemoveAnchor() QCOMPARE( layout->count(), 0 ); + delete widget1; + delete widget2; + delete widget3; + delete widget4; + delete widget5; + widget->setLayout(layout); delete widget; } @@ -1740,9 +1748,9 @@ void tst_QGraphicsAnchorLayout1::testBasicLayout() QRectF actual = truncate(widgets[item.index]->geometry()); QCOMPARE(actual, expected); - delete widgets[item.index]; } + qDeleteAll(widgets); delete widget; } @@ -2231,8 +2239,9 @@ void tst_QGraphicsAnchorLayout1::testRemoveCenterAnchor() const BasicLayoutTestResult item = result[i]; QCOMPARE(widgets[item.index]->geometry(), item.rect); - delete widgets[item.index]; } + + qDeleteAll(widgets); delete widget; } @@ -2360,7 +2369,7 @@ void tst_QGraphicsAnchorLayout1::testSingleSizePolicy() QFETCH(bool, valid); // create objects - QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsWidget widget; TheAnchorLayout *layout = new TheAnchorLayout; TestWidget *childWidget = new TestWidget; @@ -2370,11 +2379,11 @@ void tst_QGraphicsAnchorLayout1::testSingleSizePolicy() layout->setAnchor( layout, Qt::AnchorTop, childWidget, Qt::AnchorTop, 10 ); layout->setAnchor( childWidget, Qt::AnchorBottom, layout, Qt::AnchorBottom, 10 ); - widget->setLayout( layout ); + widget.setLayout( layout ); // set test case specific: policy and size childWidget->setSizePolicy( policy ); - widget->setGeometry( QRectF( QPoint(0,0), size ) ); + widget.setGeometry( QRectF( QPoint(0,0), size ) ); QCOMPARE( layout->isValid() , valid ); @@ -2516,7 +2525,7 @@ void tst_QGraphicsAnchorLayout1::testDoubleSizePolicy() QFETCH(qreal, width2); // create objects - QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsWidget widget; TheAnchorLayout *layout = new TheAnchorLayout; TestWidget *childWidget1 = new TestWidget; TestWidget *childWidget2 = new TestWidget; @@ -2526,13 +2535,13 @@ void tst_QGraphicsAnchorLayout1::testDoubleSizePolicy() layout->setAnchor( childWidget1, Qt::AnchorRight, childWidget2, Qt::AnchorLeft, 10 ); layout->setAnchor( childWidget2, Qt::AnchorRight, layout, Qt::AnchorRight, 10 ); - widget->setLayout( layout ); + widget.setLayout( layout ); // set test case specific: policy childWidget1->setSizePolicy( policy1 ); childWidget2->setSizePolicy( policy2 ); - widget->setGeometry( QRectF( QPoint(0,0), QSize( 100,100 ) ) ); + widget.setGeometry( QRectF( QPoint(0,0), QSize( 100,100 ) ) ); // check results: if ( width1 == -1.0f ) { @@ -2649,7 +2658,7 @@ void tst_QGraphicsAnchorLayout1::testSizeDistribution() QFETCH(qreal, width2); // create objects - QGraphicsWidget *widget = new QGraphicsWidget; + QGraphicsWidget widget; TheAnchorLayout *layout = new TheAnchorLayout; TestWidget *childWidget1 = new TestWidget; TestWidget *childWidget2 = new TestWidget; @@ -2659,7 +2668,7 @@ void tst_QGraphicsAnchorLayout1::testSizeDistribution() layout->setAnchor( childWidget1, Qt::AnchorRight, childWidget2, Qt::AnchorLeft, 10 ); layout->setAnchor( childWidget2, Qt::AnchorRight, layout, Qt::AnchorRight, 10 ); - widget->setLayout( layout ); + widget.setLayout( layout ); // set test case specific: size hints childWidget1->setMinimumWidth( sizeHints1.value( Qt::MinimumSize ) ); @@ -2670,7 +2679,7 @@ void tst_QGraphicsAnchorLayout1::testSizeDistribution() childWidget2->setPreferredWidth( sizeHints2.value( Qt::PreferredSize ) ); childWidget2->setMaximumWidth( sizeHints2.value( Qt::MaximumSize ) ); - widget->setGeometry( QRectF( QPoint(0,0), QSize( 100,100 ) ) ); + widget.setGeometry( QRectF( QPoint(0,0), QSize( 100,100 ) ) ); // check results: if ( width1 == -1.0f ) { diff --git a/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp index 38abc3d..c5e57f7 100644 --- a/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp +++ b/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp @@ -280,6 +280,7 @@ private slots: void task160653_selectionChanged(); void task250680_childClip(); void taskQTBUG_5904_crashWithDeviceCoordinateCache(); + void taskQT657_paintIntoCacheWithTransparentParts(); }; void tst_QGraphicsScene::initTestCase() @@ -1462,6 +1463,7 @@ void tst_QGraphicsScene::focusItemLostFocus() class ClearTestItem : public QGraphicsRectItem { public: + ClearTestItem(QGraphicsItem *parent = 0) : QGraphicsRectItem(parent) {} ~ClearTestItem() { qDeleteAll(items); } QList<QGraphicsItem *> items; }; @@ -1486,6 +1488,11 @@ void tst_QGraphicsScene::clear() scene.addItem(secondItem); QCOMPARE(scene.items().at(0), firstItem); QCOMPARE(scene.items().at(1), secondItem); + + ClearTestItem *thirdItem = new ClearTestItem(firstItem); + QGraphicsItem *forthItem = new QGraphicsRectItem(firstItem); + thirdItem->items += forthItem; + // must not crash even if firstItem deletes secondItem scene.clear(); QVERIFY(scene.items().isEmpty()); @@ -4264,5 +4271,43 @@ void tst_QGraphicsScene::taskQTBUG_5904_crashWithDeviceCoordinateCache() // No crash, then it passed! } +void tst_QGraphicsScene::taskQT657_paintIntoCacheWithTransparentParts() +{ + QWidget *w = new QWidget(); + w->setPalette(Qt::blue); + w->setGeometry(0, 0, 50, 50); + + QGraphicsScene *scene = new QGraphicsScene(); + QGraphicsView *view = new QGraphicsView(scene); + + QGraphicsProxyWidget *proxy = scene->addWidget(w); + proxy->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + proxy->rotate(15); + + view->show(); + QTest::qWaitForWindowShown(view); + w->update(10,10,10,10); + QTest::qWait(50); + + QPixmap pix; + QGraphicsItemPrivate* itemp = QGraphicsItemPrivate::get(proxy); + QPixmapCache::Key key = itemp->extraItemCache()->deviceData.value(view->viewport()).key; + QVERIFY(QPixmapCache::find(key, &pix)); + + QTransform t = proxy->sceneTransform(); + // Map from scene coordinates to pixmap coordinates. + // X origin in the pixmap is the most-left point + // of the item's boundingRect in the scene. + qreal adjust = t.mapRect(proxy->boundingRect().toRect()).left(); + QRect rect = t.mapRect(QRect(10, 10, 10, 10)).adjusted(-adjust, 0, -adjust + 1, 1); + QPixmap subpix = pix.copy(rect); + + QImage im = subpix.toImage(); + for(int i = 0; i < im.width(); i++) { + for(int j = 0; j < im.height(); j++) + QCOMPARE(qAlpha(im.pixel(i, j)), 255); + } +} + QTEST_MAIN(tst_QGraphicsScene) #include "tst_qgraphicsscene.moc" diff --git a/tests/auto/qmenu/tst_qmenu.cpp b/tests/auto/qmenu/tst_qmenu.cpp index 7cdfe46..6079189 100644 --- a/tests/auto/qmenu/tst_qmenu.cpp +++ b/tests/auto/qmenu/tst_qmenu.cpp @@ -82,7 +82,7 @@ private slots: void keyboardNavigation_data(); void keyboardNavigation(); void focus(); - void overrideMenuAction(); + void overrideMenuAction(); void statusTip(); void widgetActionFocus(); void mouseActivation(); @@ -103,12 +103,14 @@ private slots: void task258920_mouseBorder(); void setFixedWidth(); void deleteActionInTriggered(); + void pushButtonPopulateOnAboutToShow(); protected slots: void onActivated(QAction*); void onHighlighted(QAction*); void onStatusMessageChanged(const QString &); void onStatusTipTimer(); void deleteAction(QAction *a) { delete a; } + void populateMenu(); private: void createActions(); QMenu *menus[2], *lastMenu; @@ -258,6 +260,15 @@ void tst_QMenu::onStatusMessageChanged(const QString &s) statustip=s; } +void tst_QMenu::populateMenu(){ + //just adds 3 dummy actions and a separator. + lastMenu->addAction("Foo"); + lastMenu->addAction("Bar"); + lastMenu->addAction("FooBar"); + lastMenu->addSeparator(); + +} + //actual tests void @@ -896,7 +907,30 @@ void tst_QMenu::deleteActionInTriggered() QVERIFY(!a); } +void tst_QMenu::pushButtonPopulateOnAboutToShow() +{ + QPushButton b("Test PushButton"); + b.setWindowFlags(Qt::FramelessWindowHint); + b.setWindowFlags(Qt::X11BypassWindowManagerHint); + lastMenu = new QMenu; + b.setMenu(lastMenu); + const int scrNumber = QApplication::desktop()->screenNumber(&b); + connect(lastMenu, SIGNAL(aboutToShow()), this, SLOT(populateMenu())); + b.show(); + const QRect screen = QApplication::desktop()->screenGeometry(scrNumber); + b.move(10, screen.bottom()-b.height()-5); + QTest::qWaitForWindowShown(&b); + QTimer::singleShot(300,lastMenu, SLOT(hide())); + QTest::mouseClick(&b, Qt::LeftButton, Qt::NoModifier, b.rect().center()); + QVERIFY(!lastMenu->geometry().intersects(b.geometry())); + + b.move(10, screen.bottom()-lastMenu->height()-5); + QTimer::singleShot(300,lastMenu, SLOT(hide())); + QTest::mouseClick(&b, Qt::LeftButton, Qt::NoModifier, b.rect().center()); + QVERIFY(!lastMenu->geometry().intersects(b.geometry())); + +} QTEST_MAIN(tst_QMenu) diff --git a/tools/linguist/lrelease/lrelease.pro b/tools/linguist/lrelease/lrelease.pro index b13c03e..6beafa3 100644 --- a/tools/linguist/lrelease/lrelease.pro +++ b/tools/linguist/lrelease/lrelease.pro @@ -16,7 +16,7 @@ include(../../../src/tools/bootstrap/bootstrap.pri) include(../shared/formats.pri) include(../shared/proparser.pri) -win32-msvc*:LIBS += advapi32.lib # for qsettings_win.cpp +win32:LIBS += -ladvapi32 # for qsettings_win.cpp target.path=$$[QT_INSTALL_BINS] INSTALLS += target |